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