0001: /*
0002: * Copyright 2004-2008 H2 Group. Licensed under the H2 License, Version 1.0
0003: * (http://h2database.com/html/license.html).
0004: * Initial Developer: H2 Group
0005: */
0006: package org.h2.server.web;
0007:
0008: import java.io.BufferedInputStream;
0009: import java.io.BufferedOutputStream;
0010: import java.io.DataInputStream;
0011: import java.io.File;
0012: import java.io.FileInputStream;
0013: import java.io.FileWriter;
0014: import java.io.IOException;
0015: import java.io.InputStream;
0016: import java.io.OutputStream;
0017: import java.io.PrintWriter;
0018: import java.io.StringReader;
0019: import java.io.StringWriter;
0020: import java.lang.reflect.Method;
0021: import java.net.MalformedURLException;
0022: import java.net.Socket;
0023: import java.security.SecureClassLoader;
0024: import java.sql.Connection;
0025: import java.sql.DatabaseMetaData;
0026: import java.sql.ParameterMetaData;
0027: import java.sql.PreparedStatement;
0028: import java.sql.ResultSet;
0029: import java.sql.ResultSetMetaData;
0030: import java.sql.SQLException;
0031: import java.sql.Statement;
0032: import java.sql.Types;
0033: import java.text.SimpleDateFormat;
0034: import java.util.ArrayList;
0035: import java.util.Collections;
0036: import java.util.Date;
0037: import java.util.HashMap;
0038: import java.util.Iterator;
0039: import java.util.Locale;
0040: import java.util.Map;
0041: import java.util.Properties;
0042: import java.util.Random;
0043: import java.util.StringTokenizer;
0044: import java.util.Map.Entry;
0045:
0046: import org.h2.api.DatabaseEventListener;
0047: import org.h2.bnf.Bnf;
0048: import org.h2.constant.ErrorCode;
0049: import org.h2.engine.Constants;
0050: import org.h2.jdbc.JdbcSQLException;
0051: import org.h2.message.TraceSystem;
0052: import org.h2.tools.SimpleResultSet;
0053: import org.h2.util.IOUtils;
0054: import org.h2.util.JdbcUtils;
0055: import org.h2.util.MathUtils;
0056: import org.h2.util.MemoryUtils;
0057: import org.h2.util.NetUtils;
0058: import org.h2.util.ObjectArray;
0059: import org.h2.util.ObjectUtils;
0060: import org.h2.util.ScriptReader;
0061: import org.h2.util.StringUtils;
0062:
0063: /**
0064: * For each connection to a session, an object of this class is created.
0065: * This class is used by the H2 Console.
0066: */
0067: class WebThread extends Thread implements DatabaseEventListener {
0068: private WebServer server;
0069: private WebSession session;
0070: private Properties attributes;
0071: private Socket socket;
0072:
0073: private InputStream input;
0074: private OutputStream output;
0075: private String ifModifiedSince;
0076: private String mimeType;
0077: private boolean cache;
0078: private int listenerLastState;
0079: private long listenerLastEvent;
0080: private boolean stop;
0081:
0082: // TODO web: support online data editing like http://numsum.com/
0083:
0084: WebThread(Socket socket, WebServer server) {
0085: this .server = server;
0086: this .socket = socket;
0087: setName("H2 Console thread");
0088: }
0089:
0090: void setSession(WebSession session, Properties attributes) {
0091: this .session = session;
0092: this .attributes = attributes;
0093: }
0094:
0095: public void stopNow() {
0096: this .stop = true;
0097: }
0098:
0099: private String getAllowedFile(String requestedFile) {
0100: if (!allow()) {
0101: return "notAllowed.jsp";
0102: }
0103: if (requestedFile.length() == 0) {
0104: return "index.do";
0105: }
0106: return requestedFile;
0107: }
0108:
0109: public String processRequest(String file, String hostAddr) {
0110: int index = file.lastIndexOf('.');
0111: String suffix;
0112: if (index >= 0) {
0113: suffix = file.substring(index + 1);
0114: } else {
0115: suffix = "";
0116: }
0117: if ("ico".equals(suffix)) {
0118: mimeType = "image/x-icon";
0119: cache = true;
0120: } else if ("gif".equals(suffix)) {
0121: mimeType = "image/gif";
0122: cache = true;
0123: } else if ("css".equals(suffix)) {
0124: cache = true;
0125: mimeType = "text/css";
0126: } else if ("html".equals(suffix) || "do".equals(suffix)
0127: || "jsp".equals(suffix)) {
0128: cache = false;
0129: mimeType = "text/html";
0130: if (session == null) {
0131: session = server.createNewSession(hostAddr);
0132: if (!"notAllowed.jsp".equals(file)) {
0133: file = "index.do";
0134: }
0135: }
0136: } else if ("js".equals(suffix)) {
0137: cache = true;
0138: mimeType = "text/javascript";
0139: } else {
0140: cache = false;
0141: mimeType = "text/html";
0142: file = "error.jsp";
0143: server.trace("unknown mime type, file " + file);
0144: }
0145: server.trace("mimeType=" + mimeType);
0146: server.trace(file);
0147: if (file.endsWith(".do")) {
0148: file = process(file);
0149: }
0150: return file;
0151: }
0152:
0153: public void run() {
0154: try {
0155: input = new BufferedInputStream(socket.getInputStream());
0156: output = new BufferedOutputStream(socket.getOutputStream());
0157: while (true) {
0158: if (!process()) {
0159: break;
0160: }
0161: }
0162: } catch (IOException e) {
0163: TraceSystem.traceThrowable(e);
0164: } catch (SQLException e) {
0165: TraceSystem.traceThrowable(e);
0166: }
0167: IOUtils.closeSilently(output);
0168: IOUtils.closeSilently(input);
0169: try {
0170: socket.close();
0171: } catch (IOException e) {
0172: // ignore
0173: } finally {
0174: server.remove(this );
0175: }
0176: }
0177:
0178: public boolean process() throws IOException, SQLException {
0179: boolean keepAlive = false;
0180: String head = readHeaderLine();
0181: if (head.startsWith("GET ") || head.startsWith("POST ")) {
0182: int begin = head.indexOf('/'), end = head.lastIndexOf(' ');
0183: String file = head.substring(begin + 1, end).trim();
0184: server.trace(head + ": " + file);
0185: file = getAllowedFile(file);
0186: attributes = new Properties();
0187: int paramIndex = file.indexOf("?");
0188: session = null;
0189: if (paramIndex >= 0) {
0190: String attrib = file.substring(paramIndex + 1);
0191: parseAttributes(attrib);
0192: String sessionId = attributes.getProperty("jsessionid");
0193: file = file.substring(0, paramIndex);
0194: session = server.getSession(sessionId);
0195: }
0196: keepAlive = parseHeader();
0197: String hostAddr = socket.getInetAddress().getHostAddress();
0198: file = processRequest(file, hostAddr);
0199: if (file.length() == 0) {
0200: // asynchronous request
0201: return true;
0202: }
0203: String message;
0204: byte[] bytes;
0205: if (cache
0206: && ifModifiedSince != null
0207: && ifModifiedSince
0208: .equals(server.getStartDateTime())) {
0209: bytes = null;
0210: message = "HTTP/1.1 304 Not Modified\n";
0211: } else {
0212: bytes = server.getFile(file);
0213: if (bytes == null) {
0214: message = "HTTP/1.0 404 Not Found\n";
0215: bytes = StringUtils.utf8Encode("File not found: "
0216: + file);
0217: } else {
0218: if (session != null && file.endsWith(".jsp")) {
0219: String page = StringUtils.utf8Decode(bytes);
0220: page = PageParser.parse(server, page,
0221: session.map);
0222: try {
0223: bytes = StringUtils.utf8Encode(page);
0224: } catch (SQLException e) {
0225: server.traceError(e);
0226: }
0227: }
0228: message = "HTTP/1.1 200 OK\n";
0229: message += "Content-Type: " + mimeType + "\n";
0230: if (!cache) {
0231: message += "Cache-Control: no-cache\n";
0232: } else {
0233: message += "Cache-Control: max-age=10\n";
0234: message += "Last-Modified: "
0235: + server.getStartDateTime() + "\n";
0236: }
0237: message += "Content-Length: " + bytes.length + "\n";
0238: }
0239: }
0240: message += "\n";
0241: server.trace(message);
0242: output.write(message.getBytes());
0243: if (bytes != null) {
0244: output.write(bytes);
0245: }
0246: output.flush();
0247: }
0248: return keepAlive;
0249: }
0250:
0251: protected String getComboBox(String[] elements, String selected) {
0252: StringBuffer buff = new StringBuffer();
0253: for (int i = 0; i < elements.length; i++) {
0254: String value = elements[i];
0255: buff.append("<option value=\"");
0256: buff.append(PageParser.escapeHtmlData(value));
0257: buff.append("\"");
0258: if (value.equals(selected)) {
0259: buff.append(" selected");
0260: }
0261: buff.append(">");
0262: buff.append(PageParser.escapeHtml(value));
0263: buff.append("</option>");
0264: }
0265: return buff.toString();
0266: }
0267:
0268: protected String getComboBox(String[][] elements, String selected) {
0269: StringBuffer buff = new StringBuffer();
0270: for (int i = 0; i < elements.length; i++) {
0271: String[] n = elements[i];
0272: buff.append("<option value=\"");
0273: buff.append(PageParser.escapeHtmlData(n[0]));
0274: buff.append("\"");
0275: if (n[0].equals(selected)) {
0276: buff.append(" selected");
0277: }
0278: buff.append(">");
0279: buff.append(PageParser.escapeHtml(n[1]));
0280: buff.append("</option>");
0281: }
0282: return buff.toString();
0283: }
0284:
0285: private String readHeaderLine() throws IOException {
0286: StringBuffer buff = new StringBuffer();
0287: while (true) {
0288: int i = input.read();
0289: if (i == -1) {
0290: throw new IOException("Unexpected EOF");
0291: } else if (i == '\r' && input.read() == '\n') {
0292: return buff.length() > 0 ? buff.toString() : null;
0293: } else {
0294: buff.append((char) i);
0295: }
0296: }
0297: }
0298:
0299: private void parseAttributes(String s) throws SQLException {
0300: server.trace("data=" + s);
0301: while (s != null) {
0302: int idx = s.indexOf('=');
0303: if (idx >= 0) {
0304: String property = s.substring(0, idx);
0305: s = s.substring(idx + 1);
0306: idx = s.indexOf('&');
0307: String value;
0308: if (idx >= 0) {
0309: value = s.substring(0, idx);
0310: s = s.substring(idx + 1);
0311: } else {
0312: value = s;
0313: }
0314: // TODO compatibility problem with JDK 1.3
0315: // String attr = URLDecoder.decode(value, "UTF-8");
0316: // String attr = URLDecoder.decode(value);
0317: String attr = StringUtils.urlDecode(value);
0318: attributes.put(property, attr);
0319: } else {
0320: break;
0321: }
0322: }
0323: server.trace(attributes.toString());
0324: }
0325:
0326: private boolean parseHeader() throws IOException, SQLException {
0327: boolean keepAlive = false;
0328: server.trace("parseHeader");
0329: int len = 0;
0330: ifModifiedSince = null;
0331: while (true) {
0332: String line = readHeaderLine();
0333: if (line == null) {
0334: break;
0335: }
0336: server.trace(" " + line);
0337: String lower = StringUtils.toLowerEnglish(line);
0338: if (lower.startsWith("if-modified-since")) {
0339: ifModifiedSince = line.substring(line.indexOf(':') + 1)
0340: .trim();
0341: } else if (lower.startsWith("connection")) {
0342: String conn = line.substring(line.indexOf(':') + 1)
0343: .trim();
0344: if ("keep-alive".equals(conn)) {
0345: keepAlive = true;
0346: }
0347: } else if (lower.startsWith("content-length")) {
0348: len = Integer.parseInt(line.substring(
0349: line.indexOf(':') + 1).trim());
0350: server.trace("len=" + len);
0351: } else if (lower.startsWith("accept-language")) {
0352: if (session != null) {
0353: Locale locale = session.locale;
0354: if (locale == null) {
0355: String languages = line.substring(
0356: line.indexOf(':') + 1).trim();
0357: StringTokenizer tokenizer = new StringTokenizer(
0358: languages, ",;");
0359: while (tokenizer.hasMoreTokens()) {
0360: String token = tokenizer.nextToken();
0361: if (!token.startsWith("q=")) {
0362: if (server.supportsLanguage(token)) {
0363: int dash = token.indexOf('-');
0364: if (dash >= 0) {
0365: String language = token
0366: .substring(0, dash);
0367: String country = token
0368: .substring(dash + 1);
0369: locale = new Locale(language,
0370: country);
0371: } else {
0372: locale = new Locale(token, "");
0373: }
0374: session.locale = locale;
0375: String language = locale
0376: .getLanguage();
0377: session.put("language", language);
0378: server.readTranslations(session,
0379: language);
0380: break;
0381: }
0382: }
0383: }
0384: }
0385: }
0386: } else if (line.trim().length() == 0) {
0387: break;
0388: }
0389: }
0390: if (session != null && len > 0) {
0391: byte[] bytes = new byte[len];
0392: for (int pos = 0; pos < len;) {
0393: pos += input.read(bytes, pos, len - pos);
0394: }
0395: String s = new String(bytes);
0396: parseAttributes(s);
0397: }
0398: return keepAlive;
0399: }
0400:
0401: String process(String file) {
0402: server.trace("process " + file);
0403: while (file.endsWith(".do")) {
0404: if ("login.do".equals(file)) {
0405: file = login();
0406: } else if ("index.do".equals(file)) {
0407: file = index();
0408: } else if ("logout.do".equals(file)) {
0409: file = logout();
0410: } else if ("settingRemove.do".equals(file)) {
0411: file = settingRemove();
0412: } else if ("settingSave.do".equals(file)) {
0413: file = settingSave();
0414: } else if ("test.do".equals(file)) {
0415: file = test();
0416: } else if ("query.do".equals(file)) {
0417: file = query();
0418: } else if ("tables.do".equals(file)) {
0419: file = tables();
0420: } else if ("editResult.do".equals(file)) {
0421: file = editResult();
0422: } else if ("getHistory.do".equals(file)) {
0423: file = getHistory();
0424: } else if ("admin.do".equals(file)) {
0425: file = admin();
0426: } else if ("adminSave.do".equals(file)) {
0427: file = adminSave();
0428: } else if ("adminShutdown.do".equals(file)) {
0429: file = adminShutdown();
0430: } else if ("autoCompleteList.do".equals(file)) {
0431: file = autoCompleteList();
0432: } else {
0433: file = "error.jsp";
0434: }
0435: }
0436: server.trace("return " + file);
0437: return file;
0438: }
0439:
0440: private String autoCompleteList() {
0441: String query = (String) attributes.get("query");
0442: boolean lowercase = false;
0443: if (query.trim().length() > 0
0444: && Character.isLowerCase(query.trim().charAt(0))) {
0445: lowercase = true;
0446: }
0447: try {
0448: String sql = query;
0449: if (sql.endsWith(";")) {
0450: sql += " ";
0451: }
0452: ScriptReader reader = new ScriptReader(
0453: new StringReader(sql));
0454: reader.setSkipRemarks(true);
0455: String lastSql = "";
0456: while (true) {
0457: String n = reader.readStatement();
0458: if (n == null) {
0459: break;
0460: }
0461: lastSql = n;
0462: }
0463: String result = "";
0464: if (reader.isInsideRemark()) {
0465: if (reader.isBlockRemark()) {
0466: result = "1#(End Remark)# */\n" + result;
0467: } else {
0468: result = "1#(Newline)#\n" + result;
0469: }
0470: } else {
0471: sql = lastSql == null ? "" : lastSql;
0472: while (sql.length() > 0 && sql.charAt(0) <= ' ') {
0473: sql = sql.substring(1);
0474: }
0475: if (sql.trim().length() > 0
0476: && Character.isLowerCase(sql.trim().charAt(0))) {
0477: lowercase = true;
0478: }
0479: Bnf bnf = session.getBnf();
0480: if (bnf == null) {
0481: return "autoCompleteList.jsp";
0482: }
0483: HashMap map = bnf.getNextTokenList(sql);
0484: String space = "";
0485: if (sql.length() > 0) {
0486: char last = sql.charAt(sql.length() - 1);
0487: if (!Character.isWhitespace(last)
0488: && (last != '.' && last >= ' '
0489: && last != '\'' && last != '"')) {
0490: space = " ";
0491: }
0492: }
0493: ArrayList list = new ArrayList(map.size());
0494: Iterator it = map.entrySet().iterator();
0495: while (it.hasNext()) {
0496: Map.Entry entry = (Entry) it.next();
0497: String key = (String) entry.getKey();
0498: String type = "" + key.charAt(0);
0499: String value = (String) entry.getValue();
0500: key = key.substring(2);
0501: if (Character.isLetter(key.charAt(0)) && lowercase) {
0502: key = StringUtils.toLowerEnglish(key);
0503: value = StringUtils.toLowerEnglish(value);
0504: }
0505: if (key.equals(value) && !".".equals(value)) {
0506: value = space + value;
0507: }
0508: key = StringUtils.urlEncode(key);
0509: key = StringUtils.replaceAll(key, "+", " ");
0510: value = StringUtils.urlEncode(value);
0511: value = StringUtils.replaceAll(value, "+", " ");
0512: list.add(type + "#" + key + "#" + value);
0513: }
0514: Collections.sort(list);
0515: StringBuffer buff = new StringBuffer();
0516: if (query.endsWith("\n") || query.trim().endsWith(";")) {
0517: list.add(0, "1#(Newline)#\n");
0518: }
0519: for (int i = 0; i < list.size(); i++) {
0520: if (i > 0) {
0521: buff.append('|');
0522: }
0523: buff.append((String) list.get(i));
0524: }
0525: result = buff.toString();
0526: }
0527: session.put("autoCompleteList", result);
0528: } catch (Throwable e) {
0529: e.printStackTrace();
0530: }
0531: return "autoCompleteList.jsp";
0532: }
0533:
0534: private String admin() {
0535: session.put("port", "" + server.getPort());
0536: session.put("allowOthers", "" + server.getAllowOthers());
0537: session.put("ssl", String.valueOf(server.getSSL()));
0538: session.put("sessions", server.getSessions());
0539: return "admin.jsp";
0540: }
0541:
0542: private String adminSave() {
0543: try {
0544: server.setPort(MathUtils.decodeInt((String) attributes
0545: .get("port")));
0546: server.setAllowOthers(Boolean.valueOf(
0547: (String) attributes.get("allowOthers"))
0548: .booleanValue());
0549: server.setSSL(Boolean.valueOf(
0550: (String) attributes.get("ssl")).booleanValue());
0551: server.saveSettings();
0552: } catch (Exception e) {
0553: server.trace(e.toString());
0554: }
0555: return admin();
0556: }
0557:
0558: private String adminShutdown() {
0559: server.shutdown();
0560: return "admin.jsp";
0561: }
0562:
0563: private String index() {
0564: String[][] languageArray = server.getLanguageArray();
0565: String language = (String) attributes.get("language");
0566: Locale locale = session.locale;
0567: if (language != null) {
0568: if (locale == null
0569: || !StringUtils
0570: .toLowerEnglish(locale.getLanguage())
0571: .equals(language)) {
0572: locale = new Locale(language, "");
0573: server.readTranslations(session, locale.getLanguage());
0574: session.put("language", language);
0575: session.locale = locale;
0576: }
0577: } else {
0578: language = (String) session.get("language");
0579: }
0580: session.put("languageCombo", getComboBox(languageArray,
0581: language));
0582: String[] settingNames = server.getSettingNames();
0583: String setting = attributes.getProperty("setting");
0584: if (setting == null && settingNames.length > 0) {
0585: setting = settingNames[0];
0586: }
0587: String combobox = getComboBox(settingNames, setting);
0588: session.put("settingsList", combobox);
0589: ConnectionInfo info = server.getSetting(setting);
0590: if (info == null) {
0591: info = new ConnectionInfo();
0592: }
0593: session.put("setting", PageParser.escapeHtml(setting));
0594: session.put("name", PageParser.escapeHtml(setting));
0595: session.put("driver", PageParser.escapeHtml(info.driver));
0596: session.put("url", PageParser.escapeHtml(info.url));
0597: session.put("user", PageParser.escapeHtml(info.user));
0598: return "index.jsp";
0599: }
0600:
0601: private String getHistory() {
0602: int id = Integer.parseInt(attributes.getProperty("id"));
0603: String sql = session.getCommand(id);
0604: session.put("query", PageParser.escapeHtmlData(sql));
0605: return "query.jsp";
0606: }
0607:
0608: private int addColumns(DbTableOrView table, StringBuffer buff,
0609: int treeIndex, boolean showColumnTypes,
0610: StringBuffer columnsBuffer) throws SQLException {
0611: DbColumn[] columns = table.columns;
0612: for (int i = 0; columns != null && i < columns.length; i++) {
0613: DbColumn column = columns[i];
0614: if (columnsBuffer.length() > 0) {
0615: columnsBuffer.append(' ');
0616: }
0617: columnsBuffer.append(column.name);
0618: String col = StringUtils.urlEncode(PageParser
0619: .escapeJavaScript(column.name));
0620: buff.append("setNode(" + treeIndex + ", 1, 1, 'column', '"
0621: + PageParser.escapeJavaScript(column.name)
0622: + "', 'javascript:ins(\\'" + col + "\\')');\n");
0623: treeIndex++;
0624: if (showColumnTypes) {
0625: buff.append("setNode(" + treeIndex
0626: + ", 2, 2, 'type', '"
0627: + PageParser.escapeJavaScript(column.dataType)
0628: + "', null);\n");
0629: treeIndex++;
0630: }
0631: }
0632: return treeIndex;
0633: }
0634:
0635: private static class IndexInfo {
0636: String name;
0637: String type;
0638: String columns;
0639: }
0640:
0641: private int addIndexes(DatabaseMetaData meta, String table,
0642: String schema, StringBuffer buff, int treeIndex)
0643: throws SQLException {
0644: // index reading is very slow for oracle (2 seconds per index), so don't
0645: // do it
0646: ResultSet rs = meta.getIndexInfo(null, schema, table, false,
0647: false);
0648: HashMap indexMap = new HashMap();
0649: while (rs.next()) {
0650: String name = rs.getString("INDEX_NAME");
0651: IndexInfo info = (IndexInfo) indexMap.get(name);
0652: if (info == null) {
0653: int t = rs.getInt("TYPE");
0654: String type;
0655: if (t == DatabaseMetaData.tableIndexClustered) {
0656: type = "";
0657: } else if (t == DatabaseMetaData.tableIndexHashed) {
0658: type = " (${text.tree.hashed})";
0659: } else if (t == DatabaseMetaData.tableIndexOther) {
0660: type = "";
0661: } else {
0662: type = null;
0663: }
0664: if (name != null && type != null) {
0665: info = new IndexInfo();
0666: info.name = name;
0667: type = (rs.getBoolean("NON_UNIQUE") ? "${text.tree.nonUnique}"
0668: : "${text.tree.unique}")
0669: + type;
0670: info.type = type;
0671: info.columns = rs.getString("COLUMN_NAME");
0672: indexMap.put(name, info);
0673: }
0674: } else {
0675: info.columns += ", " + rs.getString("COLUMN_NAME");
0676: }
0677: }
0678: rs.close();
0679: if (indexMap.size() > 0) {
0680: buff
0681: .append("setNode("
0682: + treeIndex
0683: + ", 1, 1, 'index_az', '${text.tree.indexes}', null);\n");
0684: treeIndex++;
0685: for (Iterator it = indexMap.values().iterator(); it
0686: .hasNext();) {
0687: IndexInfo info = (IndexInfo) it.next();
0688: buff.append("setNode(" + treeIndex
0689: + ", 2, 1, 'index', '"
0690: + PageParser.escapeJavaScript(info.name)
0691: + "', null);\n");
0692: treeIndex++;
0693: buff.append("setNode(" + treeIndex
0694: + ", 3, 2, 'type', '" + info.type
0695: + "', null);\n");
0696: treeIndex++;
0697: buff.append("setNode(" + treeIndex
0698: + ", 3, 2, 'type', '"
0699: + PageParser.escapeJavaScript(info.columns)
0700: + "', null);\n");
0701: treeIndex++;
0702: }
0703: }
0704: return treeIndex;
0705: }
0706:
0707: private int addTablesAndViews(DbSchema schema, boolean mainSchema,
0708: StringBuffer buff, int treeIndex) throws SQLException {
0709: if (schema == null) {
0710: return treeIndex;
0711: }
0712: Connection conn = session.getConnection();
0713: DatabaseMetaData meta = session.getMetaData();
0714: int level = mainSchema ? 0 : 1;
0715: String indentation = ", " + level + ", " + (level + 1) + ", ";
0716: String indentNode = ", " + (level + 1) + ", " + (level + 1)
0717: + ", ";
0718: DbTableOrView[] tables = schema.tables;
0719: if (tables == null) {
0720: return treeIndex;
0721: }
0722: boolean isOracle = schema.contents.isOracle;
0723: boolean notManyTables = tables.length < 100;
0724: for (int i = 0; i < tables.length; i++) {
0725: DbTableOrView table = tables[i];
0726: if (table.isView) {
0727: continue;
0728: }
0729: int tableId = treeIndex;
0730: String tab = table.quotedName;
0731: if (!mainSchema) {
0732: tab = schema.quotedName + "." + tab;
0733: }
0734: tab = StringUtils.urlEncode(PageParser
0735: .escapeJavaScript(tab));
0736: buff
0737: .append("setNode(" + treeIndex + indentation
0738: + " 'table', '"
0739: + PageParser.escapeJavaScript(table.name)
0740: + "', 'javascript:ins(\\'" + tab
0741: + "\\',true)');\n");
0742: treeIndex++;
0743: if (mainSchema) {
0744: StringBuffer columnsBuffer = new StringBuffer();
0745: treeIndex = addColumns(table, buff, treeIndex,
0746: notManyTables, columnsBuffer);
0747: if (!isOracle && notManyTables) {
0748: treeIndex = addIndexes(meta, table.name,
0749: schema.name, buff, treeIndex);
0750: }
0751: buff
0752: .append("addTable('"
0753: + PageParser
0754: .escapeJavaScript(table.name)
0755: + "', '"
0756: + PageParser
0757: .escapeJavaScript(columnsBuffer
0758: .toString()) + "', "
0759: + tableId + ");\n");
0760: }
0761: }
0762: tables = schema.tables;
0763: for (int i = 0; i < tables.length; i++) {
0764: DbTableOrView view = tables[i];
0765: if (!view.isView) {
0766: continue;
0767: }
0768: int tableId = treeIndex;
0769: String tab = view.quotedName;
0770: if (!mainSchema) {
0771: tab = view.schema.quotedName + "." + tab;
0772: }
0773: tab = StringUtils.urlEncode(PageParser
0774: .escapeJavaScript(tab));
0775: buff
0776: .append("setNode(" + treeIndex + indentation
0777: + " 'view', '"
0778: + PageParser.escapeJavaScript(view.name)
0779: + "', 'javascript:ins(\\'" + tab
0780: + "\\',true)');\n");
0781: treeIndex++;
0782: if (mainSchema) {
0783: StringBuffer columnsBuffer = new StringBuffer();
0784: treeIndex = addColumns(view, buff, treeIndex,
0785: notManyTables, columnsBuffer);
0786: if (schema.contents.isH2) {
0787: PreparedStatement prep = null;
0788: try {
0789: prep = conn
0790: .prepareStatement("SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=?");
0791: prep.setString(1, view.name);
0792: ResultSet rs = prep.executeQuery();
0793: if (rs.next()) {
0794: String sql = rs.getString("SQL");
0795: buff.append("setNode(" + treeIndex
0796: + indentNode + " 'type', '"
0797: + PageParser.escapeJavaScript(sql)
0798: + "', null);\n");
0799: treeIndex++;
0800: }
0801: rs.close();
0802: } finally {
0803: JdbcUtils.closeSilently(prep);
0804: }
0805: }
0806: buff
0807: .append("addTable('"
0808: + PageParser
0809: .escapeJavaScript(view.name)
0810: + "', '"
0811: + PageParser
0812: .escapeJavaScript(columnsBuffer
0813: .toString()) + "', "
0814: + tableId + ");\n");
0815: }
0816: }
0817: return treeIndex;
0818: }
0819:
0820: private String tables() {
0821: DbContents contents = session.getContents();
0822: boolean isH2 = false;
0823: try {
0824: contents.readContents(session.getMetaData());
0825: session.loadBnf();
0826: Connection conn = session.getConnection();
0827: DatabaseMetaData meta = session.getMetaData();
0828: isH2 = contents.isH2;
0829:
0830: StringBuffer buff = new StringBuffer();
0831: buff.append("setNode(0, 0, 0, 'database', '"
0832: + PageParser.escapeJavaScript((String) session
0833: .get("url")) + "', null);\n");
0834: int treeIndex = 1;
0835:
0836: DbSchema defaultSchema = contents.defaultSchema;
0837: treeIndex = addTablesAndViews(defaultSchema, true, buff,
0838: treeIndex);
0839: DbSchema[] schemas = contents.schemas;
0840: for (int i = 0; i < schemas.length; i++) {
0841: DbSchema schema = schemas[i];
0842: if (schema == defaultSchema || schema == null) {
0843: continue;
0844: }
0845: buff.append("setNode(" + treeIndex
0846: + ", 0, 1, 'folder', '"
0847: + PageParser.escapeJavaScript(schema.name)
0848: + "', null);\n");
0849: treeIndex++;
0850: treeIndex = addTablesAndViews(schema, false, buff,
0851: treeIndex);
0852: }
0853: if (isH2) {
0854: Statement stat = null;
0855: try {
0856: stat = conn.createStatement();
0857: ResultSet rs = stat
0858: .executeQuery("SELECT * FROM INFORMATION_SCHEMA.SEQUENCES ORDER BY SEQUENCE_NAME");
0859: for (int i = 0; rs.next(); i++) {
0860: if (i == 0) {
0861: buff
0862: .append("setNode("
0863: + treeIndex
0864: + ", 0, 1, 'sequences', '${text.tree.sequences}', null);\n");
0865: treeIndex++;
0866: }
0867: String name = rs.getString("SEQUENCE_NAME");
0868: String current = rs.getString("CURRENT_VALUE");
0869: String increment = rs.getString("INCREMENT");
0870: buff.append("setNode(" + treeIndex
0871: + ", 1, 1, 'sequence', '"
0872: + PageParser.escapeJavaScript(name)
0873: + "', null);\n");
0874: treeIndex++;
0875: buff
0876: .append("setNode("
0877: + treeIndex
0878: + ", 2, 2, 'type', '${text.tree.current}: "
0879: + PageParser
0880: .escapeJavaScript(current)
0881: + "', null);\n");
0882: treeIndex++;
0883: if (!"1".equals(increment)) {
0884: buff
0885: .append("setNode("
0886: + treeIndex
0887: + ", 2, 2, 'type', '${text.tree.increment}: "
0888: + PageParser
0889: .escapeJavaScript(increment)
0890: + "', null);\n");
0891: treeIndex++;
0892: }
0893: }
0894: rs.close();
0895: rs = stat
0896: .executeQuery("SELECT * FROM INFORMATION_SCHEMA.USERS ORDER BY NAME");
0897: for (int i = 0; rs.next(); i++) {
0898: if (i == 0) {
0899: buff
0900: .append("setNode("
0901: + treeIndex
0902: + ", 0, 1, 'users', '${text.tree.users}', null);\n");
0903: treeIndex++;
0904: }
0905: String name = rs.getString("NAME");
0906: String admin = rs.getString("ADMIN");
0907: buff.append("setNode(" + treeIndex
0908: + ", 1, 1, 'user', '"
0909: + PageParser.escapeJavaScript(name)
0910: + "', null);\n");
0911: treeIndex++;
0912: if (admin.equalsIgnoreCase("TRUE")) {
0913: buff
0914: .append("setNode("
0915: + treeIndex
0916: + ", 2, 2, 'type', '${text.tree.admin}', null);\n");
0917: treeIndex++;
0918: }
0919: }
0920: rs.close();
0921: } finally {
0922: JdbcUtils.closeSilently(stat);
0923: }
0924: }
0925: String version = meta.getDatabaseProductName() + " "
0926: + meta.getDatabaseProductVersion();
0927: buff.append("setNode(" + treeIndex + ", 0, 0, 'info', '"
0928: + PageParser.escapeJavaScript(version)
0929: + "', null);\n");
0930: buff.append("refreshQueryTables();");
0931: session.put("tree", buff.toString());
0932: } catch (Exception e) {
0933: session.put("tree", "");
0934: session.put("error", getStackTrace(0, e, isH2));
0935: }
0936: return "tables.jsp";
0937: }
0938:
0939: private String getStackTrace(int id, Throwable e, boolean isH2) {
0940: try {
0941: StringWriter writer = new StringWriter();
0942: e.printStackTrace(new PrintWriter(writer));
0943: String stackTrace = writer.toString();
0944: stackTrace = PageParser.escapeHtml(stackTrace);
0945: if (isH2) {
0946: stackTrace = linkToSource(stackTrace);
0947: }
0948: stackTrace = StringUtils.replaceAll(stackTrace, "\t",
0949: " ");
0950: String message = PageParser.escapeHtml(e.getMessage());
0951: String error = "<a class=\"error\" href=\"#\" onclick=\"var x=document.getElementById('st"
0952: + id
0953: + "').style;x.display=x.display==''?'none':'';\">"
0954: + message + "</a>";
0955: if (e instanceof SQLException) {
0956: SQLException se = (SQLException) e;
0957: error += " " + se.getSQLState() + "/"
0958: + se.getErrorCode();
0959: if (isH2) {
0960: int code = se.getErrorCode();
0961: error += " <a href=\"http://h2database.com/javadoc/org/h2/constant/ErrorCode.html#c"
0962: + code + "\">(${text.a.help})</a>";
0963: }
0964: }
0965: error += "<span style=\"display: none;\" id=\"st" + id
0966: + "\"><br />" + stackTrace + "</span>";
0967: error = formatAsError(error);
0968: return error;
0969: } catch (OutOfMemoryError e2) {
0970: e.printStackTrace();
0971: return e.toString();
0972: }
0973: }
0974:
0975: private String linkToSource(String s) {
0976: try {
0977: StringBuffer result = new StringBuffer(s.length());
0978: int idx = s.indexOf("<br />");
0979: result.append(s.substring(0, idx));
0980: while (true) {
0981: int start = s.indexOf("org.h2.", idx);
0982: if (start < 0) {
0983: result.append(s.substring(idx));
0984: break;
0985: }
0986: result.append(s.substring(idx, start));
0987: int end = s.indexOf(')', start);
0988: if (end < 0) {
0989: result.append(s.substring(idx));
0990: break;
0991: }
0992: String element = s.substring(start, end);
0993: int open = element.lastIndexOf('(');
0994: int dotMethod = element.lastIndexOf('.', open - 1);
0995: int dotClass = element.lastIndexOf('.', dotMethod - 1);
0996: String packageName = element.substring(0, dotClass);
0997: int colon = element.lastIndexOf(':');
0998: String file = element.substring(open + 1, colon);
0999: String lineNumber = element.substring(colon + 1,
1000: element.length());
1001: String fullFileName = packageName.replace('.', '/')
1002: + "/" + file;
1003: result
1004: .append("<a href=\"http://h2database.com/html/source.html?file=");
1005: result.append(fullFileName);
1006: result.append("&line=");
1007: result.append(lineNumber);
1008: result.append("&build=");
1009: result.append(Constants.BUILD_ID);
1010: result.append("\">");
1011: result.append(element);
1012: result.append("</a>");
1013: idx = end;
1014: }
1015: return result.toString();
1016: } catch (Throwable t) {
1017: return s;
1018: }
1019: }
1020:
1021: private String formatAsError(String s) {
1022: return "<div class=\"error\">" + s + "</div>";
1023: }
1024:
1025: private String test() {
1026: String driver = attributes.getProperty("driver", "");
1027: String url = attributes.getProperty("url", "");
1028: String user = attributes.getProperty("user", "");
1029: String password = attributes.getProperty("password", "");
1030: session.put("driver", driver);
1031: session.put("url", url);
1032: session.put("user", user);
1033: boolean isH2 = url.startsWith("jdbc:h2:");
1034: try {
1035: Connection conn = server.getConnection(driver, url, user,
1036: password, this );
1037: JdbcUtils.closeSilently(conn);
1038: session.put("error", "${text.login.testSuccessful}");
1039: return "login.jsp";
1040: } catch (Exception e) {
1041: session.put("error", getLoginError(e, isH2));
1042: return "login.jsp";
1043: }
1044: }
1045:
1046: private String getLoginError(Exception e, boolean isH2) {
1047: if (e instanceof JdbcSQLException
1048: && ((JdbcSQLException) e).getErrorCode() == ErrorCode.CLASS_NOT_FOUND_1) {
1049: return "${text.login.driverNotFound}<br />"
1050: + getStackTrace(0, e, isH2);
1051: } else {
1052: return getStackTrace(0, e, isH2);
1053: }
1054: }
1055:
1056: private String login() {
1057: final String driver = attributes.getProperty("driver", "");
1058: final String url = attributes.getProperty("url", "");
1059: final String user = attributes.getProperty("user", "");
1060: final String password = attributes.getProperty("password", "");
1061: session.put("autoCommit", "checked");
1062: session.put("autoComplete", "1");
1063: session.put("maxrows", "1000");
1064: boolean thread = false;
1065: if (socket != null && url.startsWith("jdbc:h2:")
1066: && !url.startsWith("jdbc:h2:tcp:")
1067: && !url.startsWith("jdbc:h2:ssl:")
1068: && !url.startsWith("jdbc:h2:mem:")) {
1069: thread = true;
1070: }
1071: if (!thread) {
1072: boolean isH2 = url.startsWith("jdbc:h2:");
1073: try {
1074: Connection conn = server.getConnection(driver, url,
1075: user, password, this );
1076: session.setConnection(conn);
1077: session.put("url", url);
1078: session.put("user", user);
1079: session.remove("error");
1080: settingSave();
1081: return "frame.jsp";
1082: } catch (Exception e) {
1083: session.put("error", getLoginError(e, isH2));
1084: return "login.jsp";
1085: }
1086: }
1087: class LoginTask implements Runnable, DatabaseEventListener {
1088: private PrintWriter writer;
1089: private SimpleDateFormat dateFormat = new SimpleDateFormat(
1090: "HH:mm:ss.SSS");
1091:
1092: LoginTask() throws IOException {
1093: String message = "HTTP/1.1 200 OK\n";
1094: message += "Content-Type: " + mimeType + "\n\n";
1095: output.write(message.getBytes());
1096: writer = new PrintWriter(output);
1097: writer
1098: .println("<html><head><link rel=\"stylesheet\" type=\"text/css\" href=\"stylesheet.css\" /></head>");
1099: writer.println("<body><h2>Opening Database</h2>URL: "
1100: + PageParser.escapeHtml(url) + "<br />");
1101: writer.println("User: " + PageParser.escapeHtml(user)
1102: + "<br />");
1103: writer.println("Version: " + Constants.getVersion()
1104: + "<br /><br />");
1105: writer.flush();
1106: log("Start...");
1107: }
1108:
1109: public void closingDatabase() {
1110: log("Closing database");
1111: }
1112:
1113: public void diskSpaceIsLow(long stillAvailable)
1114: throws SQLException {
1115: log("Disk space is low; still available: "
1116: + stillAvailable);
1117: }
1118:
1119: public void exceptionThrown(SQLException e, String sql) {
1120: log("Exception: " + PageParser.escapeHtml(e.toString())
1121: + " SQL: " + PageParser.escapeHtml(sql));
1122: server.traceError(e);
1123: }
1124:
1125: public void init(String url) {
1126: log("Init: " + PageParser.escapeHtml(url));
1127: }
1128:
1129: public void opened() {
1130: log("Database was opened");
1131: }
1132:
1133: public void setProgress(int state, String name, int x,
1134: int max) {
1135: name = PageParser.escapeHtml(name);
1136: if (state == listenerLastState) {
1137: long time = System.currentTimeMillis();
1138: if (listenerLastEvent + 500 < time) {
1139: return;
1140: }
1141: listenerLastEvent = time;
1142: } else {
1143: listenerLastState = state;
1144: }
1145: switch (state) {
1146: case DatabaseEventListener.STATE_BACKUP_FILE:
1147: log("Backing up " + name + " " + (100L * x / max)
1148: + "%");
1149: break;
1150: case DatabaseEventListener.STATE_CREATE_INDEX:
1151: log("Creating index " + name + " "
1152: + (100L * x / max) + "%");
1153: break;
1154: case DatabaseEventListener.STATE_RECOVER:
1155: log("Recovering " + name + " " + (100L * x / max)
1156: + "%");
1157: break;
1158: case DatabaseEventListener.STATE_SCAN_FILE:
1159: log("Scanning file " + name + " "
1160: + (100L * x / max) + "%");
1161: break;
1162: default:
1163: log("Unknown state: " + state);
1164: }
1165: }
1166:
1167: private synchronized void log(String message) {
1168: if (output != null) {
1169: message = dateFormat.format(new Date()) + ": "
1170: + message;
1171: writer.println(message + "<br />");
1172: writer.flush();
1173: }
1174: server.trace(message);
1175: }
1176:
1177: public void run() {
1178: String sessionId = (String) session.get("sessionId");
1179: boolean isH2 = url.startsWith("jdbc:h2:");
1180: try {
1181: Connection conn = server.getConnection(driver, url,
1182: user, password, this );
1183: session.setConnection(conn);
1184: session.put("url", url);
1185: session.put("user", user);
1186: session.remove("error");
1187: settingSave();
1188: log("OK<script type=\"text/javascript\">top.location=\"frame.jsp?jsessionid="
1189: + sessionId + "\"</script></body></htm>");
1190: // return "frame.jsp";
1191: } catch (Exception e) {
1192: session.put("error", getLoginError(e, isH2));
1193: log("Error<script type=\"text/javascript\">top.location=\"index.jsp?jsessionid="
1194: + sessionId + "\"</script></body></html>");
1195: // return "index.jsp";
1196: }
1197: synchronized (this ) {
1198: IOUtils.closeSilently(output);
1199: try {
1200: socket.close();
1201: } catch (IOException e) {
1202: // ignore
1203: }
1204: output = null;
1205: }
1206: }
1207: }
1208: try {
1209: LoginTask login = new LoginTask();
1210: Thread t = new Thread(login);
1211: t.start();
1212: } catch (IOException e) {
1213: // ignore
1214: }
1215: return "";
1216: }
1217:
1218: private String logout() {
1219: try {
1220: Connection conn = session.getConnection();
1221: session.setConnection(null);
1222: session.remove("conn");
1223: session.remove("result");
1224: session.remove("tables");
1225: session.remove("user");
1226: if (conn != null) {
1227: conn.close();
1228: }
1229: } catch (Exception e) {
1230: server.trace(e.toString());
1231: }
1232: return "index.do";
1233: }
1234:
1235: private String query() {
1236: String sql = attributes.getProperty("sql").trim();
1237: try {
1238: Connection conn = session.getConnection();
1239: String result;
1240: if (sql.startsWith("@JAVA")) {
1241: if (server.getAllowScript()) {
1242: try {
1243: result = executeJava(sql.substring("@JAVA"
1244: .length()));
1245: } catch (Throwable t) {
1246: result = getStackTrace(0, t, false);
1247: }
1248: } else {
1249: result = "Executing Java code is not allowed, use command line parameters -webScript true";
1250: }
1251: } else if ("@AUTOCOMMIT TRUE".equals(sql)) {
1252: conn.setAutoCommit(true);
1253: result = "${text.result.autoCommitOn}";
1254: } else if ("@AUTOCOMMIT FALSE".equals(sql)) {
1255: conn.setAutoCommit(false);
1256: result = "${text.result.autoCommitOff}";
1257: } else if (sql.startsWith("@TRANSACTION_ISOLATION")) {
1258: String s = sql.substring(
1259: "@TRANSACTION_ISOLATION".length()).trim();
1260: if (s.length() > 0) {
1261: int level = Integer.parseInt(s);
1262: conn.setTransactionIsolation(level);
1263: }
1264: result = "Transaction Isolation: "
1265: + conn.getTransactionIsolation() + "<br />";
1266: result += Connection.TRANSACTION_READ_UNCOMMITTED
1267: + ": READ_UNCOMMITTED<br />";
1268: result += Connection.TRANSACTION_READ_COMMITTED
1269: + ": READ_COMMITTED<br />";
1270: result += Connection.TRANSACTION_REPEATABLE_READ
1271: + ": REPEATABLE_READ<br />";
1272: result += Connection.TRANSACTION_SERIALIZABLE
1273: + ": SERIALIZABLE";
1274: } else if (sql.startsWith("@SET MAXROWS ")) {
1275: int maxrows = Integer.parseInt(sql
1276: .substring("@SET MAXROWS ".length()));
1277: session.put("maxrows", "" + maxrows);
1278: result = "${text.result.maxrowsSet}";
1279: } else {
1280: ScriptReader r = new ScriptReader(new StringReader(sql));
1281: ObjectArray list = new ObjectArray();
1282: while (true) {
1283: String s = r.readStatement();
1284: if (s == null) {
1285: break;
1286: }
1287: list.add(s);
1288: }
1289: StringBuffer buff = new StringBuffer();
1290: for (int i = 0; i < list.size(); i++) {
1291: String s = (String) list.get(i);
1292: if (!s.startsWith("@")) {
1293: buff.append(PageParser.escapeHtml(s + ";"));
1294: buff.append("<br />");
1295: }
1296: buff.append(getResult(conn, i + 1, s,
1297: list.size() == 1, false));
1298: buff.append("<br />");
1299: }
1300: result = buff.toString();
1301: }
1302: session.put("result", result);
1303: } catch (Throwable e) {
1304: session.put("result", getStackTrace(0, e, session
1305: .getContents().isH2));
1306: }
1307: return "result.jsp";
1308: }
1309:
1310: static class DynamicClassLoader extends SecureClassLoader {
1311:
1312: private String name;
1313: private byte[] data;
1314: private Class clazz;
1315:
1316: DynamicClassLoader(String name, byte[] data)
1317: throws MalformedURLException {
1318: super (DynamicClassLoader.class.getClassLoader());
1319: this .name = name;
1320: this .data = data;
1321: }
1322:
1323: public Class loadClass(String className)
1324: throws ClassNotFoundException {
1325: return findClass(className);
1326: }
1327:
1328: public Class findClass(String className)
1329: throws ClassNotFoundException {
1330: if (className.equals(name)) {
1331: if (clazz == null) {
1332: clazz = defineClass(className, data, 0, data.length);
1333: }
1334: return clazz;
1335: }
1336: try {
1337: return findSystemClass(className);
1338: } catch (Exception e) {
1339: }
1340: return super .findClass(className);
1341: }
1342: }
1343:
1344: private String executeJava(String code) throws Exception {
1345: File javaFile = new File("Java.java");
1346: File classFile = new File("Java.class");
1347: try {
1348: PrintWriter out = new PrintWriter(new FileWriter(javaFile));
1349: classFile.delete();
1350: int endImport = code.indexOf("@CODE");
1351: String importCode = "import java.util.*; import java.math.*; import java.sql.*;";
1352: if (endImport >= 0) {
1353: importCode = code.substring(0, endImport);
1354: code = code.substring("@CODE".length() + endImport);
1355: }
1356: out.println(importCode);
1357: out
1358: .println("public class Java { public static Object run() throws Throwable {"
1359: + code + "}}");
1360: out.close();
1361: Process p = Runtime.getRuntime().exec("javac Java.java");
1362: InputStream processIn = p.getInputStream();
1363: InputStream processErrorIn = p.getErrorStream();
1364: StringBuffer buff = new StringBuffer();
1365: while (true) {
1366: int c = processIn.read();
1367: if (c == -1) {
1368: break;
1369: }
1370: buff.append((char) c);
1371: }
1372: while (true) {
1373: int c = processErrorIn.read();
1374: if (c == -1) {
1375: break;
1376: }
1377: buff.append((char) c);
1378: }
1379: String error = buff.toString().trim();
1380: if (error.length() > 0) {
1381: throw new Exception("Error compiling: " + error);
1382: }
1383: byte[] data = new byte[(int) classFile.length()];
1384: DataInputStream in = new DataInputStream(
1385: new FileInputStream(classFile));
1386: in.readFully(data);
1387: in.close();
1388: DynamicClassLoader cl = new DynamicClassLoader("Java", data);
1389: Class clazz = cl.loadClass("Java");
1390: Method[] methods = clazz.getMethods();
1391: for (int i = 0; i < methods.length; i++) {
1392: Method m = methods[i];
1393: if (m.getName().equals("run")) {
1394: return "" + m.invoke(null, new Object[0]);
1395: }
1396: }
1397: return null;
1398: } finally {
1399: javaFile.delete();
1400: classFile.delete();
1401: }
1402: }
1403:
1404: private String editResult() {
1405: ResultSet rs = session.result;
1406: int row = Integer.parseInt(attributes.getProperty("row"));
1407: int op = Integer.parseInt(attributes.getProperty("op"));
1408: String result = "", error = "";
1409: try {
1410: if (op == 1) {
1411: boolean insert = row < 0;
1412: if (insert) {
1413: rs.moveToInsertRow();
1414: } else {
1415: rs.absolute(row);
1416: }
1417: for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
1418: String x = attributes.getProperty("r" + row + "c"
1419: + (i + 1));
1420: rs.updateString(i + 1, unescapeData(x));
1421: }
1422: if (insert) {
1423: rs.insertRow();
1424: } else {
1425: rs.updateRow();
1426: }
1427: } else if (op == 2) {
1428: rs.absolute(row);
1429: rs.deleteRow();
1430: } else if (op == 3) {
1431: // cancel
1432: }
1433: } catch (Throwable e) {
1434: result = "<br />"
1435: + getStackTrace(0, e, session.getContents().isH2);
1436: error = formatAsError(e.getMessage());
1437: }
1438: String sql = "@EDIT " + (String) session.get("resultSetSQL");
1439: Connection conn = session.getConnection();
1440: result = error + getResult(conn, -1, sql, true, true) + result;
1441: session.put("result", result);
1442: return "result.jsp";
1443: }
1444:
1445: private ResultSet getMetaResultSet(Connection conn, String sql)
1446: throws SQLException {
1447: DatabaseMetaData meta = conn.getMetaData();
1448: if (sql.startsWith("@TABLES")) {
1449: String[] p = split(sql);
1450: String[] types = p[4] == null ? null : StringUtils
1451: .arraySplit(p[4], ',', false);
1452: return meta.getTables(p[1], p[2], p[3], types);
1453: } else if (sql.startsWith("@COLUMNS")) {
1454: String[] p = split(sql);
1455: return meta.getColumns(p[1], p[2], p[3], p[4]);
1456: } else if (sql.startsWith("@INDEX_INFO")) {
1457: String[] p = split(sql);
1458: boolean unique = p[4] == null ? false : Boolean.valueOf(
1459: p[4]).booleanValue();
1460: boolean approx = p[5] == null ? false : Boolean.valueOf(
1461: p[5]).booleanValue();
1462: return meta.getIndexInfo(p[1], p[2], p[3], unique, approx);
1463: } else if (sql.startsWith("@PRIMARY_KEYS")) {
1464: String[] p = split(sql);
1465: return meta.getPrimaryKeys(p[1], p[2], p[3]);
1466: } else if (sql.startsWith("@PROCEDURES")) {
1467: String[] p = split(sql);
1468: return meta.getProcedures(p[1], p[2], p[3]);
1469: } else if (sql.startsWith("@PROCEDURE_COLUMNS")) {
1470: String[] p = split(sql);
1471: return meta.getProcedureColumns(p[1], p[2], p[3], p[4]);
1472: } else if (sql.startsWith("@SCHEMAS")) {
1473: return meta.getSchemas();
1474: } else if (sql.startsWith("@CATALOG")) {
1475: SimpleResultSet rs = new SimpleResultSet();
1476: rs.addColumn("CATALOG", Types.VARCHAR, 0, 0);
1477: rs.addRow(new String[] { conn.getCatalog() });
1478: return rs;
1479: } else if (sql.startsWith("@MEMORY")) {
1480: SimpleResultSet rs = new SimpleResultSet();
1481: rs.addColumn("Type", Types.VARCHAR, 0, 0);
1482: rs.addColumn("Value", Types.VARCHAR, 0, 0);
1483: rs.addRow(new String[] { "Used Memory",
1484: "" + MemoryUtils.getMemoryUsed() });
1485: rs.addRow(new String[] { "Free Memory",
1486: "" + MemoryUtils.getMemoryFree() });
1487: return rs;
1488: } else if (sql.startsWith("@INFO")) {
1489: SimpleResultSet rs = new SimpleResultSet();
1490: rs.addColumn("KEY", Types.VARCHAR, 0, 0);
1491: rs.addColumn("VALUE", Types.VARCHAR, 0, 0);
1492: rs.addRow(new String[] { "conn.getCatalog",
1493: conn.getCatalog() });
1494: rs.addRow(new String[] { "conn.getAutoCommit",
1495: "" + conn.getAutoCommit() });
1496: rs.addRow(new String[] { "conn.getTransactionIsolation",
1497: "" + conn.getTransactionIsolation() });
1498: rs.addRow(new String[] { "conn.getWarnings",
1499: "" + conn.getWarnings() });
1500: String map;
1501: try {
1502: map = "" + conn.getTypeMap();
1503: } catch (SQLException e) {
1504: map = e.toString();
1505: }
1506: rs.addRow(new String[] { "conn.getTypeMap", "" + map });
1507: rs.addRow(new String[] { "conn.isReadOnly",
1508: "" + conn.isReadOnly() });
1509: rs.addRow(new String[] { "meta.getCatalogSeparator",
1510: "" + meta.getCatalogSeparator() });
1511: rs.addRow(new String[] { "meta.getCatalogTerm",
1512: "" + meta.getCatalogTerm() });
1513: rs.addRow(new String[] { "meta.getDatabaseProductName",
1514: "" + meta.getDatabaseProductName() });
1515: rs.addRow(new String[] { "meta.getDatabaseProductVersion",
1516: "" + meta.getDatabaseProductVersion() });
1517: rs.addRow(new String[] {
1518: "meta.getDefaultTransactionIsolation",
1519: "" + meta.getDefaultTransactionIsolation() });
1520: rs.addRow(new String[] { "meta.getDriverMajorVersion",
1521: "" + meta.getDriverMajorVersion() });
1522: rs.addRow(new String[] { "meta.getDriverMinorVersion",
1523: "" + meta.getDriverMinorVersion() });
1524: rs.addRow(new String[] { "meta.getDriverName",
1525: "" + meta.getDriverName() });
1526: rs.addRow(new String[] { "meta.getDriverVersion",
1527: "" + meta.getDriverVersion() });
1528: rs.addRow(new String[] { "meta.getExtraNameCharacters",
1529: "" + meta.getExtraNameCharacters() });
1530: rs.addRow(new String[] { "meta.getIdentifierQuoteString",
1531: "" + meta.getIdentifierQuoteString() });
1532: rs.addRow(new String[] { "meta.getMaxBinaryLiteralLength",
1533: "" + meta.getMaxBinaryLiteralLength() });
1534: rs.addRow(new String[] { "meta.getMaxCatalogNameLength",
1535: "" + meta.getMaxCatalogNameLength() });
1536: rs.addRow(new String[] { "meta.getMaxCharLiteralLength",
1537: "" + meta.getMaxCharLiteralLength() });
1538: rs.addRow(new String[] { "meta.getMaxColumnNameLength",
1539: "" + meta.getMaxColumnNameLength() });
1540: rs.addRow(new String[] { "meta.getMaxColumnsInGroupBy",
1541: "" + meta.getMaxColumnsInGroupBy() });
1542: rs.addRow(new String[] { "meta.getMaxColumnsInIndex",
1543: "" + meta.getMaxColumnsInIndex() });
1544: rs.addRow(new String[] { "meta.getMaxColumnsInOrderBy",
1545: "" + meta.getMaxColumnsInOrderBy() });
1546: rs.addRow(new String[] { "meta.getMaxColumnsInSelect",
1547: "" + meta.getMaxColumnsInSelect() });
1548: rs.addRow(new String[] { "meta.getMaxColumnsInTable",
1549: "" + meta.getMaxColumnsInTable() });
1550: rs.addRow(new String[] { "meta.getMaxConnections",
1551: "" + meta.getMaxConnections() });
1552: rs.addRow(new String[] { "meta.getMaxCursorNameLength",
1553: "" + meta.getMaxCursorNameLength() });
1554: rs.addRow(new String[] { "meta.getMaxIndexLength",
1555: "" + meta.getMaxIndexLength() });
1556: rs.addRow(new String[] { "meta.getMaxProcedureNameLength",
1557: "" + meta.getMaxProcedureNameLength() });
1558: rs.addRow(new String[] { "meta.getMaxRowSize",
1559: "" + meta.getMaxRowSize() });
1560: rs.addRow(new String[] { "meta.getMaxSchemaNameLength",
1561: "" + meta.getMaxSchemaNameLength() });
1562: rs.addRow(new String[] { "meta.getMaxStatementLength",
1563: "" + meta.getMaxStatementLength() });
1564: rs.addRow(new String[] { "meta.getMaxStatements",
1565: "" + meta.getMaxStatements() });
1566: rs.addRow(new String[] { "meta.getMaxTableNameLength",
1567: "" + meta.getMaxTableNameLength() });
1568: rs.addRow(new String[] { "meta.getMaxTablesInSelect",
1569: "" + meta.getMaxTablesInSelect() });
1570: rs.addRow(new String[] { "meta.getMaxUserNameLength",
1571: "" + meta.getMaxUserNameLength() });
1572: rs.addRow(new String[] { "meta.getNumericFunctions",
1573: "" + meta.getNumericFunctions() });
1574: rs.addRow(new String[] { "meta.getProcedureTerm",
1575: "" + meta.getProcedureTerm() });
1576: rs.addRow(new String[] { "meta.getSchemaTerm",
1577: "" + meta.getSchemaTerm() });
1578: rs.addRow(new String[] { "meta.getSearchStringEscape",
1579: "" + meta.getSearchStringEscape() });
1580: rs.addRow(new String[] { "meta.getSQLKeywords",
1581: "" + meta.getSQLKeywords() });
1582: rs.addRow(new String[] { "meta.getStringFunctions",
1583: "" + meta.getStringFunctions() });
1584: rs.addRow(new String[] { "meta.getSystemFunctions",
1585: "" + meta.getSystemFunctions() });
1586: rs.addRow(new String[] { "meta.getTimeDateFunctions",
1587: "" + meta.getTimeDateFunctions() });
1588: rs
1589: .addRow(new String[] { "meta.getURL",
1590: "" + meta.getURL() });
1591: rs.addRow(new String[] { "meta.getUserName",
1592: "" + meta.getUserName() });
1593: rs.addRow(new String[] { "meta.isCatalogAtStart",
1594: "" + meta.isCatalogAtStart() });
1595: rs.addRow(new String[] { "meta.isReadOnly",
1596: "" + meta.isReadOnly() });
1597: rs.addRow(new String[] { "meta.allProceduresAreCallable",
1598: "" + meta.allProceduresAreCallable() });
1599: rs.addRow(new String[] { "meta.allTablesAreSelectable",
1600: "" + meta.allTablesAreSelectable() });
1601: rs
1602: .addRow(new String[] {
1603: "meta.dataDefinitionCausesTransactionCommit",
1604: ""
1605: + meta
1606: .dataDefinitionCausesTransactionCommit() });
1607: rs.addRow(new String[] {
1608: "meta.dataDefinitionIgnoredInTransactions",
1609: "" + meta.dataDefinitionIgnoredInTransactions() });
1610: rs.addRow(new String[] { "meta.doesMaxRowSizeIncludeBlobs",
1611: "" + meta.doesMaxRowSizeIncludeBlobs() });
1612: rs.addRow(new String[] { "meta.nullPlusNonNullIsNull",
1613: "" + meta.nullPlusNonNullIsNull() });
1614: rs.addRow(new String[] { "meta.nullsAreSortedAtEnd",
1615: "" + meta.nullsAreSortedAtEnd() });
1616: rs.addRow(new String[] { "meta.nullsAreSortedAtStart",
1617: "" + meta.nullsAreSortedAtStart() });
1618: rs.addRow(new String[] { "meta.nullsAreSortedHigh",
1619: "" + meta.nullsAreSortedHigh() });
1620: rs.addRow(new String[] { "meta.nullsAreSortedLow",
1621: "" + meta.nullsAreSortedLow() });
1622: rs.addRow(new String[] { "meta.storesLowerCaseIdentifiers",
1623: "" + meta.storesLowerCaseIdentifiers() });
1624: rs.addRow(new String[] {
1625: "meta.storesLowerCaseQuotedIdentifiers",
1626: "" + meta.storesLowerCaseQuotedIdentifiers() });
1627: rs.addRow(new String[] { "meta.storesMixedCaseIdentifiers",
1628: "" + meta.storesMixedCaseIdentifiers() });
1629: rs.addRow(new String[] {
1630: "meta.storesMixedCaseQuotedIdentifiers",
1631: "" + meta.storesMixedCaseQuotedIdentifiers() });
1632: rs.addRow(new String[] { "meta.storesUpperCaseIdentifiers",
1633: "" + meta.storesUpperCaseIdentifiers() });
1634: rs.addRow(new String[] {
1635: "meta.storesUpperCaseQuotedIdentifiers",
1636: "" + meta.storesUpperCaseQuotedIdentifiers() });
1637: rs.addRow(new String[] {
1638: "meta.supportsAlterTableWithAddColumn",
1639: "" + meta.supportsAlterTableWithAddColumn() });
1640: rs.addRow(new String[] {
1641: "meta.supportsAlterTableWithDropColumn",
1642: "" + meta.supportsAlterTableWithDropColumn() });
1643: rs.addRow(new String[] {
1644: "meta.supportsANSI92EntryLevelSQL",
1645: "" + meta.supportsANSI92EntryLevelSQL() });
1646: rs.addRow(new String[] { "meta.supportsANSI92FullSQL",
1647: "" + meta.supportsANSI92FullSQL() });
1648: rs.addRow(new String[] {
1649: "meta.supportsANSI92IntermediateSQL",
1650: "" + meta.supportsANSI92IntermediateSQL() });
1651: rs.addRow(new String[] { "meta.supportsBatchUpdates",
1652: "" + meta.supportsBatchUpdates() });
1653: rs.addRow(new String[] {
1654: "meta.supportsCatalogsInDataManipulation",
1655: "" + meta.supportsCatalogsInDataManipulation() });
1656: rs.addRow(new String[] {
1657: "meta.supportsCatalogsInIndexDefinitions",
1658: "" + meta.supportsCatalogsInIndexDefinitions() });
1659: rs
1660: .addRow(new String[] {
1661: "meta.supportsCatalogsInPrivilegeDefinitions",
1662: ""
1663: + meta
1664: .supportsCatalogsInPrivilegeDefinitions() });
1665: rs.addRow(new String[] {
1666: "meta.supportsCatalogsInProcedureCalls",
1667: "" + meta.supportsCatalogsInProcedureCalls() });
1668: rs.addRow(new String[] {
1669: "meta.supportsCatalogsInTableDefinitions",
1670: "" + meta.supportsCatalogsInTableDefinitions() });
1671: rs.addRow(new String[] { "meta.supportsColumnAliasing",
1672: "" + meta.supportsColumnAliasing() });
1673: rs.addRow(new String[] { "meta.supportsConvert",
1674: "" + meta.supportsConvert() });
1675: rs.addRow(new String[] { "meta.supportsCoreSQLGrammar",
1676: "" + meta.supportsCoreSQLGrammar() });
1677: rs.addRow(new String[] {
1678: "meta.supportsCorrelatedSubqueries",
1679: "" + meta.supportsCorrelatedSubqueries() });
1680: rs
1681: .addRow(new String[] {
1682: "meta.supportsDataDefinitionAndDataManipulationTransactions",
1683: ""
1684: + meta
1685: .supportsDataDefinitionAndDataManipulationTransactions() });
1686: rs
1687: .addRow(new String[] {
1688: "meta.supportsDataManipulationTransactionsOnly",
1689: ""
1690: + meta
1691: .supportsDataManipulationTransactionsOnly() });
1692: rs
1693: .addRow(new String[] {
1694: "meta.supportsDifferentTableCorrelationNames",
1695: ""
1696: + meta
1697: .supportsDifferentTableCorrelationNames() });
1698: rs.addRow(new String[] {
1699: "meta.supportsExpressionsInOrderBy",
1700: "" + meta.supportsExpressionsInOrderBy() });
1701: rs.addRow(new String[] { "meta.supportsExtendedSQLGrammar",
1702: "" + meta.supportsExtendedSQLGrammar() });
1703: rs.addRow(new String[] { "meta.supportsFullOuterJoins",
1704: "" + meta.supportsFullOuterJoins() });
1705: rs.addRow(new String[] { "meta.supportsGroupBy",
1706: "" + meta.supportsGroupBy() });
1707: // TODO meta data: more supports methods (I'm tired now)
1708: rs.addRow(new String[] { "meta.usesLocalFilePerTable",
1709: "" + meta.usesLocalFilePerTable() });
1710: rs.addRow(new String[] { "meta.usesLocalFiles",
1711: "" + meta.usesLocalFiles() });
1712: //#ifdef JDK14
1713: rs.addRow(new String[] { "conn.getHoldability",
1714: "" + conn.getHoldability() });
1715: rs.addRow(new String[] { "meta.getDatabaseMajorVersion",
1716: "" + meta.getDatabaseMajorVersion() });
1717: rs.addRow(new String[] { "meta.getDatabaseMinorVersion",
1718: "" + meta.getDatabaseMinorVersion() });
1719: rs.addRow(new String[] { "meta.getJDBCMajorVersion",
1720: "" + meta.getJDBCMajorVersion() });
1721: rs.addRow(new String[] { "meta.getJDBCMinorVersion",
1722: "" + meta.getJDBCMinorVersion() });
1723: rs.addRow(new String[] { "meta.getResultSetHoldability",
1724: "" + meta.getResultSetHoldability() });
1725: rs.addRow(new String[] { "meta.getSQLStateType",
1726: "" + meta.getSQLStateType() });
1727: rs.addRow(new String[] { "meta.supportsGetGeneratedKeys",
1728: "" + meta.supportsGetGeneratedKeys() });
1729: rs.addRow(new String[] { "meta.locatorsUpdateCopy",
1730: "" + meta.locatorsUpdateCopy() });
1731: //#endif
1732: return rs;
1733: } else if (sql.startsWith("@CATALOGS")) {
1734: return meta.getCatalogs();
1735: } else if (sql.startsWith("@TABLE_TYPES")) {
1736: return meta.getTableTypes();
1737: } else if (sql.startsWith("@COLUMN_PRIVILEGES")) {
1738: String[] p = split(sql);
1739: return meta.getColumnPrivileges(p[1], p[2], p[3], p[4]);
1740: } else if (sql.startsWith("@TABLE_PRIVILEGES")) {
1741: String[] p = split(sql);
1742: return meta.getTablePrivileges(p[1], p[2], p[3]);
1743: } else if (sql.startsWith("@BEST_ROW_IDENTIFIER")) {
1744: String[] p = split(sql);
1745: int scale = p[4] == null ? 0 : Integer.parseInt(p[4]);
1746: boolean nullable = p[5] == null ? false : Boolean.valueOf(
1747: p[5]).booleanValue();
1748: return meta.getBestRowIdentifier(p[1], p[2], p[3], scale,
1749: nullable);
1750: } else if (sql.startsWith("@VERSION_COLUMNS")) {
1751: String[] p = split(sql);
1752: return meta.getVersionColumns(p[1], p[2], p[3]);
1753: } else if (sql.startsWith("@IMPORTED_KEYS")) {
1754: String[] p = split(sql);
1755: return meta.getImportedKeys(p[1], p[2], p[3]);
1756: } else if (sql.startsWith("@EXPORTED_KEYS")) {
1757: String[] p = split(sql);
1758: return meta.getExportedKeys(p[1], p[2], p[3]);
1759: } else if (sql.startsWith("@CROSS_REFERENCE")) {
1760: String[] p = split(sql);
1761: return meta.getCrossReference(p[1], p[2], p[3], p[4], p[5],
1762: p[6]);
1763: } else if (sql.startsWith("@UDTS")) {
1764: String[] p = split(sql);
1765: int[] types;
1766: if (p[4] == null) {
1767: types = null;
1768: } else {
1769: String[] t = StringUtils.arraySplit(p[4], ',', false);
1770: types = new int[t.length];
1771: for (int i = 0; i < t.length; i++) {
1772: types[i] = Integer.parseInt(t[i]);
1773: }
1774: }
1775: return meta.getUDTs(p[1], p[2], p[3], types);
1776: } else if (sql.startsWith("@TYPE_INFO")) {
1777: return meta.getTypeInfo();
1778: //#ifdef JDK14
1779: } else if (sql.startsWith("@SUPER_TYPES")) {
1780: String[] p = split(sql);
1781: return meta.getSuperTypes(p[1], p[2], p[3]);
1782: } else if (sql.startsWith("@SUPER_TABLES")) {
1783: String[] p = split(sql);
1784: return meta.getSuperTables(p[1], p[2], p[3]);
1785: } else if (sql.startsWith("@ATTRIBUTES")) {
1786: String[] p = split(sql);
1787: return meta.getAttributes(p[1], p[2], p[3], p[4]);
1788: //#endif
1789: }
1790: return null;
1791: }
1792:
1793: private String[] split(String s) {
1794: String[] list = new String[10];
1795: String[] t = StringUtils.arraySplit(s, ' ', true);
1796: System.arraycopy(t, 0, list, 0, t.length);
1797: for (int i = 0; i < list.length; i++) {
1798: if ("null".equals(list[i])) {
1799: list[i] = null;
1800: }
1801: }
1802: return list;
1803: }
1804:
1805: private int getMaxrows() {
1806: String r = (String) session.get("maxrows");
1807: int maxrows = r == null ? 0 : Integer.parseInt(r);
1808: return maxrows;
1809: }
1810:
1811: private String getResult(Connection conn, int id, String sql,
1812: boolean allowEdit, boolean forceEdit) {
1813: try {
1814: sql = sql.trim();
1815: StringBuffer buff = new StringBuffer();
1816: String sqlUpper = StringUtils.toUpperEnglish(sql);
1817: if (sqlUpper.indexOf("CREATE") >= 0
1818: || sqlUpper.indexOf("DROP") >= 0
1819: || sqlUpper.indexOf("ALTER") >= 0
1820: || sqlUpper.indexOf("RUNSCRIPT") >= 0) {
1821: String sessionId = attributes.getProperty("jsessionid");
1822: buff
1823: .append("<script type=\"text/javascript\">top['h2menu'].location='tables.do?jsessionid="
1824: + sessionId + "';</script>");
1825: }
1826: Statement stat;
1827: DbContents contents = session.getContents();
1828: if (forceEdit || (allowEdit && contents.isH2)) {
1829: stat = conn.createStatement(
1830: ResultSet.TYPE_SCROLL_INSENSITIVE,
1831: ResultSet.CONCUR_UPDATABLE);
1832: } else {
1833: stat = conn.createStatement();
1834: }
1835: ResultSet rs;
1836: long time = System.currentTimeMillis();
1837: boolean metadata = false;
1838: boolean generatedKeys = false;
1839: boolean edit = false;
1840: boolean list = false;
1841: if ("@CANCEL".equals(sql)) {
1842: stat = session.executingStatement;
1843: if (stat != null) {
1844: stat.cancel();
1845: buff.append("${text.result.statementWasCancelled}");
1846: } else {
1847: buff.append("${text.result.noRunningStatement}");
1848: }
1849: return buff.toString();
1850: } else if (sql.startsWith("@PARAMETER_META")) {
1851: sql = sql.substring("@PARAMETER_META".length()).trim();
1852: PreparedStatement prep = conn.prepareStatement(sql);
1853: buff.append(getParameterResultSet(prep
1854: .getParameterMetaData()));
1855: return buff.toString();
1856: } else if (sql.startsWith("@META")) {
1857: metadata = true;
1858: sql = sql.substring("@META".length()).trim();
1859: } else if (sql.startsWith("@LIST")) {
1860: list = true;
1861: sql = sql.substring("@LIST".length()).trim();
1862: } else if (sql.startsWith("@GENERATED")) {
1863: generatedKeys = true;
1864: sql = sql.substring("@GENERATED".length()).trim();
1865: } else if (sql.startsWith("@LOOP")) {
1866: metadata = true;
1867: sql = sql.substring("@LOOP".length()).trim();
1868: int idx = sql.indexOf(' ');
1869: int count = MathUtils.decodeInt(sql.substring(0, idx));
1870: sql = sql.substring(idx).trim();
1871: return executeLoop(conn, count, sql);
1872: } else if (sql.startsWith("@EDIT")) {
1873: edit = true;
1874: sql = sql.substring("@EDIT".length()).trim();
1875: session.put("resultSetSQL", sql);
1876: } else if ("@HISTORY".equals(sql)) {
1877: buff.append(getHistoryString());
1878: return buff.toString();
1879: }
1880: if (sql.startsWith("@")) {
1881: rs = getMetaResultSet(conn, sql);
1882: if (rs == null) {
1883: buff.append("?: " + sql);
1884: return buff.toString();
1885: }
1886: } else {
1887: int maxrows = getMaxrows();
1888: stat.setMaxRows(maxrows);
1889: session.executingStatement = stat;
1890: boolean isResultSet = stat.execute(sql);
1891: session.addCommand(sql);
1892: if (generatedKeys) {
1893: rs = null;
1894: //#ifdef JDK14
1895: rs = stat.getGeneratedKeys();
1896: //#endif
1897: } else {
1898: if (!isResultSet) {
1899: buff.append("${text.result.updateCount}: "
1900: + stat.getUpdateCount());
1901: time = System.currentTimeMillis() - time;
1902: buff.append("<br />(");
1903: buff.append(time);
1904: buff.append(" ms)");
1905: stat.close();
1906: return buff.toString();
1907: }
1908: rs = stat.getResultSet();
1909: }
1910: }
1911: time = System.currentTimeMillis() - time;
1912: buff.append(getResultSet(sql, rs, metadata, list, edit,
1913: time, allowEdit));
1914: // SQLWarning warning = stat.getWarnings();
1915: // if(warning != null) {
1916: // buff.append("<br />Warning:<br />");
1917: // buff.append(getStackTrace(id, warning));
1918: // }
1919: if (!edit) {
1920: stat.close();
1921: }
1922: return buff.toString();
1923: } catch (Throwable e) {
1924: // throwable: including OutOfMemoryError and so on
1925: return getStackTrace(id, e, session.getContents().isH2);
1926: } finally {
1927: session.executingStatement = null;
1928: }
1929: }
1930:
1931: private String executeLoop(Connection conn, int count, String sql)
1932: throws SQLException {
1933: ArrayList params = new ArrayList();
1934: int idx = 0;
1935: while (!stop) {
1936: idx = sql.indexOf('?', idx);
1937: if (idx < 0) {
1938: break;
1939: }
1940: if (sql.substring(idx).startsWith("?/*RND*/")) {
1941: params.add(ObjectUtils.getInteger(1));
1942: sql = sql.substring(0, idx) + "?"
1943: + sql.substring(idx + "/*RND*/".length() + 1);
1944: } else {
1945: params.add(ObjectUtils.getInteger(0));
1946: }
1947: idx++;
1948: }
1949: int rows = 0;
1950: boolean prepared;
1951: Random random = new Random(1);
1952: long time = System.currentTimeMillis();
1953: if (sql.startsWith("@STATEMENT")) {
1954: sql = sql.substring("@STATEMENT".length()).trim();
1955: prepared = false;
1956: Statement stat = conn.createStatement();
1957: for (int i = 0; !stop && i < count; i++) {
1958: String s = sql;
1959: for (int j = 0; j < params.size(); j++) {
1960: idx = s.indexOf('?');
1961: Integer type = (Integer) params.get(j);
1962: if (type.intValue() == 1) {
1963: s = s.substring(0, idx) + random.nextInt(count)
1964: + s.substring(idx + 1);
1965: } else {
1966: s = s.substring(0, idx) + i
1967: + s.substring(idx + 1);
1968: }
1969: }
1970: if (stat.execute(s)) {
1971: ResultSet rs = stat.getResultSet();
1972: while (!stop && rs.next()) {
1973: rows++;
1974: // maybe get the data as well
1975: }
1976: rs.close();
1977: // maybe close result set
1978: }
1979: }
1980: } else {
1981: prepared = true;
1982: PreparedStatement prep = conn.prepareStatement(sql);
1983: for (int i = 0; !stop && i < count; i++) {
1984: for (int j = 0; j < params.size(); j++) {
1985: Integer type = (Integer) params.get(j);
1986: if (type.intValue() == 1) {
1987: prep.setInt(j + 1, random.nextInt(count));
1988: } else {
1989: prep.setInt(j + 1, i);
1990: }
1991: }
1992: if (session.getContents().isSQLite) {
1993: // SQLite currently throws an exception on prep.execute()
1994: prep.executeUpdate();
1995: } else {
1996: if (prep.execute()) {
1997: ResultSet rs = prep.getResultSet();
1998: while (!stop && rs.next()) {
1999: rows++;
2000: // maybe get the data as well
2001: }
2002: rs.close();
2003: }
2004: }
2005: }
2006: }
2007: time = System.currentTimeMillis() - time;
2008: String result = time + " ms: " + count + " * ";
2009: if (prepared) {
2010: result += "(Prepared) ";
2011: } else {
2012: result += "(Statement) ";
2013: }
2014: result += "(";
2015: StringBuffer buff = new StringBuffer();
2016: for (int i = 0; i < params.size(); i++) {
2017: if (i > 0) {
2018: buff.append(", ");
2019: }
2020: buff.append(((Integer) params.get(i)).intValue() == 0 ? "i"
2021: : "rnd");
2022: }
2023: result += buff.toString();
2024: result += ") " + sql;
2025: return result;
2026: }
2027:
2028: private String getHistoryString() {
2029: StringBuffer buff = new StringBuffer();
2030: ArrayList history = session.getCommands();
2031: buff.append("<table cellspacing=0 cellpadding=0>");
2032: buff.append("<tr><th></th><th>Command</th></tr>");
2033: for (int i = history.size() - 1; i >= 0; i--) {
2034: String sql = (String) history.get(i);
2035: buff.append("<tr><td>");
2036: buff.append("<a href=\"getHistory.do?id=");
2037: buff.append(i);
2038: buff
2039: .append("&jsessionid=${sessionId}\" target=\"h2query\" ><img width=16 height=16 src=\"ico_write.gif\" onmouseover = \"this.className ='icon_hover'\" onmouseout = \"this.className ='icon'\" class=\"icon\" alt=\"${text.resultEdit.edit}\" title=\"${text.resultEdit.edit}\" border=\"1\"/></a>");
2040: buff.append("</td><td>");
2041: buff.append(PageParser.escapeHtml(sql));
2042: buff.append("</td></tr>");
2043: }
2044: buff.append("</table>");
2045: return buff.toString();
2046: }
2047:
2048: private String getParameterResultSet(ParameterMetaData meta)
2049: throws SQLException {
2050: StringBuffer buff = new StringBuffer();
2051: if (meta == null) {
2052: return "No parameter meta data";
2053: }
2054: buff.append("<table cellspacing=0 cellpadding=0>");
2055: buff.append("<tr><th>className</th><th>mode</th><th>type</th>");
2056: buff
2057: .append("<th>typeName</th><th>precision</th><th>scale</th></tr>");
2058: for (int i = 0; i < meta.getParameterCount(); i++) {
2059: buff.append("</tr><td>");
2060: buff.append(meta.getParameterClassName(i + 1));
2061: buff.append("</td><td>");
2062: buff.append(meta.getParameterMode(i + 1));
2063: buff.append("</td><td>");
2064: buff.append(meta.getParameterType(i + 1));
2065: buff.append("</td><td>");
2066: buff.append(meta.getParameterTypeName(i + 1));
2067: buff.append("</td><td>");
2068: buff.append(meta.getPrecision(i + 1));
2069: buff.append("</td><td>");
2070: buff.append(meta.getScale(i + 1));
2071: buff.append("</td></tr>");
2072: }
2073: buff.append("</table>");
2074: return buff.toString();
2075: }
2076:
2077: private String getResultSet(String sql, ResultSet rs,
2078: boolean metadata, boolean list, boolean edit, long time,
2079: boolean allowEdit) throws SQLException {
2080: int maxrows = getMaxrows();
2081: time = System.currentTimeMillis() - time;
2082: StringBuffer buff = new StringBuffer();
2083: if (edit) {
2084: buff
2085: .append("<form id=\"editing\" name=\"editing\" method=\"post\" "
2086: + "action=\"editResult.do?jsessionid=${sessionId}\" id=\"mainForm\" target=\"h2result\">");
2087: buff
2088: .append("<input type=\"hidden\" name=\"op\" value=\"1\" />");
2089: buff
2090: .append("<input type=\"hidden\" name=\"row\" value=\"\" />");
2091: buff
2092: .append("<table cellspacing=0 cellpadding=0 id=\"editTable\">");
2093: } else {
2094: buff.append("<table cellspacing=0 cellpadding=0>");
2095: }
2096: ResultSetMetaData meta = rs.getMetaData();
2097: int columns = meta.getColumnCount();
2098: int rows = 0;
2099: if (metadata) {
2100: buff
2101: .append("<tr><th>i</th><th>label</th><th>cat</th><th>schem</th>");
2102: buff
2103: .append("<th>tab</th><th>col</th><th>type</th><th>typeName</th><th>class</th>");
2104: buff
2105: .append("<th>prec</th><th>scale</th><th>size</th><th>autoInc</th>");
2106: buff
2107: .append("<th>case</th><th>currency</th><th>null</th><th>ro</th>");
2108: buff
2109: .append("<th>search</th><th>sig</th><th>w</th><th>defW</th></tr>");
2110: for (int i = 1; i <= columns; i++) {
2111: buff.append("<tr>");
2112: buff.append("<td>").append(i).append("</td>");
2113: buff.append("<td>").append(
2114: PageParser.escapeHtml(meta.getColumnLabel(i)))
2115: .append("</td>");
2116: buff.append("<td>").append(
2117: PageParser.escapeHtml(meta.getCatalogName(i)))
2118: .append("</td>");
2119: buff.append("<td>").append(
2120: PageParser.escapeHtml(meta.getSchemaName(i)))
2121: .append("</td>");
2122: buff.append("<td>").append(
2123: PageParser.escapeHtml(meta.getTableName(i)))
2124: .append("</td>");
2125: buff.append("<td>").append(
2126: PageParser.escapeHtml(meta.getColumnName(i)))
2127: .append("</td>");
2128: buff.append("<td>").append(meta.getColumnType(i))
2129: .append("</td>");
2130: buff.append("<td>").append(
2131: PageParser
2132: .escapeHtml(meta.getColumnTypeName(i)))
2133: .append("</td>");
2134: buff.append("<td>").append(
2135: PageParser.escapeHtml(meta
2136: .getColumnClassName(i)))
2137: .append("</td>");
2138: buff.append("<td>").append(meta.getPrecision(i))
2139: .append("</td>");
2140: buff.append("<td>").append(meta.getScale(i)).append(
2141: "</td>");
2142: buff.append("<td>")
2143: .append(meta.getColumnDisplaySize(i)).append(
2144: "</td>");
2145: buff.append("<td>").append(meta.isAutoIncrement(i))
2146: .append("</td>");
2147: buff.append("<td>").append(meta.isCaseSensitive(i))
2148: .append("</td>");
2149: buff.append("<td>").append(meta.isCurrency(i)).append(
2150: "</td>");
2151: buff.append("<td>").append(meta.isNullable(i)).append(
2152: "</td>");
2153: buff.append("<td>").append(meta.isReadOnly(i)).append(
2154: "</td>");
2155: buff.append("<td>").append(meta.isSearchable(i))
2156: .append("</td>");
2157: buff.append("<td>").append(meta.isSigned(i)).append(
2158: "</td>");
2159: buff.append("<td>").append(meta.isWritable(i)).append(
2160: "</td>");
2161: buff.append("<td>")
2162: .append(meta.isDefinitelyWritable(i)).append(
2163: "</td>");
2164: buff.append("</tr>");
2165: }
2166: } else if (list) {
2167: buff.append("<tr><th>Column</th><th>Data</th></tr><tr>");
2168: while (rs.next()) {
2169: if (maxrows > 0 && rows >= maxrows) {
2170: break;
2171: }
2172: rows++;
2173: buff.append("<tr><td>Row #</td><td>");
2174: buff.append(rows);
2175: buff.append("</tr>");
2176: for (int i = 0; i < columns; i++) {
2177: buff.append("<tr><td>");
2178: buff.append(PageParser.escapeHtml(meta
2179: .getColumnLabel(i + 1)));
2180: buff.append("</td>");
2181: buff.append("<td>");
2182: buff.append(escapeData(rs.getString(i + 1)));
2183: buff.append("</td></tr>");
2184: }
2185: }
2186: } else {
2187: buff.append("<tr>");
2188: if (edit) {
2189: buff.append("<th>Action</th>");
2190: }
2191: for (int i = 0; i < columns; i++) {
2192: buff.append("<th>");
2193: buff.append(PageParser.escapeHtml(meta
2194: .getColumnLabel(i + 1)));
2195: buff.append("</th>");
2196: }
2197: buff.append("</tr>");
2198: while (rs.next()) {
2199: if (maxrows > 0 && rows >= maxrows) {
2200: break;
2201: }
2202: rows++;
2203: buff.append("<tr>");
2204: if (edit) {
2205: buff.append("<td>");
2206: buff.append("<img onclick=\"javascript:editRow(");
2207: buff.append(rs.getRow());
2208: buff
2209: .append(",'${sessionId}', '${text.resultEdit.save}', '${text.resultEdit.cancel}'");
2210: buff
2211: .append(")\" width=16 height=16 src=\"ico_write.gif\" onmouseover = \"this.className ='icon_hover'\" onmouseout = \"this.className ='icon'\" class=\"icon\" alt=\"${text.resultEdit.edit}\" title=\"${text.resultEdit.edit}\" border=\"1\"/>");
2212: buff.append("<a href=\"editResult.do?op=2&row=");
2213: buff.append(rs.getRow());
2214: buff
2215: .append("&jsessionid=${sessionId}\" target=\"h2result\" ><img width=16 height=16 src=\"ico_remove.gif\" onmouseover = \"this.className ='icon_hover'\" onmouseout = \"this.className ='icon'\" class=\"icon\" alt=\"${text.resultEdit.delete}\" title=\"${text.resultEdit.delete}\" border=\"1\" /></a>");
2216: buff.append("</td>");
2217: }
2218: for (int i = 0; i < columns; i++) {
2219: buff.append("<td>");
2220: buff.append(escapeData(rs.getString(i + 1)));
2221: buff.append("</td>");
2222: }
2223: buff.append("</tr>");
2224: }
2225: }
2226: boolean isUpdatable = rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE
2227: && rs.getType() != ResultSet.TYPE_FORWARD_ONLY;
2228: if (edit) {
2229: ResultSet old = session.result;
2230: if (old != null) {
2231: old.close();
2232: }
2233: session.result = rs;
2234: } else {
2235: rs.close();
2236: }
2237: if (edit) {
2238: buff.append("<tr><td>");
2239: buff
2240: .append("<img onclick=\"javascript:editRow(-1, '${sessionId}', '${text.resultEdit.save}', '${text.resultEdit.cancel}'");
2241: buff
2242: .append(")\" width=16 height=16 src=\"ico_add.gif\" onmouseover = \"this.className ='icon_hover'\" onmouseout = \"this.className ='icon'\" class=\"icon\" alt=\"${text.resultEdit.add}\" title=\"${text.resultEdit.add}\" border=\"1\"/>");
2243: buff.append("</td>");
2244: for (int i = 0; i < columns; i++) {
2245: buff.append("<td></td>");
2246: }
2247: buff.append("</tr>");
2248: }
2249: buff.append("</table>");
2250: if (edit) {
2251: buff.append("</form>");
2252: }
2253: if (rows == 0) {
2254: buff.append("(${text.result.noRows}");
2255: } else if (rows == 1) {
2256: buff.append("(${text.result.1row}");
2257: } else {
2258: buff.append("(");
2259: buff.append(rows);
2260: buff.append(" ${text.result.rows}");
2261: }
2262: buff.append(", ");
2263: time = System.currentTimeMillis() - time;
2264: buff.append(time);
2265: buff.append(" ms)");
2266: if (!edit && isUpdatable && allowEdit) {
2267: buff
2268: .append("<br /><br /><form name=\"editResult\" method=\"post\" action=\"query.do?jsessionid=${sessionId}\" target=\"h2result\">");
2269: buff
2270: .append("<input type=\"submit\" class=\"button\" value=\"${text.resultEdit.editResult}\" />");
2271: buff
2272: .append("<input type=\"hidden\" name=\"sql\" value=\"@EDIT "
2273: + PageParser.escapeHtml(sql) + "\" />");
2274: buff.append("</form>");
2275: }
2276: return buff.toString();
2277: }
2278:
2279: private String settingSave() {
2280: ConnectionInfo info = new ConnectionInfo();
2281: info.name = attributes.getProperty("name", "");
2282: info.driver = attributes.getProperty("driver", "");
2283: info.url = attributes.getProperty("url", "");
2284: info.user = attributes.getProperty("user", "");
2285: server.updateSetting(info);
2286: attributes.put("setting", info.name);
2287: server.saveSettings();
2288: return "index.do";
2289: }
2290:
2291: private String escapeData(String d) {
2292: if (d == null) {
2293: return "<i>null</i>";
2294: } else if (d.startsWith("null")) {
2295: return "<div style='display: none'>=</div>"
2296: + PageParser.escapeHtml(d);
2297: }
2298: return PageParser.escapeHtml(d);
2299: }
2300:
2301: private String unescapeData(String d) {
2302: if (d.endsWith("null")) {
2303: if (d.equals("null")) {
2304: return null;
2305: } else if (d.startsWith("=")) {
2306: return d.substring(1);
2307: }
2308: }
2309: return d;
2310: }
2311:
2312: private String settingRemove() {
2313: String setting = attributes.getProperty("name", "");
2314: server.removeSetting(setting);
2315: ArrayList settings = server.getSettings();
2316: if (settings.size() > 0) {
2317: attributes.put("setting", settings.get(0));
2318: }
2319: server.saveSettings();
2320: return "index.do";
2321: }
2322:
2323: boolean allow() {
2324: if (server.getAllowOthers()) {
2325: return true;
2326: }
2327: return NetUtils.isLoopbackAddress(socket);
2328: }
2329:
2330: public String getMimeType() {
2331: return mimeType;
2332: }
2333:
2334: public boolean getCache() {
2335: return cache;
2336: }
2337:
2338: public WebSession getSession() {
2339: return session;
2340: }
2341:
2342: public void closingDatabase() {
2343: log("Closing database");
2344: }
2345:
2346: public void diskSpaceIsLow(long stillAvailable) throws SQLException {
2347: log("Disk space is low; still available: " + stillAvailable);
2348: }
2349:
2350: public void exceptionThrown(SQLException e, String sql) {
2351: log("Exception: " + e.toString() + " SQL: " + sql);
2352: }
2353:
2354: public void init(String url) {
2355: log("Init: " + url);
2356: }
2357:
2358: public void opened() {
2359: log("Database was opened");
2360: }
2361:
2362: public void setProgress(int state, String name, int x, int max) {
2363: if (state == listenerLastState) {
2364: long time = System.currentTimeMillis();
2365: if (listenerLastEvent + 500 < time) {
2366: return;
2367: }
2368: listenerLastEvent = time;
2369: } else {
2370: listenerLastState = state;
2371: }
2372: switch (state) {
2373: case DatabaseEventListener.STATE_BACKUP_FILE:
2374: log("Backing up " + name + " " + (100L * x / max) + "%");
2375: break;
2376: case DatabaseEventListener.STATE_CREATE_INDEX:
2377: log("Creating index " + name + " " + (100L * x / max) + "%");
2378: break;
2379: case DatabaseEventListener.STATE_RECOVER:
2380: log("Recovering " + name + " " + (100L * x / max) + "%");
2381: break;
2382: case DatabaseEventListener.STATE_SCAN_FILE:
2383: log("Scanning file " + name + " " + (100L * x / max) + "%");
2384: break;
2385: default:
2386: log("Unknown state: " + state);
2387: }
2388: }
2389:
2390: private void log(String s) {
2391: // System.out.println(s);
2392: }
2393:
2394: }
|