File D:\maven-qcr-plugin-testsunitaires\qcr4java\h2-1.3.170-sources\target\site\qc\lecode\server\web\WebApp.java.html

1 /*
2 * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
3 * Version 1.0, and under the Eclipse Public License, Version 1.0
4 * (http://h2database.com/html/license.html).
5 * Initial Developer: H2 Group
6 */
7 package org.h2.server.web;
8
9 import java.io.ByteArrayOutputStream;
10 import java.io.PrintStream;
11 import java.io.PrintWriter;
12 import java.io.StringReader;
13 import java.io.StringWriter;
14 import java.lang.reflect.InvocationTargetException;
15 import java.lang.reflect.Method;
16 import java.math.BigDecimal;
17 import java.sql.Connection;
18 import java.sql.DatabaseMetaData;
19 import java.sql.ParameterMetaData;
20 import java.sql.PreparedStatement;
21 import java.sql.ResultSet;
22 import java.sql.ResultSetMetaData;
23 import java.sql.SQLException;
24 import java.sql.Statement;
25 import java.sql.Types;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.Collections;
29 import java.util.Comparator;
30 import java.util.HashMap;
31 import java.util.Iterator;
32 import java.util.Locale;
33 import java.util.Map;
34 import java.util.Properties;
35 import java.util.Random;
36 import org.h2.bnf.Bnf;
37 import org.h2.constant.ErrorCode;
38 import org.h2.constant.SysProperties;
39 import org.h2.engine.Constants;
40 import org.h2.jdbc.JdbcSQLException;
41 import org.h2.message.DbException;
42 import org.h2.security.SHA256;
43 import org.h2.tools.Backup;
44 import org.h2.tools.ChangeFileEncryption;
45 import org.h2.tools.ConvertTraceFile;
46 import org.h2.tools.CreateCluster;
47 import org.h2.tools.DeleteDbFiles;
48 import org.h2.tools.Recover;
49 import org.h2.tools.Restore;
50 import org.h2.tools.RunScript;
51 import org.h2.tools.Script;
52 import org.h2.tools.SimpleResultSet;
53 import org.h2.util.JdbcUtils;
54 import org.h2.util.New;
55 import org.h2.util.Profiler;
56 import org.h2.util.ScriptReader;
57 import org.h2.util.SortedProperties;
58 import org.h2.util.StatementBuilder;
59 import org.h2.util.StringUtils;
60 import org.h2.util.Tool;
61 import org.h2.util.Utils;
62
63 /**
64 * For each connection to a session, an object of this class is created.
65 * This class is used by the H2 Console.
66 */
67 public class WebApp {
68
69 /**
70 * The web server.
71 */
72 protected final WebServer server;
73
74 /**
75 * The session.
76 */
77 protected WebSession session;
78
79 /**
80 * The session attributes
81 */
82 protected Properties attributes;
83
84 /**
85 * The mime type of the current response.
86 */
87 protected String mimeType;
88
89 /**
90 * Whether the response can be cached.
91 */
92 protected boolean cache;
93
94 /**
95 * Whether to close the connection.
96 */
97 protected boolean stop;
98
99 /**
100 * The language in the HTTP header.
101 */
102 protected String headerLanguage;
103
104 private Profiler profiler;
105
106 WebApp(WebServer server) {
107 this.server = server;
108 }
109
110 /**
111 * Set the web session and attributes.
112 *
113 * @param session the session
114 * @param attributes the attributes
115 */
116 void setSession(WebSession session, Properties attributes) {
117 this.session = session;
118 this.attributes = attributes;
119 }
120
121 /**
122 * Process an HTTP request.
123 *
124 * @param file the file that was requested
125 * @param hostAddr the host address
126 * @return the name of the file to return to the client
127 */
128 String processRequest(String file, String hostAddr) {
129 int index = file.lastIndexOf('.');
130 String suffix;
131 if (index >= 0) {
132 suffix = file.substring(index + 1);
133 } else {
134 suffix = "";
135 }
136 if ("ico".equals(suffix)) {
137 mimeType = "image/x-icon";
138 cache = true;
139 } else if ("gif".equals(suffix)) {
140 mimeType = "image/gif";
141 cache = true;
142 } else if ("css".equals(suffix)) {
143 cache = true;
144 mimeType = "text/css";
145 } else if ("html".equals(suffix) || "do".equals(suffix) || "jsp".equals(suffix)) {
146 cache = false;
147 mimeType = "text/html";
148 if (session == null && !file.startsWith(WebServer.TRANSFER)) {
149 session = server.createNewSession(hostAddr);
150 if (!"notAllowed.jsp".equals(file)) {
151 file = "index.do";
152 }
153 }
154 } else if ("js".equals(suffix)) {
155 cache = true;
156 mimeType = "text/javascript";
157 } else {
158 cache = true;
159 mimeType = "application/octet-stream";
160 }
161 trace("mimeType=" + mimeType);
162 trace(file);
163 if (file.endsWith(".do")) {
164 file = process(file);
165 }
166 return file;
167 }
168
169 private static String getComboBox(String[] elements, String selected) {
170 StringBuilder buff = new StringBuilder();
171 for (String value : elements) {
172 buff.append("<option value=\"").
173 append(PageParser.escapeHtmlData(value)).
174 append('\"');
175 if (value.equals(selected)) {
176 buff.append(" selected");
177 }
178 buff.append('>').
179 append(PageParser.escapeHtml(value)).
180 append("</option>");
181 }
182 return buff.toString();
183 }
184
185 private static String getComboBox(String[][] elements, String selected) {
186 StringBuilder buff = new StringBuilder();
187 for (String[] n : elements) {
188 buff.append("<option value=\"").
189 append(PageParser.escapeHtmlData(n[0])).
190 append('\"');
191 if (n[0].equals(selected)) {
192 buff.append(" selected");
193 }
194 buff.append('>').
195 append(PageParser.escapeHtml(n[1])).
196 append("</option>");
197 }
198 return buff.toString();
199 }
200
201 private String process(String file) {
202 trace("process " + file);
203 while (file.endsWith(".do")) {
204 if ("login.do".equals(file)) {
205 file = login();
206 } else if ("index.do".equals(file)) {
207 file = index();
208 } else if ("logout.do".equals(file)) {
209 file = logout();
210 } else if ("settingRemove.do".equals(file)) {
211 file = settingRemove();
212 } else if ("settingSave.do".equals(file)) {
213 file = settingSave();
214 } else if ("test.do".equals(file)) {
215 file = test();
216 } else if ("query.do".equals(file)) {
217 file = query();
218 } else if ("tables.do".equals(file)) {
219 file = tables();
220 } else if ("editResult.do".equals(file)) {
221 file = editResult();
222 } else if ("getHistory.do".equals(file)) {
223 file = getHistory();
224 } else if ("admin.do".equals(file)) {
225 file = admin();
226 } else if ("adminSave.do".equals(file)) {
227 file = adminSave();
228 } else if ("adminStartTranslate.do".equals(file)) {
229 file = adminStartTranslate();
230 } else if ("adminShutdown.do".equals(file)) {
231 file = adminShutdown();
232 } else if ("autoCompleteList.do".equals(file)) {
233 file = autoCompleteList();
234 } else if ("tools.do".equals(file)) {
235 file = tools();
236 } else if ("transfer.do".equals(file)) {
237 file = "transfer.jsp";
238 } else {
239 file = "error.jsp";
240 }
241 }
242 trace("return " + file);
243 return file;
244 }
245
246 private String autoCompleteList() {
247 String query = (String) attributes.get("query");
248 boolean lowercase = false;
249 if (query.trim().length() > 0 && Character.isLowerCase(query.trim().charAt(0))) {
250 lowercase = true;
251 }
252 try {
253 String sql = query;
254 if (sql.endsWith(";")) {
255 sql += " ";
256 }
257 ScriptReader reader = new ScriptReader(new StringReader(sql));
258 reader.setSkipRemarks(true);
259 String lastSql = "";
260 while (true) {
261 String n = reader.readStatement();
262 if (n == null) {
263 break;
264 }
265 lastSql = n;
266 }
267 String result = "";
268 if (reader.isInsideRemark()) {
269 if (reader.isBlockRemark()) {
270 result = "1#(End Remark)# */\n" + result;
271 } else {
272 result = "1#(Newline)#\n" + result;
273 }
274 } else {
275 sql = lastSql;
276 while (sql.length() > 0 && sql.charAt(0) <= ' ') {
277 sql = sql.substring(1);
278 }
279 if (sql.trim().length() > 0 && Character.isLowerCase(sql.trim().charAt(0))) {
280 lowercase = true;
281 }
282 Bnf bnf = session.getBnf();
283 if (bnf == null) {
284 return "autoCompleteList.jsp";
285 }
286 HashMap<String, String> map = bnf.getNextTokenList(sql);
287 String space = "";
288 if (sql.length() > 0) {
289 char last = sql.charAt(sql.length() - 1);
290 if (!Character.isSpaceChar(last) && (last != '.' && last >= ' ' && last != '\'' && last != '"')) {
291 space = " ";
292 }
293 }
294 ArrayList<String> list = New.arrayList(map.size());
295 for (Map.Entry<String, String> entry : map.entrySet()) {
296 String key = entry.getKey();
297 String value = entry.getValue();
298 String type = "" + key.charAt(0);
299 key = key.substring(2);
300 if (Character.isLetter(key.charAt(0)) && lowercase) {
301 key = StringUtils.toLowerEnglish(key);
302 value = StringUtils.toLowerEnglish(value);
303 }
304 if (key.equals(value) && !".".equals(value)) {
305 value = space + value;
306 }
307 key = StringUtils.urlEncode(key);
308 key = StringUtils.replaceAll(key, "+", " ");
309 value = StringUtils.urlEncode(value);
310 value = StringUtils.replaceAll(value, "+", " ");
311 list.add(type + "#" + key + "#" + value);
312 }
313 Collections.sort(list);
314 if (query.endsWith("\n") || query.trim().endsWith(";")) {
315 list.add(0, "1#(Newline)#\n");
316 }
317 StatementBuilder buff = new StatementBuilder();
318 for (String s : list) {
319 buff.appendExceptFirst("|");
320 buff.append(s);
321 }
322 result = buff.toString();
323 }
324 session.put("autoCompleteList", result);
325 } catch (Throwable e) {
326 server.traceError(e);
327 }
328 return "autoCompleteList.jsp";
329 }
330
331 private String admin() {
332 session.put("port", "" + server.getPort());
333 session.put("allowOthers", "" + server.getAllowOthers());
334 session.put("ssl", String.valueOf(server.getSSL()));
335 session.put("sessions", server.getSessions());
336 return "admin.jsp";
337 }
338
339 private String adminSave() {
340 try {
341 Properties prop = new SortedProperties();
342 int port = Integer.decode((String) attributes.get("port"));
343 prop.setProperty("webPort", String.valueOf(port));
344 server.setPort(port);
345 boolean allowOthers = Boolean.parseBoolean((String) attributes.get("allowOthers"));
346 prop.setProperty("webAllowOthers", String.valueOf(allowOthers));
347 server.setAllowOthers(allowOthers);
348 boolean ssl = Boolean.parseBoolean((String) attributes.get("ssl"));
349 prop.setProperty("webSSL", String.valueOf(ssl));
350 server.setSSL(ssl);
351 server.saveProperties(prop);
352 } catch (Exception e) {
353 trace(e.toString());
354 }
355 return admin();
356 }
357
358 private String tools() {
359 try {
360 String toolName = (String) attributes.get("tool");
361 session.put("tool", toolName);
362 String args = (String) attributes.get("args");
363 String[] argList = StringUtils.arraySplit(args, ',', false);
364 Tool tool = null;
365 if ("Backup".equals(toolName)) {
366 tool = new Backup();
367 } else if ("Restore".equals(toolName)) {
368 tool = new Restore();
369 } else if ("Recover".equals(toolName)) {
370 tool = new Recover();
371 } else if ("DeleteDbFiles".equals(toolName)) {
372 tool = new DeleteDbFiles();
373 } else if ("ChangeFileEncryption".equals(toolName)) {
374 tool = new ChangeFileEncryption();
375 } else if ("Script".equals(toolName)) {
376 tool = new Script();
377 } else if ("RunScript".equals(toolName)) {
378 tool = new RunScript();
379 } else if ("ConvertTraceFile".equals(toolName)) {
380 tool = new ConvertTraceFile();
381 } else if ("CreateCluster".equals(toolName)) {
382 tool = new CreateCluster();
383 } else {
384 throw DbException.throwInternalError(toolName);
385 }
386 ByteArrayOutputStream outBuff = new ByteArrayOutputStream();
387 PrintStream out = new PrintStream(outBuff, false, "UTF-8");
388 tool.setOut(out);
389 try {
390 tool.runTool(argList);
391 out.flush();
392 String o = new String(outBuff.toByteArray(), "UTF-8");
393 String result = PageParser.escapeHtml(o);
394 session.put("toolResult", result);
395 } catch (Exception e) {
396 session.put("toolResult", getStackTrace(0, e, true));
397 }
398 } catch (Exception e) {
399 server.traceError(e);
400 }
401 return "tools.jsp";
402 }
403
404 @SuppressWarnings("unchecked")
405 private String adminStartTranslate() {
406 Map<Object, Object> p = (Map<Object, Object>) session.map.get("text");
407 String file = server.startTranslate(p);
408 session.put("translationFile", file);
409 return "helpTranslate.jsp";
410 }
411
412 /**
413 * Stop the application and the server.
414 *
415 * @return the page to display
416 */
417 protected String adminShutdown() {
418 server.shutdown();
419 return "admin.jsp";
420 }
421
422 private String index() {
423 String[][] languageArray = WebServer.LANGUAGES;
424 String language = (String) attributes.get("language");
425 Locale locale = session.locale;
426 if (language != null) {
427 if (locale == null || !StringUtils.toLowerEnglish(locale.getLanguage()).equals(language)) {
428 locale = new Locale(language, "");
429 server.readTranslations(session, locale.getLanguage());
430 session.put("language", language);
431 session.locale = locale;
432 }
433 } else {
434 language = (String) session.get("language");
435 }
436 if (language == null) {
437 // if the language is not yet known
438 // use the last header
439 language = headerLanguage;
440 }
441 session.put("languageCombo", getComboBox(languageArray, language));
442 String[] settingNames = server.getSettingNames();
443 String setting = attributes.getProperty("setting");
444 if (setting == null && settingNames.length > 0) {
445 setting = settingNames[0];
446 }
447 String combobox = getComboBox(settingNames, setting);
448 session.put("settingsList", combobox);
449 ConnectionInfo info = server.getSetting(setting);
450 if (info == null) {
451 info = new ConnectionInfo();
452 }
453 session.put("setting", PageParser.escapeHtmlData(setting));
454 session.put("name", PageParser.escapeHtmlData(setting));
455 session.put("driver", PageParser.escapeHtmlData(info.driver));
456 session.put("url", PageParser.escapeHtmlData(info.url));
457 session.put("user", PageParser.escapeHtmlData(info.user));
458 return "index.jsp";
459 }
460
461 private String getHistory() {
462 int id = Integer.parseInt(attributes.getProperty("id"));
463 String sql = session.getCommand(id);
464 session.put("query", PageParser.escapeHtmlData(sql));
465 return "query.jsp";
466 }
467
468 private static int addColumns(boolean mainSchema, DbTableOrView table,
469 StringBuilder buff, int treeIndex, boolean showColumnTypes,
470 StringBuilder columnsBuffer) {
471 DbColumn[] columns = table.columns;
472 for (int i = 0; columns != null && i < columns.length; i++) {
473 DbColumn column = columns[i];
474 if (columnsBuffer.length() > 0) {
475 columnsBuffer.append(' ');
476 }
477 columnsBuffer.append(column.name);
478 String col = escapeIdentifier(column.name);
479 String level = mainSchema ? ", 1, 1" : ", 2, 2";
480 buff.append("setNode(" + treeIndex + level + ", 'column', '" + PageParser.escapeJavaScript(column.name)
481 + "', 'javascript:ins(\\'" + col + "\\')');\n");
482 treeIndex++;
483 if (mainSchema && showColumnTypes) {
484 buff.append("setNode(" + treeIndex + ", 2, 2, 'type', '" + PageParser.escapeJavaScript(column.dataType)
485 + "', null);\n");
486 treeIndex++;
487 }
488 }
489 return treeIndex;
490 }
491
492 private static String escapeIdentifier(String name) {
493 return StringUtils.urlEncode(PageParser.escapeJavaScript(name)).replace('+', ' ');
494 }
495
496 /**
497 * This class represents index information for the GUI.
498 */
499 static class IndexInfo {
500
501 /**
502 * The index name.
503 */
504 String name;
505
506 /**
507 * The index type name.
508 */
509 String type;
510
511 /**
512 * The indexed columns.
513 */
514 String columns;
515 }
516
517 private static int addIndexes(boolean mainSchema, DatabaseMetaData meta,
518 String table, String schema, StringBuilder buff, int treeIndex)
519 throws SQLException {
520 ResultSet rs;
521 try {
522 rs = meta.getIndexInfo(null, schema, table, false, true);
523 } catch (SQLException e) {
524 // SQLite
525 return treeIndex;
526 }
527 HashMap<String, IndexInfo> indexMap = New.hashMap();
528 while (rs.next()) {
529 String name = rs.getString("INDEX_NAME");
530 IndexInfo info = indexMap.get(name);
531 if (info == null) {
532 int t = rs.getInt("TYPE");
533 String type;
534 if (t == DatabaseMetaData.tableIndexClustered) {
535 type = "";
536 } else if (t == DatabaseMetaData.tableIndexHashed) {
537 type = " (${text.tree.hashed})";
538 } else if (t == DatabaseMetaData.tableIndexOther) {
539 type = "";
540 } else {
541 type = null;
542 }
543 if (name != null && type != null) {
544 info = new IndexInfo();
545 info.name = name;
546 type = (rs.getBoolean("NON_UNIQUE") ? "${text.tree.nonUnique}" : "${text.tree.unique}") + type;
547 info.type = type;
548 info.columns = rs.getString("COLUMN_NAME");
549 indexMap.put(name, info);
550 }
551 } else {
552 info.columns += ", " + rs.getString("COLUMN_NAME");
553 }
554 }
555 rs.close();
556 if (indexMap.size() > 0) {
557 String level = mainSchema ? ", 1, 1" : ", 2, 1";
558 String levelIndex = mainSchema ? ", 2, 1" : ", 3, 1";
559 String levelColumnType = mainSchema ? ", 3, 2" : ", 4, 2";
560 buff.append("setNode(" + treeIndex + level + ", 'index_az', '${text.tree.indexes}', null);\n");
561 treeIndex++;
562 for (IndexInfo info : indexMap.values()) {
563 buff.append("setNode(" + treeIndex + levelIndex + ", 'index', '" + PageParser.escapeJavaScript(info.name)
564 + "', null);\n");
565 treeIndex++;
566 buff.append("setNode(" + treeIndex + levelColumnType + ", 'type', '" + info.type + "', null);\n");
567 treeIndex++;
568 buff.append("setNode(" + treeIndex + levelColumnType + ", 'type', '" + PageParser.escapeJavaScript(info.columns)
569 + "', null);\n");
570 treeIndex++;
571 }
572 }
573 return treeIndex;
574 }
575
576 private int addTablesAndViews(DbSchema schema, boolean mainSchema, StringBuilder buff, int treeIndex)
577 throws SQLException {
578 if (schema == null) {
579 return treeIndex;
580 }
581 Connection conn = session.getConnection();
582 DatabaseMetaData meta = session.getMetaData();
583 int level = mainSchema ? 0 : 1;
584 boolean showColumns = mainSchema || !schema.isSystem;
585 String indentation = ", " + level + ", " + (showColumns ? "1" : "2") + ", ";
586 String indentNode = ", " + (level + 1) + ", 2, ";
587 DbTableOrView[] tables = schema.tables;
588 if (tables == null) {
589 return treeIndex;
590 }
591 boolean isOracle = schema.contents.isOracle;
592 boolean notManyTables = tables.length < DbSchema.MAX_TABLES_LIST_INDEXES;
593 for (DbTableOrView table : tables) {
594 if (table.isView) {
595 continue;
596 }
597 int tableId = treeIndex;
598 String tab = table.quotedName;
599 if (!mainSchema) {
600 tab = schema.quotedName + "." + tab;
601 }
602 tab = escapeIdentifier(tab);
603 buff.append("setNode(" + treeIndex + indentation + " 'table', '" + PageParser.escapeJavaScript(table.name)
604 + "', 'javascript:ins(\\'" + tab + "\\',true)');\n");
605 treeIndex++;
606 if (mainSchema || showColumns) {
607 StringBuilder columnsBuffer = new StringBuilder();
608 treeIndex = addColumns(mainSchema, table, buff, treeIndex, notManyTables, columnsBuffer);
609 if (!isOracle && notManyTables) {
610 treeIndex = addIndexes(mainSchema, meta, table.name, schema.name, buff, treeIndex);
611 }
612 buff.append("addTable('" + PageParser.escapeJavaScript(table.name) + "', '"
613 + PageParser.escapeJavaScript(columnsBuffer.toString()) + "', " + tableId + ");\n");
614 }
615 }
616 tables = schema.tables;
617 for (DbTableOrView view : tables) {
618 if (!view.isView) {
619 continue;
620 }
621 int tableId = treeIndex;
622 String tab = view.quotedName;
623 if (!mainSchema) {
624 tab = view.schema.quotedName + "." + tab;
625 }
626 tab = escapeIdentifier(tab);
627 buff.append("setNode(" + treeIndex + indentation + " 'view', '" + PageParser.escapeJavaScript(view.name)
628 + "', 'javascript:ins(\\'" + tab + "\\',true)');\n");
629 treeIndex++;
630 if (mainSchema) {
631 StringBuilder columnsBuffer = new StringBuilder();
632 treeIndex = addColumns(mainSchema, view, buff, treeIndex, notManyTables, columnsBuffer);
633 if (schema.contents.isH2) {
634 PreparedStatement prep = null;
635 try {
636 prep = conn.prepareStatement("SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=?");
637 prep.setString(1, view.name);
638 ResultSet rs = prep.executeQuery();
639 if (rs.next()) {
640 String sql = rs.getString("SQL");
641 buff.append("setNode(" + treeIndex + indentNode + " 'type', '"
642 + PageParser.escapeJavaScript(sql) + "', null);\n");
643 treeIndex++;
644 }
645 rs.close();
646 } finally {
647 JdbcUtils.closeSilently(prep);
648 }
649 }
650 buff.append("addTable('" + PageParser.escapeJavaScript(view.name) + "', '"
651 + PageParser.escapeJavaScript(columnsBuffer.toString()) + "', " + tableId + ");\n");
652 }
653 }
654 return treeIndex;
655 }
656
657 private String tables() {
658 DbContents contents = session.getContents();
659 boolean isH2 = false;
660 try {
661 contents.readContents(session.getMetaData());
662 session.loadBnf();
663 Connection conn = session.getConnection();
664 DatabaseMetaData meta = session.getMetaData();
665 isH2 = contents.isH2;
666
667 StringBuilder buff = new StringBuilder();
668 buff.append("setNode(0, 0, 0, 'database', '" + PageParser.escapeJavaScript((String) session.get("url"))
669 + "', null);\n");
670 int treeIndex = 1;
671
672 DbSchema defaultSchema = contents.defaultSchema;
673 treeIndex = addTablesAndViews(defaultSchema, true, buff, treeIndex);
674 DbSchema[] schemas = contents.schemas;
675 for (DbSchema schema : schemas) {
676 if (schema == defaultSchema || schema == null) {
677 continue;
678 }
679 buff.append("setNode(" + treeIndex + ", 0, 1, 'folder', '" + PageParser.escapeJavaScript(schema.name)
680 + "', null);\n");
681 treeIndex++;
682 treeIndex = addTablesAndViews(schema, false, buff, treeIndex);
683 }
684 if (isH2) {
685 Statement stat = null;
686 try {
687 stat = conn.createStatement();
688 ResultSet rs = stat
689 .executeQuery("SELECT * FROM INFORMATION_SCHEMA.SEQUENCES ORDER BY SEQUENCE_NAME");
690 for (int i = 0; rs.next(); i++) {
691 if (i == 0) {
692 buff.append("setNode(" + treeIndex
693 + ", 0, 1, 'sequences', '${text.tree.sequences}', null);\n");
694 treeIndex++;
695 }
696 String name = rs.getString("SEQUENCE_NAME");
697 String current = rs.getString("CURRENT_VALUE");
698 String increment = rs.getString("INCREMENT");
699 buff.append("setNode(" + treeIndex + ", 1, 1, 'sequence', '"
700 + PageParser.escapeJavaScript(name) + "', null);\n");
701 treeIndex++;
702 buff.append("setNode(" + treeIndex + ", 2, 2, 'type', '${text.tree.current}: "
703 + PageParser.escapeJavaScript(current) + "', null);\n");
704 treeIndex++;
705 if (!"1".equals(increment)) {
706 buff.append("setNode(" + treeIndex + ", 2, 2, 'type', '${text.tree.increment}: "
707 + PageParser.escapeJavaScript(increment) + "', null);\n");
708 treeIndex++;
709 }
710 }
711 rs.close();
712 rs = stat.executeQuery("SELECT * FROM INFORMATION_SCHEMA.USERS ORDER BY NAME");
713 for (int i = 0; rs.next(); i++) {
714 if (i == 0) {
715 buff.append("setNode(" + treeIndex + ", 0, 1, 'users', '${text.tree.users}', null);\n");
716 treeIndex++;
717 }
718 String name = rs.getString("NAME");
719 String admin = rs.getString("ADMIN");
720 buff.append("setNode(" + treeIndex + ", 1, 1, 'user', '" + PageParser.escapeJavaScript(name)
721 + "', null);\n");
722 treeIndex++;
723 if (admin.equalsIgnoreCase("TRUE")) {
724 buff.append("setNode(" + treeIndex + ", 2, 2, 'type', '${text.tree.admin}', null);\n");
725 treeIndex++;
726 }
727 }
728 rs.close();
729 } finally {
730 JdbcUtils.closeSilently(stat);
731 }
732 }
733 String version = meta.getDatabaseProductName() + " " + meta.getDatabaseProductVersion();
734 buff.append("setNode(" + treeIndex + ", 0, 0, 'info', '" + PageParser.escapeJavaScript(version)
735 + "', null);\n");
736 buff.append("refreshQueryTables();");
737 session.put("tree", buff.toString());
738 } catch (Exception e) {
739 session.put("tree", "");
740 session.put("error", getStackTrace(0, e, isH2));
741 }
742 return "tables.jsp";
743 }
744
745 private String getStackTrace(int id, Throwable e, boolean isH2) {
746 try {
747 StringWriter writer = new StringWriter();
748 e.printStackTrace(new PrintWriter(writer));
749 String stackTrace = writer.toString();
750 stackTrace = PageParser.escapeHtml(stackTrace);
751 if (isH2) {
752 stackTrace = linkToSource(stackTrace);
753 }
754 stackTrace = StringUtils.replaceAll(stackTrace, "\t", "&nbsp;&nbsp;&nbsp;&nbsp;");
755 String message = PageParser.escapeHtml(e.getMessage());
756 String error = "<a class=\"error\" href=\"#\" onclick=\"var x=document.getElementById('st" + id
757 + "').style;x.display=x.display==''?'none':'';\">" + message + "</a>";
758 if (e instanceof SQLException) {
759 SQLException se = (SQLException) e;
760 error += " " + se.getSQLState() + "/" + se.getErrorCode();
761 if (isH2) {
762 int code = se.getErrorCode();
763 error += " <a href=\"http://h2database.com/javadoc/org/h2/constant/ErrorCode.html#c" + code
764 + "\">(${text.a.help})</a>";
765 }
766 }
767 error += "<span style=\"display: none;\" id=\"st" + id + "\"><br />" + stackTrace + "</span>";
768 error = formatAsError(error);
769 return error;
770 } catch (OutOfMemoryError e2) {
771 server.traceError(e);
772 return e.toString();
773 }
774 }
775
776 private static String linkToSource(String s) {
777 try {
778 StringBuilder result = new StringBuilder(s.length());
779 int idx = s.indexOf("<br />");
780 result.append(s.substring(0, idx));
781 while (true) {
782 int start = s.indexOf("org.h2.", idx);
783 if (start < 0) {
784 result.append(s.substring(idx));
785 break;
786 }
787 result.append(s.substring(idx, start));
788 int end = s.indexOf(')', start);
789 if (end < 0) {
790 result.append(s.substring(idx));
791 break;
792 }
793 String element = s.substring(start, end);
794 int open = element.lastIndexOf('(');
795 int dotMethod = element.lastIndexOf('.', open - 1);
796 int dotClass = element.lastIndexOf('.', dotMethod - 1);
797 String packageName = element.substring(0, dotClass);
798 int colon = element.lastIndexOf(':');
799 String file = element.substring(open + 1, colon);
800 String lineNumber = element.substring(colon + 1, element.length());
801 String fullFileName = packageName.replace('.', '/') + "/" + file;
802 result.append("<a href=\"http://h2database.com/html/source.html?file=");
803 result.append(fullFileName);
804 result.append("&line=");
805 result.append(lineNumber);
806 result.append("&build=");
807 result.append(Constants.BUILD_ID);
808 result.append("\">");
809 result.append(element);
810 result.append("</a>");
811 idx = end;
812 }
813 return result.toString();
814 } catch (Throwable t) {
815 return s;
816 }
817 }
818
819 private static String formatAsError(String s) {
820 return "<div class=\"error\">" + s + "</div>";
821 }
822
823 private String test() {
824 String driver = attributes.getProperty("driver", "");
825 String url = attributes.getProperty("url", "");
826 String user = attributes.getProperty("user", "");
827 String password = attributes.getProperty("password", "");
828 session.put("driver", driver);
829 session.put("url", url);
830 session.put("user", user);
831 boolean isH2 = url.startsWith("jdbc:h2:");
832 try {
833 long start = System.currentTimeMillis();
834 String profOpen = "", profClose = "";
835 Profiler prof = new Profiler();
836 prof.startCollecting();
837 Connection conn;
838 try {
839 conn = server.getConnection(driver, url, user, password);
840 } finally {
841 prof.stopCollecting();
842 profOpen = prof.getTop(3);
843 }
844 prof = new Profiler();
845 prof.startCollecting();
846 try {
847 JdbcUtils.closeSilently(conn);
848 } finally {
849 prof.stopCollecting();
850 profClose = prof.getTop(3);
851 }
852 long time = System.currentTimeMillis() - start;
853 String success;
854 if (time > 1000) {
855 success = "<a class=\"error\" href=\"#\" " +
856 "onclick=\"var x=document.getElementById('prof').style;x.display=x.display==''?'none':'';\">" +
857 "${text.login.testSuccessful}</a>" +
858 "<span style=\"display: none;\" id=\"prof\"><br />" +
859 PageParser.escapeHtml(profOpen) +
860 "<br />" +
861 PageParser.escapeHtml(profClose) +
862 "</span>";
863 } else {
864 success = "${text.login.testSuccessful}";
865 }
866 session.put("error", success);
867 // session.put("error", "${text.login.testSuccessful}");
868 return "login.jsp";
869 } catch (Exception e) {
870 session.put("error", getLoginError(e, isH2));
871 return "login.jsp";
872 }
873 }
874
875 /**
876 * Get the formatted login error message.
877 *
878 * @param e the exception
879 * @param isH2 if the current database is a H2 database
880 * @return the formatted error message
881 */
882 private String getLoginError(Exception e, boolean isH2) {
883 if (e instanceof JdbcSQLException && ((JdbcSQLException) e).getErrorCode() == ErrorCode.CLASS_NOT_FOUND_1) {
884 return "${text.login.driverNotFound}<br />" + getStackTrace(0, e, isH2);
885 }
886 return getStackTrace(0, e, isH2);
887 }
888
889 private String login() {
890 String driver = attributes.getProperty("driver", "");
891 String url = attributes.getProperty("url", "");
892 String user = attributes.getProperty("user", "");
893 String password = attributes.getProperty("password", "");
894 session.put("autoCommit", "checked");
895 session.put("autoComplete", "1");
896 session.put("maxrows", "1000");
897 boolean isH2 = url.startsWith("jdbc:h2:");
898 try {
899 Connection conn = server.getConnection(driver, url, user, password);
900 session.setConnection(conn);
901 session.put("url", url);
902 session.put("user", user);
903 session.remove("error");
904 settingSave();
905 return "frame.jsp";
906 } catch (Exception e) {
907 session.put("error", getLoginError(e, isH2));
908 return "login.jsp";
909 }
910 }
911
912 private String logout() {
913 try {
914 Connection conn = session.getConnection();
915 session.setConnection(null);
916 session.remove("conn");
917 session.remove("result");
918 session.remove("tables");
919 session.remove("user");
920 session.remove("tool");
921 if (conn != null) {
922 if (session.getShutdownServerOnDisconnect()) {
923 server.shutdown();
924 } else {
925 conn.close();
926 }
927 }
928 } catch (Exception e) {
929 trace(e.toString());
930 }
931 return "index.do";
932 }
933
934 private String query() {
935 String sql = attributes.getProperty("sql").trim();
936 try {
937 ScriptReader r = new ScriptReader(new StringReader(sql));
938 final ArrayList<String> list = New.arrayList();
939 while (true) {
940 String s = r.readStatement();
941 if (s == null) {
942 break;
943 }
944 list.add(s);
945 }
946 final Connection conn = session.getConnection();
947 if (SysProperties.CONSOLE_STREAM && server.getAllowChunked()) {
948 String page = StringUtils.utf8Decode(server.getFile("result.jsp"));
949 int idx = page.indexOf("${result}");
950 // the first element of the list is the header, the last the footer
951 list.add(0, page.substring(0, idx));
952 list.add(page.substring(idx + "${result}".length()));
953 session.put("chunks", new Iterator<String>() {
954 private int i;
955 public boolean hasNext() {
956 return i < list.size();
957 }
958 public String next() {
959 String s = list.get(i++);
960 if (i == 1 || i == list.size()) {
961 return s;
962 }
963 StringBuilder b = new StringBuilder();
964 query(conn, s, i - 1, list.size() - 2, b);
965 return b.toString();
966 }
967 public void remove() {
968 throw new UnsupportedOperationException();
969 }
970 });
971 return "result.jsp";
972 }
973 String result;
974 StringBuilder buff = new StringBuilder();
975 for (int i = 0; i < list.size(); i++) {
976 String s = list.get(i);
977 query(conn, s, i, list.size(), buff);
978 }
979 result = buff.toString();
980 session.put("result", result);
981 } catch (Throwable e) {
982 session.put("result", getStackTrace(0, e, session.getContents().isH2));
983 }
984 return "result.jsp";
985 }
986
987 /**
988 * Execute a query and append the result to the buffer.
989 *
990 * @param conn the connection
991 * @param s the statement
992 * @param i the index
993 * @param size the number of statements
994 * @param buff the target buffer
995 */
996 void query(Connection conn, String s, int i, int size, StringBuilder buff) {
997 if (!(s.startsWith("@") && s.endsWith("."))) {
998 buff.append(PageParser.escapeHtml(s + ";")).append("<br />");
999 }
1000 boolean forceEdit = s.startsWith("@edit");
1001 buff.append(getResult(conn, i + 1, s, size == 1, forceEdit)).append("<br />");
1002 }
1003
1004 private String editResult() {
1005 ResultSet rs = session.result;
1006 int row = Integer.parseInt(attributes.getProperty("row"));
1007 int op = Integer.parseInt(attributes.getProperty("op"));
1008 String result = "", error = "";
1009 try {
1010 if (op == 1) {
1011 boolean insert = row < 0;
1012 if (insert) {
1013 rs.moveToInsertRow();
1014 } else {
1015 rs.absolute(row);
1016 }
1017 for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
1018 String x = attributes.getProperty("r" + row + "c" + (i + 1));
1019 unescapeData(x, rs, i + 1);
1020 }
1021 if (insert) {
1022 rs.insertRow();
1023 } else {
1024 rs.updateRow();
1025 }
1026 } else if (op == 2) {
1027 rs.absolute(row);
1028 rs.deleteRow();
1029 } else if (op == 3) {
1030 // cancel
1031 }
1032 } catch (Throwable e) {
1033 result = "<br />" + getStackTrace(0, e, session.getContents().isH2);
1034 error = formatAsError(e.getMessage());
1035 }
1036 String sql = "@edit " + (String) session.get("resultSetSQL");
1037 Connection conn = session.getConnection();
1038 result = error + getResult(conn, -1, sql, true, true) + result;
1039 session.put("result", result);
1040 return "result.jsp";
1041 }
1042
1043 private ResultSet getMetaResultSet(Connection conn, String sql) throws SQLException {
1044 DatabaseMetaData meta = conn.getMetaData();
1045 if (isBuiltIn(sql, "@best_row_identifier")) {
1046 String[] p = split(sql);
1047 int scale = p[4] == null ? 0 : Integer.parseInt(p[4]);
1048 boolean nullable = p[5] == null ? false : Boolean.parseBoolean(p[5]);
1049 return meta.getBestRowIdentifier(p[1], p[2], p[3], scale, nullable);
1050 } else if (isBuiltIn(sql, "@catalogs")) {
1051 return meta.getCatalogs();
1052 } else if (isBuiltIn(sql, "@columns")) {
1053 String[] p = split(sql);
1054 return meta.getColumns(p[1], p[2], p[3], p[4]);
1055 } else if (isBuiltIn(sql, "@column_privileges")) {
1056 String[] p = split(sql);
1057 return meta.getColumnPrivileges(p[1], p[2], p[3], p[4]);
1058 } else if (isBuiltIn(sql, "@cross_references")) {
1059 String[] p = split(sql);
1060 return meta.getCrossReference(p[1], p[2], p[3], p[4], p[5], p[6]);
1061 } else if (isBuiltIn(sql, "@exported_keys")) {
1062 String[] p = split(sql);
1063 return meta.getExportedKeys(p[1], p[2], p[3]);
1064 } else if (isBuiltIn(sql, "@imported_keys")) {
1065 String[] p = split(sql);
1066 return meta.getImportedKeys(p[1], p[2], p[3]);
1067 } else if (isBuiltIn(sql, "@index_info")) {
1068 String[] p = split(sql);
1069 boolean unique = p[4] == null ? false : Boolean.parseBoolean(p[4]);
1070 boolean approx = p[5] == null ? false : Boolean.parseBoolean(p[5]);
1071 return meta.getIndexInfo(p[1], p[2], p[3], unique, approx);
1072 } else if (isBuiltIn(sql, "@primary_keys")) {
1073 String[] p = split(sql);
1074 return meta.getPrimaryKeys(p[1], p[2], p[3]);
1075 } else if (isBuiltIn(sql, "@procedures")) {
1076 String[] p = split(sql);
1077 return meta.getProcedures(p[1], p[2], p[3]);
1078 } else if (isBuiltIn(sql, "@procedure_columns")) {
1079 String[] p = split(sql);
1080 return meta.getProcedureColumns(p[1], p[2], p[3], p[4]);
1081 } else if (isBuiltIn(sql, "@schemas")) {
1082 return meta.getSchemas();
1083 } else if (isBuiltIn(sql, "@tables")) {
1084 String[] p = split(sql);
1085 String[] types = p[4] == null ? null : StringUtils.arraySplit(p[4], ',', false);
1086 return meta.getTables(p[1], p[2], p[3], types);
1087 } else if (isBuiltIn(sql, "@table_privileges")) {
1088 String[] p = split(sql);
1089 return meta.getTablePrivileges(p[1], p[2], p[3]);
1090 } else if (isBuiltIn(sql, "@table_types")) {
1091 return meta.getTableTypes();
1092 } else if (isBuiltIn(sql, "@type_info")) {
1093 return meta.getTypeInfo();
1094 } else if (isBuiltIn(sql, "@udts")) {
1095 String[] p = split(sql);
1096 int[] types;
1097 if (p[4] == null) {
1098 types = null;
1099 } else {
1100 String[] t = StringUtils.arraySplit(p[4], ',', false);
1101 types = new int[t.length];
1102 for (int i = 0; i < t.length; i++) {
1103 types[i] = Integer.parseInt(t[i]);
1104 }
1105 }
1106 return meta.getUDTs(p[1], p[2], p[3], types);
1107 } else if (isBuiltIn(sql, "@version_columns")) {
1108 String[] p = split(sql);
1109 return meta.getVersionColumns(p[1], p[2], p[3]);
1110 } else if (isBuiltIn(sql, "@memory")) {
1111 SimpleResultSet rs = new SimpleResultSet();
1112 rs.addColumn("Type", Types.VARCHAR, 0, 0);
1113 rs.addColumn("KB", Types.VARCHAR, 0, 0);
1114 rs.addRow("Used Memory", "" + Utils.getMemoryUsed());
1115 rs.addRow("Free Memory", "" + Utils.getMemoryFree());
1116 return rs;
1117 } else if (isBuiltIn(sql, "@info")) {
1118 SimpleResultSet rs = new SimpleResultSet();
1119 rs.addColumn("KEY", Types.VARCHAR, 0, 0);
1120 rs.addColumn("VALUE", Types.VARCHAR, 0, 0);
1121 rs.addRow("conn.getCatalog", conn.getCatalog());
1122 rs.addRow("conn.getAutoCommit", "" + conn.getAutoCommit());
1123 rs.addRow("conn.getTransactionIsolation", "" + conn.getTransactionIsolation());
1124 rs.addRow("conn.getWarnings", "" + conn.getWarnings());
1125 String map;
1126 try {
1127 map = "" + conn.getTypeMap();
1128 } catch (SQLException e) {
1129 map = e.toString();
1130 }
1131 rs.addRow("conn.getTypeMap", "" + map);
1132 rs.addRow("conn.isReadOnly", "" + conn.isReadOnly());
1133 rs.addRow("conn.getHoldability", "" + conn.getHoldability());
1134 addDatabaseMetaData(rs, meta);
1135 return rs;
1136 } else if (isBuiltIn(sql, "@attributes")) {
1137 String[] p = split(sql);
1138 return meta.getAttributes(p[1], p[2], p[3], p[4]);
1139 } else if (isBuiltIn(sql, "@super_tables")) {
1140 String[] p = split(sql);
1141 return meta.getSuperTables(p[1], p[2], p[3]);
1142 } else if (isBuiltIn(sql, "@super_types")) {
1143 String[] p = split(sql);
1144 return meta.getSuperTypes(p[1], p[2], p[3]);
1145 } else if (isBuiltIn(sql, "@prof_stop")) {
1146 if (profiler != null) {
1147 profiler.stopCollecting();
1148 SimpleResultSet rs = new SimpleResultSet();
1149 rs.addColumn("Top Stack Trace(s)", Types.VARCHAR, 0, 0);
1150 rs.addRow(profiler.getTop(3));
1151 profiler = null;
1152 return rs;
1153 }
1154 }
1155 return null;
1156 }
1157
1158 private static void addDatabaseMetaData(SimpleResultSet rs, DatabaseMetaData meta) {
1159 Method[] methods = DatabaseMetaData.class.getDeclaredMethods();
1160 Arrays.sort(methods, new Comparator<Method>() {
1161 public int compare(Method o1, Method o2) {
1162 return o1.toString().compareTo(o2.toString());
1163 }
1164 });
1165 for (Method m : methods) {
1166 if (m.getParameterTypes().length == 0) {
1167 try {
1168 Object o = m.invoke(meta);
1169 rs.addRow("meta." + m.getName(), "" + o);
1170 } catch (InvocationTargetException e) {
1171 rs.addRow("meta." + m.getName(), e.getTargetException().toString());
1172 } catch (Exception e) {
1173 rs.addRow("meta." + m.getName(), e.toString());
1174 }
1175 }
1176 }
1177 }
1178
1179 private static String[] split(String s) {
1180 String[] list = new String[10];
1181 String[] t = StringUtils.arraySplit(s, ' ', true);
1182 System.arraycopy(t, 0, list, 0, t.length);
1183 for (int i = 0; i < list.length; i++) {
1184 if ("null".equals(list[i])) {
1185 list[i] = null;
1186 }
1187 }
1188 return list;
1189 }
1190
1191 private int getMaxrows() {
1192 String r = (String) session.get("maxrows");
1193 int maxrows = r == null ? 0 : Integer.parseInt(r);
1194 return maxrows;
1195 }
1196
1197 private String getResult(Connection conn, int id, String sql, boolean allowEdit, boolean forceEdit) {
1198 try {
1199 sql = sql.trim();
1200 StringBuilder buff = new StringBuilder();
1201 String sqlUpper = StringUtils.toUpperEnglish(sql);
1202 if (sqlUpper.indexOf("CREATE") >= 0 || sqlUpper.indexOf("DROP") >= 0 || sqlUpper.indexOf("ALTER") >= 0
1203 || sqlUpper.indexOf("RUNSCRIPT") >= 0) {
1204 String sessionId = attributes.getProperty("jsessionid");
1205 buff.append("<script type=\"text/javascript\">top['h2menu'].location='tables.do?jsessionid="
1206 + sessionId + "';</script>");
1207 }
1208 Statement stat;
1209 DbContents contents = session.getContents();
1210 if (forceEdit || (allowEdit && contents.isH2)) {
1211 stat = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
1212 } else {
1213 stat = conn.createStatement();
1214 }
1215 ResultSet rs;
1216 long time = System.currentTimeMillis();
1217 boolean metadata = false;
1218 boolean generatedKeys = false;
1219 boolean edit = false;
1220 boolean list = false;
1221 if (isBuiltIn(sql, "@autocommit_true")) {
1222 conn.setAutoCommit(true);
1223 return "${text.result.autoCommitOn}";
1224 } else if (isBuiltIn(sql, "@autocommit_false")) {
1225 conn.setAutoCommit(false);
1226 return "${text.result.autoCommitOff}";
1227 } else if (isBuiltIn(sql, "@cancel")) {
1228 stat = session.executingStatement;
1229 if (stat != null) {
1230 stat.cancel();
1231 buff.append("${text.result.statementWasCanceled}");
1232 } else {
1233 buff.append("${text.result.noRunningStatement}");
1234 }
1235 return buff.toString();
1236 } else if (isBuiltIn(sql, "@edit")) {
1237 edit = true;
1238 sql = sql.substring("@edit".length()).trim();
1239 session.put("resultSetSQL", sql);
1240 }
1241 if (isBuiltIn(sql, "@list")) {
1242 list = true;
1243 sql = sql.substring("@list".length()).trim();
1244 }
1245 if (isBuiltIn(sql, "@meta")) {
1246 metadata = true;
1247 sql = sql.substring("@meta".length()).trim();
1248 }
1249 if (isBuiltIn(sql, "@generated")) {
1250 generatedKeys = true;
1251 sql = sql.substring("@generated".length()).trim();
1252 } else if (isBuiltIn(sql, "@history")) {
1253 buff.append(getHistoryString());
1254 return buff.toString();
1255 } else if (isBuiltIn(sql, "@loop")) {
1256 sql = sql.substring("@loop".length()).trim();
1257 int idx = sql.indexOf(' ');
1258 int count = Integer.decode(sql.substring(0, idx));
1259 sql = sql.substring(idx).trim();
1260 return executeLoop(conn, count, sql);
1261 } else if (isBuiltIn(sql, "@maxrows")) {
1262 int maxrows = (int) Double.parseDouble(sql.substring("@maxrows".length()).trim());
1263 session.put("maxrows", "" + maxrows);
1264 return "${text.result.maxrowsSet}";
1265 } else if (isBuiltIn(sql, "@parameter_meta")) {
1266 sql = sql.substring("@parameter_meta".length()).trim();
1267 PreparedStatement prep = conn.prepareStatement(sql);
1268 buff.append(getParameterResultSet(prep.getParameterMetaData()));
1269 return buff.toString();
1270 } else if (isBuiltIn(sql, "@password_hash")) {
1271 sql = sql.substring("@password_hash".length()).trim();
1272 String[] p = split(sql);
1273 return StringUtils.convertBytesToHex(SHA256.getKeyPasswordHash(p[0], p[1].toCharArray()));
1274 } else if (isBuiltIn(sql, "@prof_start")) {
1275 if (profiler != null) {
1276 profiler.stopCollecting();
1277 }
1278 profiler = new Profiler();
1279 profiler.startCollecting();
1280 return "Ok";
1281 } else if (isBuiltIn(sql, "@sleep")) {
1282 String s = sql.substring("@sleep".length()).trim();
1283 int sleep = 1;
1284 if (s.length() > 0) {
1285 sleep = Integer.parseInt(s);
1286 }
1287 Thread.sleep(sleep * 1000);
1288 return "Ok";
1289 } else if (isBuiltIn(sql, "@transaction_isolation")) {
1290 String s = sql.substring("@transaction_isolation".length()).trim();
1291 if (s.length() > 0) {
1292 int level = Integer.parseInt(s);
1293 conn.setTransactionIsolation(level);
1294 }
1295 buff.append("Transaction Isolation: " + conn.getTransactionIsolation() + "<br />");
1296 buff.append(Connection.TRANSACTION_READ_UNCOMMITTED + ": read_uncommitted<br />");
1297 buff.append(Connection.TRANSACTION_READ_COMMITTED + ": read_committed<br />");
1298 buff.append(Connection.TRANSACTION_REPEATABLE_READ + ": repeatable_read<br />");
1299 buff.append(Connection.TRANSACTION_SERIALIZABLE + ": serializable");
1300 }
1301 if (sql.startsWith("@")) {
1302 rs = getMetaResultSet(conn, sql);
1303 if (rs == null) {
1304 buff.append("?: " + sql);
1305 return buff.toString();
1306 }
1307 } else {
1308 int maxrows = getMaxrows();
1309 stat.setMaxRows(maxrows);
1310 session.executingStatement = stat;
1311 boolean isResultSet = stat.execute(sql);
1312 session.addCommand(sql);
1313 if (generatedKeys) {
1314 rs = null;
1315 rs = stat.getGeneratedKeys();
1316 } else {
1317 if (!isResultSet) {
1318 buff.append("${text.result.updateCount}: " + stat.getUpdateCount());
1319 time = System.currentTimeMillis() - time;
1320 buff.append("<br />(").append(time).append(" ms)");
1321 stat.close();
1322 return buff.toString();
1323 }
1324 rs = stat.getResultSet();
1325 }
1326 }
1327 time = System.currentTimeMillis() - time;
1328 buff.append(getResultSet(sql, rs, metadata, list, edit, time, allowEdit));
1329 // SQLWarning warning = stat.getWarnings();
1330 // if(warning != null) {
1331 // buff.append("<br />Warning:<br />").
1332 // append(getStackTrace(id, warning));
1333 // }
1334 if (!edit) {
1335 stat.close();
1336 }
1337 return buff.toString();
1338 } catch (Throwable e) {
1339 // throwable: including OutOfMemoryError and so on
1340 return getStackTrace(id, e, session.getContents().isH2);
1341 } finally {
1342 session.executingStatement = null;
1343 }
1344 }
1345
1346 private static boolean isBuiltIn(String sql, String builtIn) {
1347 return StringUtils.startsWithIgnoreCase(sql, builtIn);
1348 }
1349
1350 private String executeLoop(Connection conn, int count, String sql) throws SQLException {
1351 ArrayList<Integer> params = New.arrayList();
1352 int idx = 0;
1353 while (!stop) {
1354 idx = sql.indexOf('?', idx);
1355 if (idx < 0) {
1356 break;
1357 }
1358 if (isBuiltIn(sql.substring(idx), "?/*rnd*/")) {
1359 params.add(1);
1360 sql = sql.substring(0, idx) + "?" + sql.substring(idx + "/*rnd*/".length() + 1);
1361 } else {
1362 params.add(0);
1363 }
1364 idx++;
1365 }
1366 boolean prepared;
1367 Random random = new Random(1);
1368 long time = System.currentTimeMillis();
1369 if (isBuiltIn(sql, "@statement")) {
1370 sql = sql.substring("@statement".length()).trim();
1371 prepared = false;
1372 Statement stat = conn.createStatement();
1373 for (int i = 0; !stop && i < count; i++) {
1374 String s = sql;
1375 for (Integer type : params) {
1376 idx = s.indexOf('?');
1377 if (type.intValue() == 1) {
1378 s = s.substring(0, idx) + random.nextInt(count) + s.substring(idx + 1);
1379 } else {
1380 s = s.substring(0, idx) + i + s.substring(idx + 1);
1381 }
1382 }
1383 if (stat.execute(s)) {
1384 ResultSet rs = stat.getResultSet();
1385 while (!stop && rs.next()) {
1386 // maybe get the data as well
1387 }
1388 rs.close();
1389 }
1390 }
1391 } else {
1392 prepared = true;
1393 PreparedStatement prep = conn.prepareStatement(sql);
1394 for (int i = 0; !stop && i < count; i++) {
1395 for (int j = 0; j < params.size(); j++) {
1396 Integer type = params.get(j);
1397 if (type.intValue() == 1) {
1398 prep.setInt(j + 1, random.nextInt(count));
1399 } else {
1400 prep.setInt(j + 1, i);
1401 }
1402 }
1403 if (session.getContents().isSQLite) {
1404 // SQLite currently throws an exception on prep.execute()
1405 prep.executeUpdate();
1406 } else {
1407 if (prep.execute()) {
1408 ResultSet rs = prep.getResultSet();
1409 while (!stop && rs.next()) {
1410 // maybe get the data as well
1411 }
1412 rs.close();
1413 }
1414 }
1415 }
1416 }
1417 time = System.currentTimeMillis() - time;
1418 StatementBuilder buff = new StatementBuilder();
1419 buff.append(time).append(" ms: ").append(count).append(" * ");
1420 if (prepared) {
1421 buff.append("(Prepared) ");
1422 } else {
1423 buff.append("(Statement) ");
1424 }
1425 buff.append('(');
1426 for (int p : params) {
1427 buff.appendExceptFirst(", ");
1428 buff.append(p == 0 ? "i" : "rnd");
1429 }
1430 return buff.append(") ").append(sql).toString();
1431 }
1432
1433 private String getHistoryString() {
1434 StringBuilder buff = new StringBuilder();
1435 ArrayList<String> history = session.getCommands();
1436 buff.append("<table cellspacing=0 cellpadding=0><tr><th></th><th>Command</th></tr>");
1437 for (int i = history.size() - 1; i >= 0; i--) {
1438 String sql = history.get(i);
1439 buff.append("<tr><td><a href=\"getHistory.do?id=").
1440 append(i).
1441 append("&jsessionid=${sessionId}\" target=\"h2query\" >").
1442 append("<img width=16 height=16 src=\"ico_write.gif\" onmouseover = \"this.className ='icon_hover'\" ").
1443 append("onmouseout = \"this.className ='icon'\" class=\"icon\" alt=\"${text.resultEdit.edit}\" ").
1444 append("title=\"${text.resultEdit.edit}\" border=\"1\"/></a>").
1445 append("</td><td>").
1446 append(PageParser.escapeHtml(sql)).
1447 append("</td></tr>");
1448 }
1449 buff.append("</table>");
1450 return buff.toString();
1451 }
1452
1453 private static String getParameterResultSet(ParameterMetaData meta) throws SQLException {
1454 StringBuilder buff = new StringBuilder();
1455 if (meta == null) {
1456 return "No parameter meta data";
1457 }
1458 buff.append("<table cellspacing=0 cellpadding=0>").
1459 append("<tr><th>className</th><th>mode</th><th>type</th>").
1460 append("<th>typeName</th><th>precision</th><th>scale</th></tr>");
1461 for (int i = 0; i < meta.getParameterCount(); i++) {
1462 buff.append("</tr><td>").
1463 append(meta.getParameterClassName(i + 1)).
1464 append("</td><td>").
1465 append(meta.getParameterMode(i + 1)).
1466 append("</td><td>").
1467 append(meta.getParameterType(i + 1)).
1468 append("</td><td>").
1469 append(meta.getParameterTypeName(i + 1)).
1470 append("</td><td>").
1471 append(meta.getPrecision(i + 1)).
1472 append("</td><td>").
1473 append(meta.getScale(i + 1)).
1474 append("</td></tr>");
1475 }
1476 buff.append("</table>");
1477 return buff.toString();
1478 }
1479
1480 private String getResultSet(String sql, ResultSet rs, boolean metadata, boolean list, boolean edit, long time,
1481 boolean allowEdit) throws SQLException {
1482 int maxrows = getMaxrows();
1483 time = System.currentTimeMillis() - time;
1484 StringBuilder buff = new StringBuilder();
1485 if (edit) {
1486 buff.append("<form id=\"editing\" name=\"editing\" method=\"post\" " +
1487 "action=\"editResult.do?jsessionid=${sessionId}\" id=\"mainForm\" target=\"h2result\">" +
1488 "<input type=\"hidden\" name=\"op\" value=\"1\" />" +
1489 "<input type=\"hidden\" name=\"row\" value=\"\" />" +
1490 "<table cellspacing=0 cellpadding=0 id=\"editTable\">");
1491 } else {
1492 buff.append("<table cellspacing=0 cellpadding=0>");
1493 }
1494 if (metadata) {
1495 SimpleResultSet r = new SimpleResultSet();
1496 r.addColumn("#", Types.INTEGER, 0, 0);
1497 r.addColumn("label", Types.VARCHAR, 0, 0);
1498 r.addColumn("catalog", Types.VARCHAR, 0, 0);
1499 r.addColumn("schema", Types.VARCHAR, 0, 0);
1500 r.addColumn("table", Types.VARCHAR, 0, 0);
1501 r.addColumn("column", Types.VARCHAR, 0, 0);
1502 r.addColumn("type", Types.INTEGER, 0, 0);
1503 r.addColumn("typeName", Types.VARCHAR, 0, 0);
1504 r.addColumn("class", Types.VARCHAR, 0, 0);
1505 r.addColumn("precision", Types.INTEGER, 0, 0);
1506 r.addColumn("scale", Types.INTEGER, 0, 0);
1507 r.addColumn("displaySize", Types.INTEGER, 0, 0);
1508 r.addColumn("autoIncrement", Types.BOOLEAN, 0, 0);
1509 r.addColumn("caseSensitive", Types.BOOLEAN, 0, 0);
1510 r.addColumn("currency", Types.BOOLEAN, 0, 0);
1511 r.addColumn("nullable", Types.INTEGER, 0, 0);
1512 r.addColumn("readOnly", Types.BOOLEAN, 0, 0);
1513 r.addColumn("searchable", Types.BOOLEAN, 0, 0);
1514 r.addColumn("signed", Types.BOOLEAN, 0, 0);
1515 r.addColumn("writable", Types.BOOLEAN, 0, 0);
1516 r.addColumn("definitelyWritable", Types.BOOLEAN, 0, 0);
1517 ResultSetMetaData m = rs.getMetaData();
1518 for (int i = 1; i <= m.getColumnCount(); i++) {
1519 r.addRow(i,
1520 m.getColumnLabel(i),
1521 m.getCatalogName(i),
1522 m.getSchemaName(i),
1523 m.getTableName(i),
1524 m.getColumnName(i),
1525 m.getColumnType(i),
1526 m.getColumnTypeName(i),
1527 m.getColumnClassName(i),
1528 m.getPrecision(i),
1529 m.getScale(i),
1530 m.getColumnDisplaySize(i),
1531 m.isAutoIncrement(i),
1532 m.isCaseSensitive(i),
1533 m.isCurrency(i),
1534 m.isNullable(i),
1535 m.isReadOnly(i),
1536 m.isSearchable(i),
1537 m.isSigned(i),
1538 m.isWritable(i),
1539 m.isDefinitelyWritable(i));
1540 }
1541 rs = r;
1542 }
1543 ResultSetMetaData meta = rs.getMetaData();
1544 int columns = meta.getColumnCount();
1545 int rows = 0;
1546 if (list) {
1547 buff.append("<tr><th>Column</th><th>Data</th></tr><tr>");
1548 while (rs.next()) {
1549 if (maxrows > 0 && rows >= maxrows) {
1550 break;
1551 }
1552 rows++;
1553 buff.append("<tr><td>Row #</td><td>").
1554 append(rows).append("</tr>");
1555 for (int i = 0; i < columns; i++) {
1556 buff.append("<tr><td>").
1557 append(PageParser.escapeHtml(meta.getColumnLabel(i + 1))).
1558 append("</td><td>").
1559 append(escapeData(rs, i + 1)).
1560 append("</td></tr>");
1561 }
1562 }
1563 } else {
1564 buff.append("<tr>");
1565 if (edit) {
1566 buff.append("<th>${text.resultEdit.action}</th>");
1567 }
1568 for (int i = 0; i < columns; i++) {
1569 buff.append("<th>").
1570 append(PageParser.escapeHtml(meta.getColumnLabel(i + 1))).
1571 append("</th>");
1572 }
1573 buff.append("</tr>");
1574 while (rs.next()) {
1575 if (maxrows > 0 && rows >= maxrows) {
1576 break;
1577 }
1578 rows++;
1579 buff.append("<tr>");
1580 if (edit) {
1581 buff.append("<td>").
1582 append("<img onclick=\"javascript:editRow(").
1583 append(rs.getRow()).
1584 append(",'${sessionId}', '${text.resultEdit.save}', '${text.resultEdit.cancel}'").
1585 append(")\" width=16 height=16 src=\"ico_write.gif\" " +
1586 "onmouseover = \"this.className ='icon_hover'\" " +
1587 "onmouseout = \"this.className ='icon'\" " +
1588 "class=\"icon\" alt=\"${text.resultEdit.edit}\" " +
1589 "title=\"${text.resultEdit.edit}\" border=\"1\"/>").
1590 append("<a href=\"editResult.do?op=2&row=").
1591 append(rs.getRow()).
1592 append("&jsessionid=${sessionId}\" target=\"h2result\" >" +
1593 "<img width=16 height=16 src=\"ico_remove.gif\" " +
1594 "onmouseover = \"this.className ='icon_hover'\" " +
1595 "onmouseout = \"this.className ='icon'\" " +
1596 "class=\"icon\" alt=\"${text.resultEdit.delete}\" " +
1597 "title=\"${text.resultEdit.delete}\" border=\"1\" /></a>").
1598 append("</td>");
1599 }
1600 for (int i = 0; i < columns; i++) {
1601 buff.append("<td>").
1602 append(escapeData(rs, i + 1)).
1603 append("</td>");
1604 }
1605 buff.append("</tr>");
1606 }
1607 }
1608 boolean isUpdatable = false;
1609 try {
1610 isUpdatable = rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE
1611 && rs.getType() != ResultSet.TYPE_FORWARD_ONLY;
1612 } catch (NullPointerException e) {
1613 // ignore
1614 // workaround for a JDBC-ODBC bridge problem
1615 }
1616 if (edit) {
1617 ResultSet old = session.result;
1618 if (old != null) {
1619 old.close();
1620 }
1621 session.result = rs;
1622 } else {
1623 rs.close();
1624 }
1625 if (edit) {
1626 buff.append("<tr><td>").
1627 append("<img onclick=\"javascript:editRow(-1, '${sessionId}', '${text.resultEdit.save}', '${text.resultEdit.cancel}'").
1628 append(")\" width=16 height=16 src=\"ico_add.gif\" " +
1629 "onmouseover = \"this.className ='icon_hover'\" " +
1630 "onmouseout = \"this.className ='icon'\" " +
1631 "class=\"icon\" alt=\"${text.resultEdit.add}\" " +
1632 "title=\"${text.resultEdit.add}\" border=\"1\"/>").
1633 append("</td>");
1634 for (int i = 0; i < columns; i++) {
1635 buff.append("<td></td>");
1636 }
1637 buff.append("</tr>");
1638 }
1639 buff.append("</table>");
1640 if (edit) {
1641 buff.append("</form>");
1642 }
1643 if (rows == 0) {
1644 buff.append("(${text.result.noRows}");
1645 } else if (rows == 1) {
1646 buff.append("(${text.result.1row}");
1647 } else {
1648 buff.append('(').append(rows).append(" ${text.result.rows}");
1649 }
1650 buff.append(", ");
1651 time = System.currentTimeMillis() - time;
1652 buff.append(time).append(" ms)");
1653 if (!edit && isUpdatable && allowEdit) {
1654 buff.append("<br /><br />" +
1655 "<form name=\"editResult\" method=\"post\" " +
1656 "action=\"query.do?jsessionid=${sessionId}\" target=\"h2result\">" +
1657 "<input type=\"submit\" class=\"button\" value=\"${text.resultEdit.editResult}\" />" +
1658 "<input type=\"hidden\" name=\"sql\" value=\"@edit ").
1659 append(PageParser.escapeHtmlData(sql)).
1660 append("\" /></form>");
1661 }
1662 return buff.toString();
1663 }
1664
1665 /**
1666 * Save the current connection settings to the properties file.
1667 *
1668 * @return the file to open afterwards
1669 */
1670 private String settingSave() {
1671 ConnectionInfo info = new ConnectionInfo();
1672 info.name = attributes.getProperty("name", "");
1673 info.driver = attributes.getProperty("driver", "");
1674 info.url = attributes.getProperty("url", "");
1675 info.user = attributes.getProperty("user", "");
1676 server.updateSetting(info);
1677 attributes.put("setting", info.name);
1678 server.saveProperties(null);
1679 return "index.do";
1680 }
1681
1682 private static String escapeData(ResultSet rs, int columnIndex) throws SQLException {
1683 String d = rs.getString(columnIndex);
1684 if (d == null) {
1685 return "<i>null</i>";
1686 } else if (d.length() > 100000) {
1687 String s;
1688 if (isBinary(rs.getMetaData().getColumnType(columnIndex))) {
1689 s = PageParser.escapeHtml(d.substring(0, 6)) + "... (" + (d.length() / 2) + " ${text.result.bytes})";
1690 } else {
1691 s = PageParser.escapeHtml(d.substring(0, 100)) + "... (" + d.length() + " ${text.result.characters})";
1692 }
1693 return "<div style='display: none'>=+</div>" + s;
1694 } else if (d.equals("null") || d.startsWith("= ") || d.startsWith("=+")) {
1695 return "<div style='display: none'>= </div>" + PageParser.escapeHtml(d);
1696 } else if (d.equals("")) {
1697 // PageParser.escapeHtml replaces "" with a non-breaking space
1698 return "";
1699 }
1700 return PageParser.escapeHtml(d);
1701 }
1702
1703 private static boolean isBinary(int sqlType) {
1704 switch (sqlType) {
1705 case Types.BINARY:
1706 case Types.BLOB:
1707 case Types.JAVA_OBJECT:
1708 case Types.LONGVARBINARY:
1709 case Types.OTHER:
1710 case Types.VARBINARY:
1711 return true;
1712 }
1713 return false;
1714 }
1715
1716 private void unescapeData(String x, ResultSet rs, int columnIndex) throws SQLException {
1717 if (x.equals("null")) {
1718 rs.updateNull(columnIndex);
1719 return;
1720 } else if (x.startsWith("=+")) {
1721 // don't update
1722 return;
1723 } else if (x.equals("=*")) {
1724 // set an appropriate default value
1725 int type = rs.getMetaData().getColumnType(columnIndex);
1726 switch (type) {
1727 case Types.TIME:
1728 rs.updateString(columnIndex, "12:00:00");
1729 break;
1730 case Types.TIMESTAMP:
1731 case Types.DATE:
1732 rs.updateString(columnIndex, "2001-01-01");
1733 break;
1734 default:
1735 rs.updateString(columnIndex, "1");
1736 break;
1737 }
1738 return;
1739 } else if (x.startsWith("= ")) {
1740 x = x.substring(2);
1741 }
1742 ResultSetMetaData meta = rs.getMetaData();
1743 int type = meta.getColumnType(columnIndex);
1744 if (session.getContents().isH2) {
1745 rs.updateString(columnIndex, x);
1746 return;
1747 }
1748 switch (type) {
1749 case Types.BIGINT:
1750 rs.updateLong(columnIndex, Long.decode(x));
1751 break;
1752 case Types.DECIMAL:
1753 rs.updateBigDecimal(columnIndex, new BigDecimal(x));
1754 break;
1755 case Types.DOUBLE:
1756 case Types.FLOAT:
1757 rs.updateDouble(columnIndex, Double.parseDouble(x));
1758 break;
1759 case Types.REAL:
1760 rs.updateFloat(columnIndex, Float.parseFloat(x));
1761 break;
1762 case Types.INTEGER:
1763 rs.updateInt(columnIndex, Integer.decode(x));
1764 break;
1765 case Types.TINYINT:
1766 rs.updateShort(columnIndex, Short.decode(x));
1767 break;
1768 default:
1769 rs.updateString(columnIndex, x);
1770 }
1771 }
1772
1773 private String settingRemove() {
1774 String setting = attributes.getProperty("name", "");
1775 server.removeSetting(setting);
1776 ArrayList<ConnectionInfo> settings = server.getSettings();
1777 if (settings.size() > 0) {
1778 attributes.put("setting", settings.get(0));
1779 }
1780 server.saveProperties(null);
1781 return "index.do";
1782 }
1783
1784 /**
1785 * Get the current mime type.
1786 *
1787 * @return the mime type
1788 */
1789 String getMimeType() {
1790 return mimeType;
1791 }
1792
1793 boolean getCache() {
1794 return cache;
1795 }
1796
1797 WebSession getSession() {
1798 return session;
1799 }
1800
1801 private void trace(String s) {
1802 server.trace(s);
1803 }
1804
1805 }
1806