root/java/sql-vyuka/src/java/cz/frantovo/sql/vyuka/dao/PiskovisteDAO.java @ 78:3b4abb1ec5a3

Revision 78:3b4abb1ec5a3, 4.9 KB (checked in by František Kučera <franta-hg@…>, 12 years ago)

Limit počtu řádků (10000) a doby provádění SQL dotazu (3 vteřiny) v pískovišti,
nedokonalá ochrana proti DoS útoku (kartézský součin, náročný dotaz).

Line 
1package cz.frantovo.sql.vyuka.dao;
2
3import cz.frantovo.sql.vyuka.dao.VyukaSuperDAO.DATABAZE;
4import cz.frantovo.sql.vyuka.dto.Hlaska;
5import cz.frantovo.sql.vyuka.dto.Hlaska.Typ;
6import cz.frantovo.sql.vyuka.dto.Tabulka;
7import cz.frantovo.sql.vyuka.dto.Uzivatel;
8import cz.frantovo.sql.vyuka.dto.VysledekSQL;
9import java.sql.Connection;
10import java.sql.PreparedStatement;
11import java.sql.ResultSet;
12import java.sql.SQLException;
13import java.util.logging.Level;
14
15/**
16 * Pro spouštění uživatelových příkazů.
17 *
18 * @author fiki
19 */
20public class PiskovisteDAO extends VyukaSuperDAO {
21
22        /** maximální doba trvání SQL dotazu – vteřiny */
23        private static final int LIMIT_ČASU = 3;
24        /** maximální počet řádků */
25        private static final int LIMIT_POČTU = 10000;
26
27        private enum VLASTNOSTI {
28
29                VYCHOZI_CESTA,
30                LIMIT_ČASU
31        }
32        TipyDAO tipy = new TipyDAO();
33        HistorieDAO historie = new HistorieDAO();
34
35        public VysledekSQL vykonejSQL(String sql, Uzivatel uzivatel) {
36                VysledekSQL v = new VysledekSQL();
37                if (historie.ulozPrikaz(sql, uzivatel)) {
38
39                        Connection db = getSpojeni(DATABAZE.PISKOVISTE);
40                        if (db == null) {
41                                v.getHlasky().add(new Hlaska("Došlo k chybě spojení.", Typ.Chyba));
42                        } else {
43                                PreparedStatement ps = null;
44                                ResultSet rs = null;
45                                try {
46                                        /**
47                                         * Uživatelskému SQL příkazu předřadíme výchozí cestu (search_path).
48                                         * Protože uživatelé si ji mohou měnit a kvůli recyklaci databázových zdrojů
49                                         * by jeden uživatel mohl ovlivnit jiného.
50                                         */
51                                        if (getVlastnost(VLASTNOSTI.VYCHOZI_CESTA) != null) {
52                                                sql = orizni(getVlastnost(VLASTNOSTI.VYCHOZI_CESTA)) + sql;
53                                        }
54                                       
55                                        /**
56                                         * TODO:
57                                         * použít ps.setQueryTimeout(LIMIT_ČASU);
58                                         * až ho bude podporovat JDBC ovladač,
59                                         * viz níže.
60                                         * Uživatel ale stejně může zadat:
61                                         * SET statement_timeout 0;
62                                         * do svého SQL dotazu.
63                                         */
64                                        if (getVlastnost(VLASTNOSTI.LIMIT_ČASU) != null) {
65                                                sql = orizni(getVlastnost(VLASTNOSTI.LIMIT_ČASU)) + sql;
66                                        }
67
68                                        long casPred = System.currentTimeMillis();
69                                        ps = db.prepareStatement(sql);
70                                        /**
71                                         * Limit času bohužel není podporován JDBC ovladačem.
72                                         * Alespoň ne v postgresql-9.1-901.jdbc4.jar
73                                         * http://jdbc.postgresql.org/todo.html
74                                         *
75                                         * TODO:
76                                         * ps.setQueryTimeout(LIMIT_ČASU);
77                                         */
78                                        ps.setMaxRows(LIMIT_POČTU);
79                                        boolean isRS = ps.execute();
80
81                                        if (isRS) {
82                                                rs = ps.getResultSet();
83                                                v.getTabulky().add(zpracujVysledek(rs));
84                                        }
85
86                                        /**
87                                         * Ošetříme případ, kdy uživatel zadá SQL příkaz, který nevrací výsledkovou
88                                         * sadu.
89                                         * Typicky nastavení výchozího schématu: SET search_path = '…';
90                                         * Poznámka: jeden „SET search_path TO "…"“ se obvykle předřazuje uživatelskému
91                                         * SQL (viz PiskovisteDAO.xml).
92                                         */
93                                        while (ps.getMoreResults() || ps.getUpdateCount() > -1) {
94                                                rs = ps.getResultSet();
95                                                if (rs == null) {
96                                                        /** Jedná se o „update count“. */
97                                                } else {
98                                                        v.getTabulky().add(zpracujVysledek(rs));
99                                                }
100                                        }
101                                        long dobaProvadeni = System.currentTimeMillis() - casPred;
102
103                                        /** Varování */
104                                        if (v.getHlasky().size() < 1 && v.getTabulky().size() < 1) {
105                                                v.getHlasky().add(new Hlaska("SQL příkaz proběhl, ale nevrátil žádná data.", Typ.Varovani));
106                                        }
107
108                                        /** Varování */
109                                        int pocitadloTabulek = 1;
110                                        for (Tabulka t : v.getTabulky()) {
111                                                if (t.getHodnoty().size() < 1) {
112                                                        v.getHlasky().add(new Hlaska("Tabulka " + pocitadloTabulek + "  je prázdná.", Typ.Varovani));
113                                                }
114                                                pocitadloTabulek++;
115                                        }
116
117                                        v.getHlasky().add(new Hlaska("SQL příkaz byl proveden úspěšně, během " + dobaProvadeni + " ms.", Typ.OK));
118
119                                } catch (SQLException e) {
120                                        log.log(Level.SEVERE, "SQL chyba při vykonávání uživatelského dotazu.", e);
121                                        v.getHlasky().add(new Hlaska("Chybné SQL: " + e.getMessage(), Typ.Chyba));
122                                } catch (Exception e) {
123                                        log.log(Level.SEVERE, "Chyba při vykonávání uživatelského dotazu.", e);
124                                        v.getHlasky().add(new Hlaska("Došlo k chybě dotazu.", Typ.Chyba));
125                                } finally {
126                                        zavri(db, ps, rs);
127                                }
128                        }
129
130                        /** Tip pro uživatele */
131                        String tip = tipy.getTip();
132                        if (tip != null) {
133                                v.getHlasky().add(new Hlaska(tip, Typ.Tip, false));
134                        }
135
136                } else {
137                        v.getHlasky().add(new Hlaska("Došlo k chybě historie.", Typ.Chyba));
138                }
139                return v;
140        }
141
142        private Tabulka zpracujVysledek(ResultSet rs) throws SQLException {
143                Tabulka t = new Tabulka();
144
145                int pocetSloupecku = rs.getMetaData().getColumnCount();
146                String[] zahlavi = new String[pocetSloupecku];
147                t.setZahlavi(zahlavi);
148                for (int i = 0; i < pocetSloupecku; i++) {
149                        zahlavi[i] = rs.getMetaData().getColumnName(i + 1);
150                }
151
152                while (rs.next()) {
153                        Object[] hodnoty = new Object[pocetSloupecku];
154                        for (int i = 0; i < pocetSloupecku; i++) {
155                                hodnoty[i] = rs.getObject(i + 1);
156                        }
157                        t.getHodnoty().add(hodnoty);
158                }
159
160                return t;
161        }
162}
Note: See TracBrowser for help on using the browser.