Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.h2.server.web.WebApp Maven / Gradle / Ivy
/*
* Copyright 2004-2019 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (https://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.server.web;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.ParameterMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import org.h2.api.ErrorCode;
import org.h2.bnf.Bnf;
import org.h2.bnf.context.DbColumn;
import org.h2.bnf.context.DbContents;
import org.h2.bnf.context.DbSchema;
import org.h2.bnf.context.DbTableOrView;
import org.h2.command.Parser;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
import org.h2.jdbc.JdbcException;
import org.h2.message.DbException;
import org.h2.security.SHA256;
import org.h2.tools.Backup;
import org.h2.tools.ChangeFileEncryption;
import org.h2.tools.ConvertTraceFile;
import org.h2.tools.CreateCluster;
import org.h2.tools.DeleteDbFiles;
import org.h2.tools.Recover;
import org.h2.tools.Restore;
import org.h2.tools.RunScript;
import org.h2.tools.Script;
import org.h2.tools.SimpleResultSet;
import org.h2.util.JdbcUtils;
import org.h2.util.NetUtils;
import org.h2.util.NetworkConnectionInfo;
import org.h2.util.Profiler;
import org.h2.util.ScriptReader;
import org.h2.util.SortedProperties;
import org.h2.util.StringUtils;
import org.h2.util.Tool;
import org.h2.util.Utils;
/**
* For each connection to a session, an object of this class is created.
* This class is used by the H2 Console.
*/
public class WebApp {
/**
* The web server.
*/
protected final WebServer server;
/**
* The session.
*/
protected WebSession session;
/**
* The session attributes
*/
protected Properties attributes;
/**
* The mime type of the current response.
*/
protected String mimeType;
/**
* Whether the response can be cached.
*/
protected boolean cache;
/**
* Whether to close the connection.
*/
protected boolean stop;
/**
* The language in the HTTP header.
*/
protected String headerLanguage;
private Profiler profiler;
WebApp(WebServer server) {
this.server = server;
}
/**
* Set the web session and attributes.
*
* @param session the session
* @param attributes the attributes
*/
void setSession(WebSession session, Properties attributes) {
this.session = session;
this.attributes = attributes;
}
/**
* Process an HTTP request.
*
* @param file the file that was requested
* @param networkConnectionInfo the network connection information
* @return the name of the file to return to the client
*/
String processRequest(String file, NetworkConnectionInfo networkConnectionInfo) {
int index = file.lastIndexOf('.');
String suffix;
if (index >= 0) {
suffix = file.substring(index + 1);
} else {
suffix = "";
}
if ("ico".equals(suffix)) {
mimeType = "image/x-icon";
cache = true;
} else if ("gif".equals(suffix)) {
mimeType = "image/gif";
cache = true;
} else if ("css".equals(suffix)) {
cache = true;
mimeType = "text/css";
} else if ("html".equals(suffix) ||
"do".equals(suffix) ||
"jsp".equals(suffix)) {
cache = false;
mimeType = "text/html";
if (session == null) {
session = server.createNewSession(
NetUtils.ipToShortForm(null, networkConnectionInfo.getClientAddr(), false).toString());
if (!"notAllowed.jsp".equals(file)) {
file = "index.do";
}
}
} else if ("js".equals(suffix)) {
cache = true;
mimeType = "text/javascript";
} else {
cache = true;
mimeType = "application/octet-stream";
}
trace("mimeType=" + mimeType);
trace(file);
if (file.endsWith(".do")) {
file = process(file, networkConnectionInfo);
} else if (file.endsWith(".jsp")) {
switch (file) {
case "admin.jsp":
case "tools.jsp":
if (!checkAdmin(file)) {
file = process("adminLogin.do", networkConnectionInfo);
}
}
}
return file;
}
private static String getComboBox(String[] elements, String selected) {
StringBuilder buff = new StringBuilder();
for (String value : elements) {
buff.append("').
append(PageParser.escapeHtml(value)).
append(" ");
}
return buff.toString();
}
private static String getComboBox(String[][] elements, String selected) {
StringBuilder buff = new StringBuilder();
for (String[] n : elements) {
buff.append("').
append(PageParser.escapeHtml(n[1])).
append(" ");
}
return buff.toString();
}
private String process(String file, NetworkConnectionInfo networkConnectionInfo) {
trace("process " + file);
while (file.endsWith(".do")) {
switch (file) {
case "login.do":
file = login(networkConnectionInfo);
break;
case "index.do":
file = index();
break;
case "logout.do":
file = logout();
break;
case "settingRemove.do":
file = settingRemove();
break;
case "settingSave.do":
file = settingSave();
break;
case "test.do":
file = test(networkConnectionInfo);
break;
case "query.do":
file = query();
break;
case "tables.do":
file = tables();
break;
case "editResult.do":
file = editResult();
break;
case "getHistory.do":
file = getHistory();
break;
case "admin.do":
file = checkAdmin(file) ? admin() : "adminLogin.do";
break;
case "adminSave.do":
file = checkAdmin(file) ? adminSave() : "adminLogin.do";
break;
case "adminStartTranslate.do":
file = checkAdmin(file) ? adminStartTranslate() : "adminLogin.do";
break;
case "adminShutdown.do":
file = checkAdmin(file) ? adminShutdown() : "adminLogin.do";
break;
case "autoCompleteList.do":
file = autoCompleteList();
break;
case "tools.do":
file = checkAdmin(file) ? tools() : "adminLogin.do";
break;
case "adminLogin.do":
file = adminLogin();
break;
default:
file = "error.jsp";
break;
}
}
trace("return " + file);
return file;
}
private boolean checkAdmin(String file) {
Boolean b = (Boolean) session.get("admin");
if (b != null && b) {
return true;
}
String key = server.getKey();
if (key != null && key.equals(session.get("key"))) {
return true;
}
session.put("adminBack", file);
return false;
}
private String adminLogin() {
String password = attributes.getProperty("password");
if (password == null || password.isEmpty() || !server.checkAdminPassword(password)) {
return "adminLogin.jsp";
}
String back = (String) session.remove("adminBack");
session.put("admin", true);
return back != null ? back : "admin.do";
}
private String autoCompleteList() {
String query = (String) attributes.get("query");
boolean lowercase = false;
String tQuery = query.trim();
if (!tQuery.isEmpty() && Character.isLowerCase(tQuery.charAt(0))) {
lowercase = true;
}
try {
String sql = query;
if (sql.endsWith(";")) {
sql += " ";
}
ScriptReader reader = new ScriptReader(new StringReader(sql));
reader.setSkipRemarks(true);
String lastSql = "";
while (true) {
String n = reader.readStatement();
if (n == null) {
break;
}
lastSql = n;
}
String result = "";
if (reader.isInsideRemark()) {
if (reader.isBlockRemark()) {
result = "1#(End Remark)# */\n" + result;
} else {
result = "1#(Newline)#\n" + result;
}
} else {
sql = lastSql;
while (sql.length() > 0 && sql.charAt(0) <= ' ') {
sql = sql.substring(1);
}
String tSql = sql.trim();
if (!tSql.isEmpty() && Character.isLowerCase(tSql.charAt(0))) {
lowercase = true;
}
Bnf bnf = session.getBnf();
if (bnf == null) {
return "autoCompleteList.jsp";
}
HashMap map = bnf.getNextTokenList(sql);
String space = "";
if (sql.length() > 0) {
char last = sql.charAt(sql.length() - 1);
if (!Character.isWhitespace(last) && (last != '.' &&
last >= ' ' && last != '\'' && last != '"')) {
space = " ";
}
}
ArrayList list = new ArrayList<>(map.size());
for (Map.Entry entry : map.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
String type = String.valueOf(key.charAt(0));
if (Integer.parseInt(type) > 2) {
continue;
}
key = key.substring(2);
if (Character.isLetter(key.charAt(0)) && lowercase) {
key = StringUtils.toLowerEnglish(key);
value = StringUtils.toLowerEnglish(value);
}
if (key.equals(value) && !".".equals(value)) {
value = space + value;
}
key = StringUtils.urlEncode(key);
key = key.replace('+', ' ');
value = StringUtils.urlEncode(value);
value = value.replace('+', ' ');
list.add(type + "#" + key + "#" + value);
}
Collections.sort(list);
if (query.endsWith("\n") || tQuery.endsWith(";")) {
list.add(0, "1#(Newline)#\n");
}
result = StringUtils.join(new StringBuilder(), list, "|").toString();
}
session.put("autoCompleteList", result);
} catch (Throwable e) {
server.traceError(e);
}
return "autoCompleteList.jsp";
}
private String admin() {
session.put("port", Integer.toString(server.getPort()));
session.put("allowOthers", Boolean.toString(server.getAllowOthers()));
session.put("ssl", String.valueOf(server.getSSL()));
session.put("sessions", server.getSessions());
return "admin.jsp";
}
private String adminSave() {
try {
Properties prop = new SortedProperties();
int port = Integer.decode((String) attributes.get("port"));
prop.setProperty("webPort", Integer.toString(port));
server.setPort(port);
boolean allowOthers = Utils.parseBoolean((String) attributes.get("allowOthers"), false, false);
prop.setProperty("webAllowOthers", String.valueOf(allowOthers));
server.setAllowOthers(allowOthers);
boolean ssl = Utils.parseBoolean((String) attributes.get("ssl"), false, false);
prop.setProperty("webSSL", String.valueOf(ssl));
server.setSSL(ssl);
byte[] adminPassword = server.getAdminPassword();
if (adminPassword != null) {
prop.setProperty("webAdminPassword", StringUtils.convertBytesToHex(adminPassword));
}
server.saveProperties(prop);
} catch (Exception e) {
trace(e.toString());
}
return admin();
}
private String tools() {
try {
String toolName = (String) attributes.get("tool");
session.put("tool", toolName);
String args = (String) attributes.get("args");
String[] argList = StringUtils.arraySplit(args, ',', false);
Tool tool = null;
if ("Backup".equals(toolName)) {
tool = new Backup();
} else if ("Restore".equals(toolName)) {
tool = new Restore();
} else if ("Recover".equals(toolName)) {
tool = new Recover();
} else if ("DeleteDbFiles".equals(toolName)) {
tool = new DeleteDbFiles();
} else if ("ChangeFileEncryption".equals(toolName)) {
tool = new ChangeFileEncryption();
} else if ("Script".equals(toolName)) {
tool = new Script();
} else if ("RunScript".equals(toolName)) {
tool = new RunScript();
} else if ("ConvertTraceFile".equals(toolName)) {
tool = new ConvertTraceFile();
} else if ("CreateCluster".equals(toolName)) {
tool = new CreateCluster();
} else {
throw DbException.throwInternalError(toolName);
}
ByteArrayOutputStream outBuff = new ByteArrayOutputStream();
PrintStream out = new PrintStream(outBuff, false, "UTF-8");
tool.setOut(out);
try {
tool.runTool(argList);
out.flush();
String o = new String(outBuff.toByteArray(), StandardCharsets.UTF_8);
String result = PageParser.escapeHtml(o);
session.put("toolResult", result);
} catch (Exception e) {
session.put("toolResult", getStackTrace(0, e, true));
}
} catch (Exception e) {
server.traceError(e);
}
return "tools.jsp";
}
private String adminStartTranslate() {
Map, ?> p = Map.class.cast(session.map.get("text"));
@SuppressWarnings("unchecked")
Map p2 = (Map) p;
String file = server.startTranslate(p2);
session.put("translationFile", file);
return "helpTranslate.jsp";
}
/**
* Stop the application and the server.
*
* @return the page to display
*/
protected String adminShutdown() {
server.shutdown();
return "admin.jsp";
}
private String index() {
String[][] languageArray = WebServer.LANGUAGES;
String language = (String) attributes.get("language");
Locale locale = session.locale;
if (language != null) {
if (locale == null || !StringUtils.toLowerEnglish(
locale.getLanguage()).equals(language)) {
locale = new Locale(language, "");
server.readTranslations(session, locale.getLanguage());
session.put("language", language);
session.locale = locale;
}
} else {
language = (String) session.get("language");
}
if (language == null) {
// if the language is not yet known
// use the last header
language = headerLanguage;
}
session.put("languageCombo", getComboBox(languageArray, language));
String[] settingNames = server.getSettingNames();
String setting = attributes.getProperty("setting");
if (setting == null && settingNames.length > 0) {
setting = settingNames[0];
}
String combobox = getComboBox(settingNames, setting);
session.put("settingsList", combobox);
ConnectionInfo info = server.getSetting(setting);
if (info == null) {
info = new ConnectionInfo();
}
session.put("setting", PageParser.escapeHtmlData(setting));
session.put("name", PageParser.escapeHtmlData(setting));
session.put("driver", PageParser.escapeHtmlData(info.driver));
session.put("url", PageParser.escapeHtmlData(info.url));
session.put("user", PageParser.escapeHtmlData(info.user));
return "index.jsp";
}
private String getHistory() {
int id = Integer.parseInt(attributes.getProperty("id"));
String sql = session.getCommand(id);
session.put("query", PageParser.escapeHtmlData(sql));
return "query.jsp";
}
private static int addColumns(boolean mainSchema, DbTableOrView table,
StringBuilder buff, int treeIndex, boolean showColumnTypes,
StringBuilder columnsBuffer) {
DbColumn[] columns = table.getColumns();
for (int i = 0; columns != null && i < columns.length; i++) {
DbColumn column = columns[i];
if (columnsBuffer.length() > 0) {
columnsBuffer.append(' ');
}
columnsBuffer.append(column.getName());
String col = escapeIdentifier(column.getName());
String level = mainSchema ? ", 1, 1" : ", 2, 2";
buff.append("setNode(").append(treeIndex).append(level)
.append(", 'column', '")
.append(PageParser.escapeJavaScript(column.getName()))
.append("', 'javascript:ins(\\'").append(col).append("\\')');\n");
treeIndex++;
if (mainSchema && showColumnTypes) {
buff.append("setNode(").append(treeIndex)
.append(", 2, 2, 'type', '")
.append(PageParser.escapeJavaScript(column.getDataType()))
.append("', null);\n");
treeIndex++;
}
}
return treeIndex;
}
private static String escapeIdentifier(String name) {
return StringUtils.urlEncode(
PageParser.escapeJavaScript(name)).replace('+', ' ');
}
/**
* This class represents index information for the GUI.
*/
static class IndexInfo {
/**
* The index name.
*/
String name;
/**
* The index type name.
*/
String type;
/**
* The indexed columns.
*/
String columns;
}
private static int addIndexes(boolean mainSchema, DatabaseMetaData meta,
String table, String schema, StringBuilder buff, int treeIndex)
throws SQLException {
ResultSet rs;
try {
rs = meta.getIndexInfo(null, schema, table, false, true);
} catch (SQLException e) {
// SQLite
return treeIndex;
}
HashMap indexMap = new HashMap<>();
while (rs.next()) {
String name = rs.getString("INDEX_NAME");
IndexInfo info = indexMap.get(name);
if (info == null) {
int t = rs.getInt("TYPE");
String type;
if (t == DatabaseMetaData.tableIndexClustered) {
type = "";
} else if (t == DatabaseMetaData.tableIndexHashed) {
type = " (${text.tree.hashed})";
} else if (t == DatabaseMetaData.tableIndexOther) {
type = "";
} else {
type = null;
}
if (name != null && type != null) {
info = new IndexInfo();
info.name = name;
type = (rs.getBoolean("NON_UNIQUE") ?
"${text.tree.nonUnique}" : "${text.tree.unique}") + type;
info.type = type;
info.columns = rs.getString("COLUMN_NAME");
indexMap.put(name, info);
}
} else {
info.columns += ", " + rs.getString("COLUMN_NAME");
}
}
rs.close();
if (indexMap.size() > 0) {
String level = mainSchema ? ", 1, 1" : ", 2, 1";
String levelIndex = mainSchema ? ", 2, 1" : ", 3, 1";
String levelColumnType = mainSchema ? ", 3, 2" : ", 4, 2";
buff.append("setNode(").append(treeIndex).append(level)
.append(", 'index_az', '${text.tree.indexes}', null);\n");
treeIndex++;
for (IndexInfo info : indexMap.values()) {
buff.append("setNode(").append(treeIndex).append(levelIndex)
.append(", 'index', '")
.append(PageParser.escapeJavaScript(info.name))
.append("', null);\n");
treeIndex++;
buff.append("setNode(").append(treeIndex).append(levelColumnType)
.append(", 'type', '").append(info.type).append("', null);\n");
treeIndex++;
buff.append("setNode(").append(treeIndex).append(levelColumnType)
.append(", 'type', '")
.append(PageParser.escapeJavaScript(info.columns))
.append("', null);\n");
treeIndex++;
}
}
return treeIndex;
}
private int addTablesAndViews(DbSchema schema, boolean mainSchema,
StringBuilder buff, int treeIndex) throws SQLException {
if (schema == null) {
return treeIndex;
}
Connection conn = session.getConnection();
DatabaseMetaData meta = session.getMetaData();
int level = mainSchema ? 0 : 1;
boolean showColumns = mainSchema || !schema.isSystem;
String indentation = ", " + level + ", " + (showColumns ? "1" : "2") + ", ";
String indentNode = ", " + (level + 1) + ", 2, ";
DbTableOrView[] tables = schema.getTables();
if (tables == null) {
return treeIndex;
}
boolean isOracle = schema.getContents().isOracle();
boolean notManyTables = tables.length < SysProperties.CONSOLE_MAX_TABLES_LIST_INDEXES;
for (DbTableOrView table : tables) {
if (table.isView()) {
continue;
}
int tableId = treeIndex;
String tab = table.getQuotedName();
if (!mainSchema) {
tab = schema.quotedName + "." + tab;
}
tab = escapeIdentifier(tab);
buff.append("setNode(").append(treeIndex).append(indentation)
.append(" 'table', '")
.append(PageParser.escapeJavaScript(table.getName()))
.append("', 'javascript:ins(\\'").append(tab).append("\\',true)');\n");
treeIndex++;
if (mainSchema || showColumns) {
StringBuilder columnsBuffer = new StringBuilder();
treeIndex = addColumns(mainSchema, table, buff, treeIndex,
notManyTables, columnsBuffer);
if (!isOracle && notManyTables) {
treeIndex = addIndexes(mainSchema, meta, table.getName(),
schema.name, buff, treeIndex);
}
buff.append("addTable('")
.append(PageParser.escapeJavaScript(table.getName())).append("', '")
.append(PageParser.escapeJavaScript(columnsBuffer.toString())).append("', ")
.append(tableId).append(");\n");
}
}
tables = schema.getTables();
for (DbTableOrView view : tables) {
if (!view.isView()) {
continue;
}
int tableId = treeIndex;
String tab = view.getQuotedName();
if (!mainSchema) {
tab = view.getSchema().quotedName + "." + tab;
}
tab = escapeIdentifier(tab);
buff.append("setNode(").append(treeIndex).append(indentation)
.append(" 'view', '")
.append(PageParser.escapeJavaScript(view.getName()))
.append("', 'javascript:ins(\\'").append(tab).append("\\',true)');\n");
treeIndex++;
if (mainSchema) {
StringBuilder columnsBuffer = new StringBuilder();
treeIndex = addColumns(mainSchema, view, buff,
treeIndex, notManyTables, columnsBuffer);
if (schema.getContents().isH2()) {
try (PreparedStatement prep = conn.prepareStatement("SELECT * FROM " +
"INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME=?")) {
prep.setString(1, view.getName());
ResultSet rs = prep.executeQuery();
if (rs.next()) {
String sql = rs.getString("SQL");
buff.append("setNode(").append(treeIndex)
.append(indentNode)
.append(" 'type', '")
.append(PageParser.escapeJavaScript(sql))
.append("', null);\n");
treeIndex++;
}
rs.close();
}
}
buff.append("addTable('")
.append(PageParser.escapeJavaScript(view.getName())).append("', '")
.append(PageParser.escapeJavaScript(columnsBuffer.toString())).append("', ")
.append(tableId).append(");\n");
}
}
return treeIndex;
}
private String tables() {
DbContents contents = session.getContents();
boolean isH2 = false;
try {
String url = (String) session.get("url");
Connection conn = session.getConnection();
contents.readContents(url, conn);
session.loadBnf();
isH2 = contents.isH2();
StringBuilder buff = new StringBuilder()
.append("setNode(0, 0, 0, 'database', '")
.append(PageParser.escapeJavaScript(url))
.append("', null);\n");
int treeIndex = 1;
DbSchema defaultSchema = contents.getDefaultSchema();
treeIndex = addTablesAndViews(defaultSchema, true, buff, treeIndex);
DbSchema[] schemas = contents.getSchemas();
for (DbSchema schema : schemas) {
if (schema == defaultSchema || schema == null) {
continue;
}
buff.append("setNode(").append(treeIndex).append(", 0, 1, 'folder', '")
.append(PageParser.escapeJavaScript(schema.name))
.append("', null);\n");
treeIndex++;
treeIndex = addTablesAndViews(schema, false, buff, treeIndex);
}
if (isH2) {
try (Statement stat = conn.createStatement()) {
ResultSet rs = stat.executeQuery("SELECT * FROM " +
"INFORMATION_SCHEMA.SEQUENCES ORDER BY SEQUENCE_NAME");
for (int i = 0; rs.next(); i++) {
if (i == 0) {
buff.append("setNode(").append(treeIndex)
.append(", 0, 1, 'sequences', '${text.tree.sequences}', null);\n");
treeIndex++;
}
String name = rs.getString("SEQUENCE_NAME");
String current = rs.getString("CURRENT_VALUE");
String increment = rs.getString("INCREMENT");
buff.append("setNode(").append(treeIndex)
.append(", 1, 1, 'sequence', '")
.append(PageParser.escapeJavaScript(name))
.append("', null);\n");
treeIndex++;
buff.append("setNode(").append(treeIndex)
.append(", 2, 2, 'type', '${text.tree.current}: ")
.append(PageParser.escapeJavaScript(current))
.append("', null);\n");
treeIndex++;
if (!"1".equals(increment)) {
buff.append("setNode(").append(treeIndex)
.append(", 2, 2, 'type', '${text.tree.increment}: ")
.append(PageParser.escapeJavaScript(increment))
.append("', null);\n");
treeIndex++;
}
}
rs.close();
rs = stat.executeQuery("SELECT * FROM " +
"INFORMATION_SCHEMA.USERS ORDER BY NAME");
for (int i = 0; rs.next(); i++) {
if (i == 0) {
buff.append("setNode(").append(treeIndex)
.append(", 0, 1, 'users', '${text.tree.users}', null);\n");
treeIndex++;
}
String name = rs.getString("NAME");
String admin = rs.getString("ADMIN");
buff.append("setNode(").append(treeIndex)
.append(", 1, 1, 'user', '")
.append(PageParser.escapeJavaScript(name))
.append("', null);\n");
treeIndex++;
if (admin.equalsIgnoreCase("TRUE")) {
buff.append("setNode(").append(treeIndex)
.append(", 2, 2, 'type', '${text.tree.admin}', null);\n");
treeIndex++;
}
}
rs.close();
}
}
DatabaseMetaData meta = session.getMetaData();
String version = meta.getDatabaseProductName() + " " +
meta.getDatabaseProductVersion();
buff.append("setNode(").append(treeIndex)
.append(", 0, 0, 'info', '")
.append(PageParser.escapeJavaScript(version))
.append("', null);\n")
.append("refreshQueryTables();");
session.put("tree", buff.toString());
} catch (Exception e) {
session.put("tree", "");
session.put("error", getStackTrace(0, e, isH2));
}
return "tables.jsp";
}
private String getStackTrace(int id, Throwable e, boolean isH2) {
try {
StringWriter writer = new StringWriter();
e.printStackTrace(new PrintWriter(writer));
String stackTrace = writer.toString();
stackTrace = PageParser.escapeHtml(stackTrace);
if (isH2) {
stackTrace = linkToSource(stackTrace);
}
stackTrace = StringUtils.replaceAll(stackTrace, "\t",
" ");
String message = PageParser.escapeHtml(e.getMessage());
String error = "" + message +
" ";
if (e instanceof SQLException) {
SQLException se = (SQLException) e;
error += " " + se.getSQLState() + "/" + se.getErrorCode();
if (isH2) {
int code = se.getErrorCode();
error += " (${text.a.help}) ";
}
}
error += " " + stackTrace + " ";
error = formatAsError(error);
return error;
} catch (OutOfMemoryError e2) {
server.traceError(e);
return e.toString();
}
}
private static String linkToSource(String s) {
try {
StringBuilder result = new StringBuilder(s.length());
int idx = s.indexOf(" ");
result.append(s, 0, idx);
while (true) {
int start = s.indexOf("org.h2.", idx);
if (start < 0) {
result.append(s.substring(idx));
break;
}
result.append(s, idx, start);
int end = s.indexOf(')', start);
if (end < 0) {
result.append(s.substring(idx));
break;
}
String element = s.substring(start, end);
int open = element.lastIndexOf('(');
int dotMethod = element.lastIndexOf('.', open - 1);
int dotClass = element.lastIndexOf('.', dotMethod - 1);
String packageName = element.substring(0, dotClass);
int colon = element.lastIndexOf(':');
String file = element.substring(open + 1, colon);
String lineNumber = element.substring(colon + 1, element.length());
String fullFileName = packageName.replace('.', '/') + "/" + file;
result.append("");
result.append(element);
result.append(" ");
idx = end;
}
return result.toString();
} catch (Throwable t) {
return s;
}
}
private static String formatAsError(String s) {
return "" + s + "
";
}
private String test(NetworkConnectionInfo networkConnectionInfo) {
String driver = attributes.getProperty("driver", "");
String url = attributes.getProperty("url", "");
String user = attributes.getProperty("user", "");
String password = attributes.getProperty("password", "");
session.put("driver", driver);
session.put("url", url);
session.put("user", user);
boolean isH2 = url.startsWith("jdbc:h2:");
try {
long start = System.currentTimeMillis();
String profOpen = "", profClose = "";
Profiler prof = new Profiler();
prof.startCollecting();
Connection conn;
try {
conn = server.getConnection(driver, url, user, password, null, networkConnectionInfo);
} finally {
prof.stopCollecting();
profOpen = prof.getTop(3);
}
prof = new Profiler();
prof.startCollecting();
try {
JdbcUtils.closeSilently(conn);
} finally {
prof.stopCollecting();
profClose = prof.getTop(3);
}
long time = System.currentTimeMillis() - start;
String success;
if (time > 1000) {
success = "" +
"${text.login.testSuccessful} " +
" " +
PageParser.escapeHtml(profOpen) +
" " +
PageParser.escapeHtml(profClose) +
" ";
} else {
success = "${text.login.testSuccessful}
";
}
session.put("error", success);
// session.put("error", "${text.login.testSuccessful}");
return "login.jsp";
} catch (Exception e) {
session.put("error", getLoginError(e, isH2));
return "login.jsp";
}
}
/**
* Get the formatted login error message.
*
* @param e the exception
* @param isH2 if the current database is a H2 database
* @return the formatted error message
*/
private String getLoginError(Exception e, boolean isH2) {
if (e instanceof JdbcException && ((JdbcException) e).getErrorCode() == ErrorCode.CLASS_NOT_FOUND_1) {
return "${text.login.driverNotFound} " + getStackTrace(0, e, isH2);
}
return getStackTrace(0, e, isH2);
}
private String login(NetworkConnectionInfo networkConnectionInfo) {
String driver = attributes.getProperty("driver", "");
String url = attributes.getProperty("url", "");
String user = attributes.getProperty("user", "");
String password = attributes.getProperty("password", "");
session.put("autoCommit", "checked");
session.put("autoComplete", "1");
session.put("maxrows", "1000");
boolean isH2 = url.startsWith("jdbc:h2:");
try {
Connection conn = server.getConnection(driver, url, user, password, (String) session.get("key"),
networkConnectionInfo);
session.setConnection(conn);
session.put("url", url);
session.put("user", user);
session.remove("error");
settingSave();
return "frame.jsp";
} catch (Exception e) {
session.put("error", getLoginError(e, isH2));
return "login.jsp";
}
}
private String logout() {
try {
Connection conn = session.getConnection();
session.setConnection(null);
session.remove("conn");
session.remove("result");
session.remove("tables");
session.remove("user");
session.remove("tool");
if (conn != null) {
if (session.getShutdownServerOnDisconnect()) {
server.shutdown();
} else {
conn.close();
}
}
} catch (Exception e) {
trace(e.toString());
}
session.remove("admin");
return "index.do";
}
private String query() {
String sql = attributes.getProperty("sql").trim();
try {
ScriptReader r = new ScriptReader(new StringReader(sql));
final ArrayList list = new ArrayList<>();
while (true) {
String s = r.readStatement();
if (s == null) {
break;
}
list.add(s);
}
final Connection conn = session.getConnection();
if (SysProperties.CONSOLE_STREAM && server.getAllowChunked()) {
String page = new String(server.getFile("result.jsp"), StandardCharsets.UTF_8);
int idx = page.indexOf("${result}");
// the first element of the list is the header, the last the
// footer
list.add(0, page.substring(0, idx));
list.add(page.substring(idx + "${result}".length()));
session.put("chunks", new Iterator() {
private int i;
@Override
public boolean hasNext() {
return i < list.size();
}
@Override
public String next() {
String s = list.get(i++);
if (i == 1 || i == list.size()) {
return s;
}
StringBuilder b = new StringBuilder();
query(conn, s, i - 1, list.size() - 2, b);
return b.toString();
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
});
return "result.jsp";
}
String result;
StringBuilder buff = new StringBuilder();
for (int i = 0; i < list.size(); i++) {
String s = list.get(i);
query(conn, s, i, list.size(), buff);
}
result = buff.toString();
session.put("result", result);
} catch (Throwable e) {
session.put("result", getStackTrace(0, e, session.getContents().isH2()));
}
return "result.jsp";
}
/**
* Execute a query and append the result to the buffer.
*
* @param conn the connection
* @param s the statement
* @param i the index
* @param size the number of statements
* @param buff the target buffer
*/
void query(Connection conn, String s, int i, int size, StringBuilder buff) {
if (!(s.startsWith("@") && s.endsWith("."))) {
buff.append(PageParser.escapeHtml(s + ";")).append(" ");
}
boolean forceEdit = s.startsWith("@edit");
buff.append(getResult(conn, i + 1, s, size == 1, forceEdit)).
append(" ");
}
private String editResult() {
ResultSet rs = session.result;
int row = Integer.parseInt(attributes.getProperty("row"));
int op = Integer.parseInt(attributes.getProperty("op"));
String result = "", error = "";
try {
if (op == 1) {
boolean insert = row < 0;
if (insert) {
rs.moveToInsertRow();
} else {
rs.absolute(row);
}
for (int i = 0; i < rs.getMetaData().getColumnCount(); i++) {
String x = attributes.getProperty("r" + row + "c" + (i + 1));
unescapeData(x, rs, i + 1);
}
if (insert) {
rs.insertRow();
} else {
rs.updateRow();
}
} else if (op == 2) {
rs.absolute(row);
rs.deleteRow();
} else if (op == 3) {
// cancel
}
} catch (Throwable e) {
result = " " + getStackTrace(0, e, session.getContents().isH2());
error = formatAsError(e.getMessage());
}
String sql = "@edit " + (String) session.get("resultSetSQL");
Connection conn = session.getConnection();
result = error + getResult(conn, -1, sql, true, true) + result;
session.put("result", result);
return "result.jsp";
}
private ResultSet getMetaResultSet(Connection conn, String sql)
throws SQLException {
DatabaseMetaData meta = conn.getMetaData();
if (isBuiltIn(sql, "@best_row_identifier")) {
String[] p = split(sql);
int scale = p[4] == null ? 0 : Integer.parseInt(p[4]);
boolean nullable = Boolean.parseBoolean(p[5]);
return meta.getBestRowIdentifier(p[1], p[2], p[3], scale, nullable);
} else if (isBuiltIn(sql, "@catalogs")) {
return meta.getCatalogs();
} else if (isBuiltIn(sql, "@columns")) {
String[] p = split(sql);
return meta.getColumns(p[1], p[2], p[3], p[4]);
} else if (isBuiltIn(sql, "@column_privileges")) {
String[] p = split(sql);
return meta.getColumnPrivileges(p[1], p[2], p[3], p[4]);
} else if (isBuiltIn(sql, "@cross_references")) {
String[] p = split(sql);
return meta.getCrossReference(p[1], p[2], p[3], p[4], p[5], p[6]);
} else if (isBuiltIn(sql, "@exported_keys")) {
String[] p = split(sql);
return meta.getExportedKeys(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@imported_keys")) {
String[] p = split(sql);
return meta.getImportedKeys(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@index_info")) {
String[] p = split(sql);
boolean unique = Boolean.parseBoolean(p[4]);
boolean approx = Boolean.parseBoolean(p[5]);
return meta.getIndexInfo(p[1], p[2], p[3], unique, approx);
} else if (isBuiltIn(sql, "@primary_keys")) {
String[] p = split(sql);
return meta.getPrimaryKeys(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@procedures")) {
String[] p = split(sql);
return meta.getProcedures(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@procedure_columns")) {
String[] p = split(sql);
return meta.getProcedureColumns(p[1], p[2], p[3], p[4]);
} else if (isBuiltIn(sql, "@schemas")) {
return meta.getSchemas();
} else if (isBuiltIn(sql, "@tables")) {
String[] p = split(sql);
String[] types = p[4] == null ? null : StringUtils.arraySplit(p[4], ',', false);
return meta.getTables(p[1], p[2], p[3], types);
} else if (isBuiltIn(sql, "@table_privileges")) {
String[] p = split(sql);
return meta.getTablePrivileges(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@table_types")) {
return meta.getTableTypes();
} else if (isBuiltIn(sql, "@type_info")) {
return meta.getTypeInfo();
} else if (isBuiltIn(sql, "@udts")) {
String[] p = split(sql);
int[] types;
if (p[4] == null) {
types = null;
} else {
String[] t = StringUtils.arraySplit(p[4], ',', false);
types = new int[t.length];
for (int i = 0; i < t.length; i++) {
types[i] = Integer.parseInt(t[i]);
}
}
return meta.getUDTs(p[1], p[2], p[3], types);
} else if (isBuiltIn(sql, "@version_columns")) {
String[] p = split(sql);
return meta.getVersionColumns(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@memory")) {
SimpleResultSet rs = new SimpleResultSet();
rs.addColumn("Type", Types.VARCHAR, 0, 0);
rs.addColumn("KB", Types.VARCHAR, 0, 0);
rs.addRow("Used Memory", Integer.toString(Utils.getMemoryUsed()));
rs.addRow("Free Memory", Integer.toString(Utils.getMemoryFree()));
return rs;
} else if (isBuiltIn(sql, "@info")) {
SimpleResultSet rs = new SimpleResultSet();
rs.addColumn("KEY", Types.VARCHAR, 0, 0);
rs.addColumn("VALUE", Types.VARCHAR, 0, 0);
rs.addRow("conn.getCatalog", conn.getCatalog());
rs.addRow("conn.getAutoCommit", Boolean.toString(conn.getAutoCommit()));
rs.addRow("conn.getTransactionIsolation", Integer.toString(conn.getTransactionIsolation()));
rs.addRow("conn.getWarnings", String.valueOf(conn.getWarnings()));
String map;
try {
map = String.valueOf(conn.getTypeMap());
} catch (SQLException e) {
map = e.toString();
}
rs.addRow("conn.getTypeMap", map);
rs.addRow("conn.isReadOnly", Boolean.toString(conn.isReadOnly()));
rs.addRow("conn.getHoldability", Integer.toString(conn.getHoldability()));
addDatabaseMetaData(rs, meta);
return rs;
} else if (isBuiltIn(sql, "@attributes")) {
String[] p = split(sql);
return meta.getAttributes(p[1], p[2], p[3], p[4]);
} else if (isBuiltIn(sql, "@super_tables")) {
String[] p = split(sql);
return meta.getSuperTables(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@super_types")) {
String[] p = split(sql);
return meta.getSuperTypes(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@prof_stop")) {
if (profiler != null) {
profiler.stopCollecting();
SimpleResultSet rs = new SimpleResultSet();
rs.addColumn("Top Stack Trace(s)", Types.VARCHAR, 0, 0);
rs.addRow(profiler.getTop(3));
profiler = null;
return rs;
}
}
return null;
}
private static void addDatabaseMetaData(SimpleResultSet rs,
DatabaseMetaData meta) {
Method[] methods = DatabaseMetaData.class.getDeclaredMethods();
Arrays.sort(methods, new Comparator() {
@Override
public int compare(Method o1, Method o2) {
return o1.toString().compareTo(o2.toString());
}
});
for (Method m : methods) {
if (m.getParameterTypes().length == 0) {
try {
Object o = m.invoke(meta);
rs.addRow("meta." + m.getName(), String.valueOf(o));
} catch (InvocationTargetException e) {
rs.addRow("meta." + m.getName(), e.getTargetException().toString());
} catch (Exception e) {
rs.addRow("meta." + m.getName(), e.toString());
}
}
}
}
private static String[] split(String s) {
String[] list = new String[10];
String[] t = StringUtils.arraySplit(s, ' ', true);
System.arraycopy(t, 0, list, 0, t.length);
for (int i = 0; i < list.length; i++) {
if ("null".equals(list[i])) {
list[i] = null;
}
}
return list;
}
private int getMaxrows() {
String r = (String) session.get("maxrows");
return r == null ? 0 : Integer.parseInt(r);
}
private String getResult(Connection conn, int id, String sql,
boolean allowEdit, boolean forceEdit) {
try {
sql = sql.trim();
StringBuilder buff = new StringBuilder();
String sqlUpper = StringUtils.toUpperEnglish(sql);
if (sqlUpper.contains("CREATE") ||
sqlUpper.contains("DROP") ||
sqlUpper.contains("ALTER") ||
sqlUpper.contains("RUNSCRIPT")) {
String sessionId = attributes.getProperty("jsessionid");
buff.append("");
}
Statement stat;
DbContents contents = session.getContents();
if (forceEdit || (allowEdit && contents.isH2())) {
stat = conn.createStatement(
ResultSet.TYPE_SCROLL_INSENSITIVE,
ResultSet.CONCUR_UPDATABLE);
} else {
stat = conn.createStatement();
}
ResultSet rs;
long time = System.currentTimeMillis();
boolean metadata = false;
Object generatedKeys = null;
boolean edit = false;
boolean list = false;
if (isBuiltIn(sql, "@autocommit_true")) {
conn.setAutoCommit(true);
return "${text.result.autoCommitOn}";
} else if (isBuiltIn(sql, "@autocommit_false")) {
conn.setAutoCommit(false);
return "${text.result.autoCommitOff}";
} else if (isBuiltIn(sql, "@cancel")) {
stat = session.executingStatement;
if (stat != null) {
stat.cancel();
buff.append("${text.result.statementWasCanceled}");
} else {
buff.append("${text.result.noRunningStatement}");
}
return buff.toString();
} else if (isBuiltIn(sql, "@edit")) {
edit = true;
sql = StringUtils.trimSubstring(sql, "@edit".length());
session.put("resultSetSQL", sql);
}
if (isBuiltIn(sql, "@list")) {
list = true;
sql = StringUtils.trimSubstring(sql, "@list".length());
}
if (isBuiltIn(sql, "@meta")) {
metadata = true;
sql = StringUtils.trimSubstring(sql, "@meta".length());
}
if (isBuiltIn(sql, "@generated")) {
generatedKeys = true;
int offset = "@generated".length();
int length = sql.length();
for (; offset < length; offset++) {
char c = sql.charAt(offset);
if (c == '(') {
Parser p = new Parser();
generatedKeys = p.parseColumnList(sql, offset);
offset = p.getLastParseIndex();
break;
}
if (!Character.isWhitespace(c)) {
break;
}
}
sql = StringUtils.trimSubstring(sql, offset);
} else if (isBuiltIn(sql, "@history")) {
buff.append(getCommandHistoryString());
return buff.toString();
} else if (isBuiltIn(sql, "@loop")) {
sql = StringUtils.trimSubstring(sql, "@loop".length());
int idx = sql.indexOf(' ');
int count = Integer.decode(sql.substring(0, idx));
sql = StringUtils.trimSubstring(sql, idx);
return executeLoop(conn, count, sql);
} else if (isBuiltIn(sql, "@maxrows")) {
int maxrows = (int) Double.parseDouble(StringUtils.trimSubstring(sql, "@maxrows".length()));
session.put("maxrows", Integer.toString(maxrows));
return "${text.result.maxrowsSet}";
} else if (isBuiltIn(sql, "@parameter_meta")) {
sql = StringUtils.trimSubstring(sql, "@parameter_meta".length());
PreparedStatement prep = conn.prepareStatement(sql);
buff.append(getParameterResultSet(prep.getParameterMetaData()));
return buff.toString();
} else if (isBuiltIn(sql, "@password_hash")) {
sql = StringUtils.trimSubstring(sql, "@password_hash".length());
String[] p = split(sql);
return StringUtils.convertBytesToHex(
SHA256.getKeyPasswordHash(p[0], p[1].toCharArray()));
} else if (isBuiltIn(sql, "@prof_start")) {
if (profiler != null) {
profiler.stopCollecting();
}
profiler = new Profiler();
profiler.startCollecting();
return "Ok";
} else if (isBuiltIn(sql, "@sleep")) {
String s = StringUtils.trimSubstring(sql, "@sleep".length());
int sleep = 1;
if (s.length() > 0) {
sleep = Integer.parseInt(s);
}
Thread.sleep(sleep * 1000);
return "Ok";
} else if (isBuiltIn(sql, "@transaction_isolation")) {
String s = StringUtils.trimSubstring(sql, "@transaction_isolation".length());
if (s.length() > 0) {
int level = Integer.parseInt(s);
conn.setTransactionIsolation(level);
}
buff.append("Transaction Isolation: ")
.append(conn.getTransactionIsolation())
.append(" ");
buff.append(Connection.TRANSACTION_READ_UNCOMMITTED)
.append(": read_uncommitted ");
buff.append(Connection.TRANSACTION_READ_COMMITTED)
.append(": read_committed ");
buff.append(Connection.TRANSACTION_REPEATABLE_READ)
.append(": repeatable_read ");
buff.append(Constants.TRANSACTION_SNAPSHOT)
.append(": snapshot ");
buff.append(Connection.TRANSACTION_SERIALIZABLE)
.append(": serializable");
}
if (sql.startsWith("@")) {
rs = getMetaResultSet(conn, sql);
if (rs == null) {
buff.append("?: ").append(sql);
return buff.toString();
}
} else {
int maxrows = getMaxrows();
stat.setMaxRows(maxrows);
session.executingStatement = stat;
boolean isResultSet;
if (generatedKeys == null) {
isResultSet = stat.execute(sql);
} else if (generatedKeys instanceof Boolean) {
isResultSet = stat.execute(sql,
((Boolean) generatedKeys) ? Statement.RETURN_GENERATED_KEYS : Statement.NO_GENERATED_KEYS);
} else if (generatedKeys instanceof String[]) {
isResultSet = stat.execute(sql, (String[]) generatedKeys);
} else {
isResultSet = stat.execute(sql, (int[]) generatedKeys);
}
session.addCommand(sql);
if (generatedKeys != null) {
rs = null;
rs = stat.getGeneratedKeys();
} else {
if (!isResultSet) {
buff.append("${text.result.updateCount}: ")
.append(stat.getUpdateCount());
time = System.currentTimeMillis() - time;
buff.append(" (").append(time).append(" ms)");
stat.close();
return buff.toString();
}
rs = stat.getResultSet();
}
}
time = System.currentTimeMillis() - time;
buff.append(getResultSet(sql, rs, metadata, list, edit, time, allowEdit));
// SQLWarning warning = stat.getWarnings();
// if (warning != null) {
// buff.append(" Warning: ").
// append(getStackTrace(id, warning));
// }
if (!edit) {
stat.close();
}
return buff.toString();
} catch (Throwable e) {
// throwable: including OutOfMemoryError and so on
return getStackTrace(id, e, session.getContents().isH2());
} finally {
session.executingStatement = null;
}
}
private static boolean isBuiltIn(String sql, String builtIn) {
return sql.regionMatches(true, 0, builtIn, 0, builtIn.length());
}
private String executeLoop(Connection conn, int count, String sql)
throws SQLException {
ArrayList params = new ArrayList<>();
int idx = 0;
while (!stop) {
idx = sql.indexOf('?', idx);
if (idx < 0) {
break;
}
if (isBuiltIn(sql.substring(idx), "?/*rnd*/")) {
params.add(1);
sql = sql.substring(0, idx) + "?" + sql.substring(idx + "/*rnd*/".length() + 1);
} else {
params.add(0);
}
idx++;
}
boolean prepared;
Random random = new Random(1);
long time = System.currentTimeMillis();
if (isBuiltIn(sql, "@statement")) {
sql = StringUtils.trimSubstring(sql, "@statement".length());
prepared = false;
Statement stat = conn.createStatement();
for (int i = 0; !stop && i < count; i++) {
String s = sql;
for (Integer type : params) {
idx = s.indexOf('?');
if (type == 1) {
s = s.substring(0, idx) + random.nextInt(count) + s.substring(idx + 1);
} else {
s = s.substring(0, idx) + i + s.substring(idx + 1);
}
}
if (stat.execute(s)) {
ResultSet rs = stat.getResultSet();
while (!stop && rs.next()) {
// maybe get the data as well
}
rs.close();
}
}
} else {
prepared = true;
PreparedStatement prep = conn.prepareStatement(sql);
for (int i = 0; !stop && i < count; i++) {
for (int j = 0; j < params.size(); j++) {
Integer type = params.get(j);
if (type == 1) {
prep.setInt(j + 1, random.nextInt(count));
} else {
prep.setInt(j + 1, i);
}
}
if (session.getContents().isSQLite()) {
// SQLite currently throws an exception on prep.execute()
prep.executeUpdate();
} else {
if (prep.execute()) {
ResultSet rs = prep.getResultSet();
while (!stop && rs.next()) {
// maybe get the data as well
}
rs.close();
}
}
}
}
time = System.currentTimeMillis() - time;
StringBuilder builder = new StringBuilder().append(time).append(" ms: ").append(count).append(" * ")
.append(prepared ? "(Prepared) " : "(Statement) ").append('(');
for (int i = 0, size = params.size(); i < size; i++) {
if (i > 0) {
builder.append(", ");
}
builder.append(params.get(i) == 0 ? "i" : "rnd");
}
return builder.append(") ").append(sql).toString();
}
private String getCommandHistoryString() {
StringBuilder buff = new StringBuilder();
ArrayList history = session.getCommandHistory();
buff.append("" +
"Command ");
for (int i = history.size() - 1; i >= 0; i--) {
String sql = history.get(i);
buff.append("").
append(" ").
append("").
append(PageParser.escapeHtml(sql)).
append(" ");
}
buff.append("
");
return buff.toString();
}
private static String getParameterResultSet(ParameterMetaData meta)
throws SQLException {
StringBuilder buff = new StringBuilder();
if (meta == null) {
return "No parameter meta data";
}
buff.append("").
append("className mode type ").
append("typeName precision scale ");
for (int i = 0; i < meta.getParameterCount(); i++) {
buff.append("").
append(meta.getParameterClassName(i + 1)).
append(" ").
append(meta.getParameterMode(i + 1)).
append(" ").
append(meta.getParameterType(i + 1)).
append(" ").
append(meta.getParameterTypeName(i + 1)).
append(" ").
append(meta.getPrecision(i + 1)).
append(" ").
append(meta.getScale(i + 1)).
append(" ");
}
buff.append("
");
return buff.toString();
}
private String getResultSet(String sql, ResultSet rs, boolean metadata,
boolean list, boolean edit, long time, boolean allowEdit)
throws SQLException {
int maxrows = getMaxrows();
time = System.currentTimeMillis() - time;
StringBuilder buff = new StringBuilder();
if (edit) {
buff.append("