提交 aaba8d61 authored 作者: Thomas Mueller's avatar Thomas Mueller

--no commit message

--no commit message
上级 624fa6ba
...@@ -637,7 +637,7 @@ SQLインジェクションとは ...@@ -637,7 +637,7 @@ SQLインジェクションとは
このデータベースは、ユーザー入力をデータベースに通す時、パラメータの使用を強制する方法を提供しています。 SQLステートメントの組み込まれたリテラルを無効にすることでこれを実行します。 次のステートメントを実行します: このデータベースは、ユーザー入力をデータベースに通す時、パラメータの使用を強制する方法を提供しています。 SQLステートメントの組み込まれたリテラルを無効にすることでこれを実行します。 次のステートメントを実行します:
@advanced_1212_p @advanced_1212_p
その後、文字列リテラルや数値リテラルのSQLステートメントはもう認められません。これは、 WHERE NAME='abc' や WHERE CustomerId=10 といった形のSQLステートメントは失敗するという意味です。 PreparedStatementや上に記載されたパラメータは使用することができます。また、 リテラルの含まれないSQLステートメントと同様に、SQLステートメントを動的に生成したり、 APIステートメントを使用することも可能です。数値リテラルが許可されている二つ目のモードもあります: SET ALLOW_LITERALS NUMBERS 全てのリテラルを許可するには、 SET ALLOW_LITERALS ALL を実行します (これはデフォルトの設定です)。リテラルはadministratorのみによって使用可能、または使用不可になります。 #Afterwards, SQL statements with text and number literals are not allowed any more. That means, SQL statement of the form WHERE NAME='abc' or WHERE CustomerId=10 will fail. It is still possible to use PreparedStatements and parameters as described above. Also, it is still possible to generate SQL statements dynamically, and use the Statement API, as long as the SQL statements do not include literals. There is also a second mode where number literals are allowed: SET ALLOW_LITERALS NUMBERS. To allow all literals, execute SET ALLOW_LITERALS ALL (this is the default setting). Literals can only be enabled or disabled by an administrator.
@advanced_1213_h3 @advanced_1213_h3
定数を使用する 定数を使用する
...@@ -4846,7 +4846,7 @@ H2DRIVERSかCLASSPATHの環境変数に、ドライバのJarファイルの位 ...@@ -4846,7 +4846,7 @@ H2DRIVERSかCLASSPATHの環境変数に、ドライバのJarファイルの位
JDBCを使用してデータベースに接続 JDBCを使用してデータベースに接続
@tutorial_1065_p @tutorial_1065_p
データベースに接続するためにJavaアプリケーションに最初に必要なことは、 データベースドライバをロードし、接続することです。簡単な方法は、次のコードを使用します: #To connect to a database, a Java application first needs to load the database driver, and then get a connection. A simple way to do that is using the following code:
@tutorial_1066_p @tutorial_1066_p
このコードは最初にドライバをロードして (Class.forName())、 接続を開始します (DriverManager.getConnection())。 このドライバの名前は全てのケースにおいて "org.h2.Driver" です。 データベースに認識されるため、データベースのURLは常に jdbc:h2: から始まります。 getConnection() 内の2番目のパラメーターはユーザ名を指しています ('sa' はこの場合、システム管理者を表しています)。3番目のパラメーターはパスワードです。このデータベースでは、ユーザ名は大文字と小文字を区別していませんが、パスワードは大文字と小文字を区別しています。 このコードは最初にドライバをロードして (Class.forName())、 接続を開始します (DriverManager.getConnection())。 このドライバの名前は全てのケースにおいて "org.h2.Driver" です。 データベースに認識されるため、データベースのURLは常に jdbc:h2: から始まります。 getConnection() 内の2番目のパラメーターはユーザ名を指しています ('sa' はこの場合、システム管理者を表しています)。3番目のパラメーターはパスワードです。このデータベースでは、ユーザ名は大文字と小文字を区別していませんが、パスワードは大文字と小文字を区別しています。
......
...@@ -829,9 +829,10 @@ public class Parser { ...@@ -829,9 +829,10 @@ public class Parser {
private TableFilter readTableFilter(boolean fromOuter) throws SQLException { private TableFilter readTableFilter(boolean fromOuter) throws SQLException {
Table table; Table table;
String alias = null;
Schema mainSchema = database.getSchema(Constants.SCHEMA_MAIN); Schema mainSchema = database.getSchema(Constants.SCHEMA_MAIN);
if (readIf("(")) { if (readIf("(")) {
if (isToken("SELECT") || isToken("FROM")) { if (isToken("SELECT") || isToken("FROM") || isToken("(")) {
Query query = parseSelect(); Query query = parseSelect();
Session s; Session s;
if (prepared != null && prepared instanceof CreateView) { if (prepared != null && prepared instanceof CreateView) {
...@@ -840,6 +841,7 @@ public class Parser { ...@@ -840,6 +841,7 @@ public class Parser {
s = session; s = session;
} }
table = TableView.createTempView(s, session.getUser(), query); table = TableView.createTempView(s, session.getUser(), query);
alias = table.getName();
read(")"); read(")");
} else { } else {
TableFilter top = readTableFilter(fromOuter); TableFilter top = readTableFilter(fromOuter);
...@@ -869,7 +871,6 @@ public class Parser { ...@@ -869,7 +871,6 @@ public class Parser {
table = readTableOrView(tableName); table = readTableOrView(tableName);
} }
} }
String alias = null;
if (readIf("AS")) { if (readIf("AS")) {
alias = readAliasIdentifier(); alias = readAliasIdentifier();
} else if (currentTokenType == IDENTIFIER) { } else if (currentTokenType == IDENTIFIER) {
...@@ -1196,9 +1197,7 @@ public class Parser { ...@@ -1196,9 +1197,7 @@ public class Parser {
ExplainPlan command = new ExplainPlan(session); ExplainPlan command = new ExplainPlan(session);
readIf("PLAN"); readIf("PLAN");
readIf("FOR"); readIf("FOR");
if (isToken("SELECT") || isToken("FROM")) { if (isToken("SELECT") || isToken("FROM") || isToken("(")) {
command.setCommand(parseSelect());
} else if (isToken("(")) {
command.setCommand(parseSelect()); command.setCommand(parseSelect());
} else if (readIf("DELETE")) { } else if (readIf("DELETE")) {
command.setCommand(parseDelete()); command.setCommand(parseDelete());
......
...@@ -200,19 +200,24 @@ public class SessionRemote implements SessionInterface, DataHandler { ...@@ -200,19 +200,24 @@ public class SessionRemote implements SessionInterface, DataHandler {
transferList = new ObjectArray(); transferList = new ObjectArray();
// TODO cluster: support at most 2 connections // TODO cluster: support at most 2 connections
boolean switchOffCluster = false; boolean switchOffCluster = false;
for (int i = 0; i < len; i++) { try {
try { for (int i = 0; i < len; i++) {
Transfer trans = initTransfer(ci, databaseName, servers[i]); try {
transferList.add(trans); Transfer trans = initTransfer(ci, databaseName, servers[i]);
} catch (IOException e) { transferList.add(trans);
switchOffCluster = true; } catch (IOException e) {
switchOffCluster = true;
}
} }
checkClosed();
if (switchOffCluster) {
switchOffCluster();
}
switchOffAutoCommitIfCluster();
} catch (SQLException e) {
traceSystem.close();
throw e;
} }
checkClosed();
if (switchOffCluster) {
switchOffCluster();
}
switchOffAutoCommitIfCluster();
} }
private void switchOffCluster() throws SQLException { private void switchOffCluster() throws SQLException {
......
...@@ -31,110 +31,113 @@ import org.h2.value.DataType; ...@@ -31,110 +31,113 @@ import org.h2.value.DataType;
*/ */
public class FullText implements Trigger { public class FullText implements Trigger {
private static final String TRIGGER_PREFIX = "FT_"; private static final String TRIGGER_PREFIX = "FT_";
private static final String SCHEMA = "FT"; private static final String SCHEMA = "FT";
private static final String FIELD_QUERY = "query"; private static final String FIELD_QUERY = "QUERY";
private IndexInfo index; private IndexInfo index;
private int[] dataTypes; private int[] dataTypes;
private PreparedStatement prepInsertWord, prepInsertRow, prepInsertMap; private PreparedStatement prepInsertWord, prepInsertRow, prepInsertMap;
private PreparedStatement prepDeleteRow, prepDeleteMap; private PreparedStatement prepDeleteRow, prepDeleteMap;
private PreparedStatement prepSelectRow; private PreparedStatement prepSelectRow;
/** /**
* Create a new full text index for a table and column list. Each table may only have one index at any time. * Create a new full text index for a table and column list. Each table may
* * only have one index at any time.
* @param conn the connection *
* @param name the name of the index (must be unique) * @param conn the connection
* @param schema the schema name of the table * @param name the name of the index (must be unique)
* @param table the table name * @param schema the schema name of the table
* @param columnList the column list (null for all columns) * @param table the table name
*/ * @param columnList the column list (null for all columns)
public static void createIndex(Connection conn, String schema, String table, String columnList) throws SQLException { */
init(conn); public static void createIndex(Connection conn, String schema, String table, String columnList) throws SQLException {
PreparedStatement prep = conn.prepareStatement("INSERT INTO "+SCHEMA+".INDEXES(SCHEMA, TABLE, COLUMNS) VALUES(?, ?, ?)"); init(conn);
prep.setString(1, schema); PreparedStatement prep = conn.prepareStatement("INSERT INTO " + SCHEMA
prep.setString(2, table); + ".INDEXES(SCHEMA, TABLE, COLUMNS) VALUES(?, ?, ?)");
prep.setString(3, columnList); prep.setString(1, schema);
prep.execute(); prep.setString(2, table);
createTrigger(conn, schema, table); prep.setString(3, columnList);
indexExistingRows(conn, schema, table); prep.execute();
} createTrigger(conn, schema, table);
indexExistingRows(conn, schema, table);
}
private static void createTrigger(Connection conn, String schema, String table) throws SQLException { private static void createTrigger(Connection conn, String schema, String table) throws SQLException {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
String trigger = StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(TRIGGER_PREFIX + table); String trigger = StringUtils.quoteIdentifier(schema) + "."
stat.execute("DROP TRIGGER IF EXISTS " + trigger); + StringUtils.quoteIdentifier(TRIGGER_PREFIX + table);
StringBuffer buff = new StringBuffer("CREATE TRIGGER IF NOT EXISTS "); stat.execute("DROP TRIGGER IF EXISTS " + trigger);
buff.append(trigger); StringBuffer buff = new StringBuffer("CREATE TRIGGER IF NOT EXISTS ");
buff.append(" AFTER INSERT, UPDATE, DELETE ON "); buff.append(trigger);
buff.append(StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(table)); buff.append(" AFTER INSERT, UPDATE, DELETE ON ");
buff.append(" FOR EACH ROW CALL \""); buff.append(StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(table));
buff.append(FullText.class.getName()); buff.append(" FOR EACH ROW CALL \"");
buff.append("\""); buff.append(FullText.class.getName());
stat.execute(buff.toString()); buff.append("\"");
} stat.execute(buff.toString());
}
private static void indexExistingRows(Connection conn, String schema, String table) throws SQLException { private static void indexExistingRows(Connection conn, String schema, String table) throws SQLException {
FullText existing = new FullText(); FullText existing = new FullText();
existing.init(conn, schema, null, table); existing.init(conn, schema, null, table);
StringBuffer buff = new StringBuffer("SELECT * FROM "); StringBuffer buff = new StringBuffer("SELECT * FROM ");
buff.append(StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(table)); buff.append(StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(table));
ResultSet rs = conn.createStatement().executeQuery(buff.toString()); ResultSet rs = conn.createStatement().executeQuery(buff.toString());
int columnCount = rs.getMetaData().getColumnCount(); int columnCount = rs.getMetaData().getColumnCount();
while (rs.next()) { while (rs.next()) {
Object[] row = new Object[columnCount]; Object[] row = new Object[columnCount];
for (int i = 0; i < columnCount; i++) { for (int i = 0; i < columnCount; i++) {
row[i] = rs.getObject(i + 1); row[i] = rs.getObject(i + 1);
} }
existing.fire(conn, null, row); existing.fire(conn, null, row);
} }
} }
/** /**
* Re-creates the full text index for this database * Re-creates the full text index for this database
* *
* @param conn the connection * @param conn the connection
*/ */
public static void reindex(Connection conn) throws SQLException { public static void reindex(Connection conn) throws SQLException {
init(conn); init(conn);
removeAllTriggers(conn); removeAllTriggers(conn);
FullTextSettings setting = FullTextSettings.getInstance(conn); FullTextSettings setting = FullTextSettings.getInstance(conn);
setting.getWordList().clear(); setting.getWordList().clear();
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("TRUNCATE TABLE "+SCHEMA+".WORDS"); stat.execute("TRUNCATE TABLE " + SCHEMA + ".WORDS");
stat.execute("TRUNCATE TABLE "+SCHEMA+".ROWS"); stat.execute("TRUNCATE TABLE " + SCHEMA + ".ROWS");
stat.execute("TRUNCATE TABLE "+SCHEMA+".MAP"); stat.execute("TRUNCATE TABLE " + SCHEMA + ".MAP");
ResultSet rs = stat.executeQuery("SELECT * FROM " + SCHEMA + ".INDEXES"); ResultSet rs = stat.executeQuery("SELECT * FROM " + SCHEMA + ".INDEXES");
while (rs.next()) { while (rs.next()) {
String schema = rs.getString("SCHEMA"); String schema = rs.getString("SCHEMA");
String table = rs.getString("TABLE"); String table = rs.getString("TABLE");
createTrigger(conn, schema, table); createTrigger(conn, schema, table);
indexExistingRows(conn, schema, table); indexExistingRows(conn, schema, table);
} }
} }
/** /**
* Change the ignore list. The ignore list is a comma separated list of common words that must * Change the ignore list. The ignore list is a comma separated list of common words that must
* not be indexed. The default ignore list is empty. If indexes already exist at the time this list is changed, * not be indexed. The default ignore list is empty. If indexes already exist at the time this list is changed,
* reindex must be called. * reindex must be called.
* *
* @param conn the connection * @param conn the connection
* @param commaSeparatedList the list * @param commaSeparatedList the list
*/ */
public static void setIgnoreList(Connection conn, String commaSeparatedList) throws SQLException { public static void setIgnoreList(Connection conn, String commaSeparatedList) throws SQLException {
init(conn); init(conn);
FullTextSettings setting = FullTextSettings.getInstance(conn); FullTextSettings setting = FullTextSettings.getInstance(conn);
setIgnoreList(setting, commaSeparatedList); setIgnoreList(setting, commaSeparatedList);
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("TRUNCATE TABLE "+SCHEMA+".IGNORELIST"); stat.execute("TRUNCATE TABLE " + SCHEMA + ".IGNORELIST");
PreparedStatement prep = conn.prepareStatement("INSERT INTO "+SCHEMA+".IGNORELIST VALUES(?)"); PreparedStatement prep = conn.prepareStatement("INSERT INTO " + SCHEMA + ".IGNORELIST VALUES(?)");
prep.setString(1, commaSeparatedList); prep.setString(1, commaSeparatedList);
prep.execute(); prep.execute();
} }
private static void setIgnoreList(FullTextSettings setting, String commaSeparatedList) { private static void setIgnoreList(FullTextSettings setting, String commaSeparatedList) {
String[] list = StringUtils.arraySplit(commaSeparatedList, ',', true); String[] list = StringUtils.arraySplit(commaSeparatedList, ',', true);
HashSet set = setting.getIgnoreList(); HashSet set = setting.getIgnoreList();
for (int i = 0; i < list.length; i++) { for (int i = 0; i < list.length; i++) {
String word = list[i]; String word = list[i];
word = setting.convertWord(word); word = setting.convertWord(word);
...@@ -153,62 +156,62 @@ public class FullText implements Trigger { ...@@ -153,62 +156,62 @@ public class FullText implements Trigger {
String name = rs.getString("TRIGGER_NAME"); String name = rs.getString("TRIGGER_NAME");
if (name.startsWith(TRIGGER_PREFIX)) { if (name.startsWith(TRIGGER_PREFIX)) {
name = StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(name); name = StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(name);
stat2.execute("DROP TRIGGER " + name); stat2.execute("DROP TRIGGER " + name);
} }
} }
} }
/** /**
* Drops all full text indexes from the database. * Drops all full text indexes from the database.
* @param conn the connection * @param conn the connection
*/ */
public static void dropAll(Connection conn) throws SQLException { public static void dropAll(Connection conn) throws SQLException {
init(conn); init(conn);
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("DROP SCHEMA IF EXISTS " + SCHEMA); stat.execute("DROP SCHEMA IF EXISTS " + SCHEMA);
removeAllTriggers(conn); removeAllTriggers(conn);
FullTextSettings setting = FullTextSettings.getInstance(conn); FullTextSettings setting = FullTextSettings.getInstance(conn);
setting.getIgnoreList().clear(); setting.getIgnoreList().clear();
setting.getWordList().clear(); setting.getWordList().clear();
} }
/** /**
* Initializes full text search functionality for this database. * Initializes full text search functionality for this database.
* This adds the following Java functions to the database: * This adds the following Java functions to the database:
* <ul> * <ul>
* <li>FT_CREATE_INDEX(schemaNameString, tableNameString, columnListString) * <li>FT_CREATE_INDEX(schemaNameString, tableNameString, columnListString)
* </li><li>FT_SEARCH(queryString, limitInt, offsetInt): result set * </li><li>FT_SEARCH(queryString, limitInt, offsetInt): result set
* </li><li>FT_REINDEX() * </li><li>FT_REINDEX()
* </li><li>FT_DROP_ALL() * </li><li>FT_DROP_ALL()
* </li></ul> * </li></ul>
* It also adds a schema FULLTEXT to the database where bookkeeping information is stored. * It also adds a schema FULLTEXT to the database where bookkeeping information is stored.
* This function may be called from a Java application, or by using the SQL statements: * This function may be called from a Java application, or by using the SQL statements:
* <pre> * <pre>
* CREATE ALIAS IF NOT EXISTS FULLTEXT_INIT FOR "org.h2.fulltext.FullText.init"; * CREATE ALIAS IF NOT EXISTS FULLTEXT_INIT FOR "org.h2.fulltext.FullText.init";
* CALL FULLTEXT_INIT(); * CALL FULLTEXT_INIT();
* </pre> * </pre>
* *
* @param conn * @param conn the connection
*/ */
public static void init(Connection conn) throws SQLException { public static void init(Connection conn) throws SQLException {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("CREATE SCHEMA IF NOT EXISTS " + SCHEMA); stat.execute("CREATE SCHEMA IF NOT EXISTS " + SCHEMA);
stat.execute("CREATE TABLE IF NOT EXISTS "+SCHEMA+".INDEXES(ID INT AUTO_INCREMENT PRIMARY KEY, SCHEMA VARCHAR, TABLE VARCHAR, COLUMNS VARCHAR, UNIQUE(SCHEMA, TABLE))"); stat.execute("CREATE TABLE IF NOT EXISTS " + SCHEMA
stat.execute("CREATE MEMORY TABLE IF NOT EXISTS "+SCHEMA+".WORDS(ID INT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR, UNIQUE(NAME))"); + ".INDEXES(ID INT AUTO_INCREMENT PRIMARY KEY, SCHEMA VARCHAR, TABLE VARCHAR, COLUMNS VARCHAR, UNIQUE(SCHEMA, TABLE))");
stat.execute("CREATE TABLE IF NOT EXISTS "+SCHEMA+".ROWS(ID IDENTITY, HASH INT, INDEXID INT, KEY VARCHAR, UNIQUE(HASH, INDEXID, KEY))"); stat.execute("CREATE MEMORY TABLE IF NOT EXISTS " + SCHEMA
+ ".WORDS(ID INT AUTO_INCREMENT PRIMARY KEY, NAME VARCHAR, UNIQUE(NAME))");
// 3391, 1484 stat.execute("CREATE TABLE IF NOT EXISTS " + SCHEMA
// stat.execute("CREATE TABLE IF NOT EXISTS "+SCHEMA+".MAP(ROWID INT, WORDID INT, UNIQUE(ROWID, WORDID), UNIQUE(WORDID, ROWID))"); + ".ROWS(ID IDENTITY, HASH INT, INDEXID INT, KEY VARCHAR, UNIQUE(HASH, INDEXID, KEY))");
// 3063, 1484 stat.execute("CREATE TABLE IF NOT EXISTS " + SCHEMA
stat.execute("CREATE TABLE IF NOT EXISTS "+SCHEMA+".MAP(ROWID INT, WORDID INT, PRIMARY KEY(WORDID, ROWID))"); + ".MAP(ROWID INT, WORDID INT, PRIMARY KEY(WORDID, ROWID))");
stat.execute("CREATE TABLE IF NOT EXISTS "+SCHEMA+".IGNORELIST(LIST VARCHAR)"); stat.execute("CREATE TABLE IF NOT EXISTS " + SCHEMA + ".IGNORELIST(LIST VARCHAR)");
stat.execute("CREATE ALIAS IF NOT EXISTS FT_CREATE_INDEX FOR \"" + FullText.class.getName()+".createIndex\""); stat.execute("CREATE ALIAS IF NOT EXISTS FT_CREATE_INDEX FOR \"" + FullText.class.getName() + ".createIndex\"");
stat.execute("CREATE ALIAS IF NOT EXISTS FT_SEARCH FOR \"" + FullText.class.getName()+".search\""); stat.execute("CREATE ALIAS IF NOT EXISTS FT_SEARCH FOR \"" + FullText.class.getName() + ".search\"");
stat.execute("CREATE ALIAS IF NOT EXISTS FT_REINDEX FOR \"" + FullText.class.getName()+".reindex\""); stat.execute("CREATE ALIAS IF NOT EXISTS FT_REINDEX FOR \"" + FullText.class.getName() + ".reindex\"");
stat.execute("CREATE ALIAS IF NOT EXISTS FT_DROP_ALL FOR \"" + FullText.class.getName()+".dropAll\""); stat.execute("CREATE ALIAS IF NOT EXISTS FT_DROP_ALL FOR \"" + FullText.class.getName() + ".dropAll\"");
FullTextSettings setting = FullTextSettings.getInstance(conn); FullTextSettings setting = FullTextSettings.getInstance(conn);
ResultSet rs = stat.executeQuery("SELECT * FROM " + SCHEMA + ".IGNORELIST"); ResultSet rs = stat.executeQuery("SELECT * FROM " + SCHEMA + ".IGNORELIST");
while (rs.next()) { while (rs.next()) {
String commaSeparatedList = rs.getString(1); String commaSeparatedList = rs.getString(1);
...@@ -218,23 +221,23 @@ public class FullText implements Trigger { ...@@ -218,23 +221,23 @@ public class FullText implements Trigger {
HashMap map = setting.getWordList(); HashMap map = setting.getWordList();
while (rs.next()) { while (rs.next()) {
String word = rs.getString("NAME"); String word = rs.getString("NAME");
long id = rs.getLong("ID"); int id = rs.getInt("ID");
word = setting.convertWord(word); word = setting.convertWord(word);
if (word != null) { if (word != null) {
map.put(word, ObjectUtils.getLong(id)); map.put(word, ObjectUtils.getInteger(id));
} }
} }
} }
/** /**
* INTERNAL * INTERNAL
*/ */
public void init(Connection conn, String schemaName, String triggerName, String tableName) throws SQLException { public void init(Connection conn, String schemaName, String triggerName, String tableName) throws SQLException {
init(conn); init(conn);
FullTextSettings setting = FullTextSettings.getInstance(conn); FullTextSettings setting = FullTextSettings.getInstance(conn);
ArrayList keyList = new ArrayList(); ArrayList keyList = new ArrayList();
DatabaseMetaData meta = conn.getMetaData(); DatabaseMetaData meta = conn.getMetaData();
ResultSet rs = meta.getColumns(null, schemaName, tableName, null); ResultSet rs = meta.getColumns(null, schemaName, tableName, null);
ArrayList columnList = new ArrayList(); ArrayList columnList = new ArrayList();
while (rs.next()) { while (rs.next()) {
columnList.add(rs.getString("COLUMN_NAME")); columnList.add(rs.getString("COLUMN_NAME"));
...@@ -259,8 +262,8 @@ public class FullText implements Trigger { ...@@ -259,8 +262,8 @@ public class FullText implements Trigger {
throw new SQLException("No primary key for table " + tableName); throw new SQLException("No primary key for table " + tableName);
} }
ArrayList indexList = new ArrayList(); ArrayList indexList = new ArrayList();
PreparedStatement prep = conn.prepareStatement("SELECT ID, COLUMNS FROM " + SCHEMA PreparedStatement prep = conn.prepareStatement(
+ ".INDEXES WHERE SCHEMA=? AND TABLE=?"); "SELECT ID, COLUMNS FROM " + SCHEMA + ".INDEXES WHERE SCHEMA=? AND TABLE=?");
prep.setString(1, schemaName); prep.setString(1, schemaName);
prep.setString(2, tableName); prep.setString(2, tableName);
rs = prep.executeQuery(); rs = prep.executeQuery();
...@@ -281,21 +284,29 @@ public class FullText implements Trigger { ...@@ -281,21 +284,29 @@ public class FullText implements Trigger {
setColumns(index.keys, keyList, columnList); setColumns(index.keys, keyList, columnList);
index.indexColumns = new int[indexList.size()]; index.indexColumns = new int[indexList.size()];
setColumns(index.indexColumns, indexList, columnList); setColumns(index.indexColumns, indexList, columnList);
setting.addIndexInfo(index); setting.addIndexInfo(index);
prepInsertWord = conn.prepareStatement("INSERT INTO "+SCHEMA+".WORDS(NAME) VALUES(?)"); prepInsertWord = conn.prepareStatement(
prepInsertRow = conn.prepareStatement("INSERT INTO "+SCHEMA+".ROWS(HASH, INDEXID, KEY) VALUES(?, ?, ?)"); "INSERT INTO " + SCHEMA + ".WORDS(NAME) VALUES(?)");
prepInsertMap = conn.prepareStatement("INSERT INTO "+SCHEMA+".MAP(ROWID, WORDID) VALUES(?, ?)"); prepInsertRow = conn.prepareStatement(
prepDeleteRow = conn.prepareStatement("DELETE FROM "+SCHEMA+".ROWS WHERE HASH=? AND INDEXID=? AND KEY=?"); "INSERT INTO " + SCHEMA + ".ROWS(HASH, INDEXID, KEY) VALUES(?, ?, ?)");
prepDeleteMap = conn.prepareStatement("DELETE FROM "+SCHEMA+".MAP WHERE ROWID=? AND WORDID=?"); prepInsertMap = conn.prepareStatement(
prepSelectRow = conn.prepareStatement("SELECT ID FROM "+SCHEMA+".ROWS WHERE HASH=? AND INDEXID=? AND KEY=?"); "INSERT INTO " + SCHEMA + ".MAP(ROWID, WORDID) VALUES(?, ?)");
prepDeleteRow = conn.prepareStatement(
"DELETE FROM " + SCHEMA + ".ROWS WHERE HASH=? AND INDEXID=? AND KEY=?");
prepDeleteMap = conn.prepareStatement(
"DELETE FROM " + SCHEMA + ".MAP WHERE ROWID=? AND WORDID=?");
prepSelectRow = conn.prepareStatement(
"SELECT ID FROM " + SCHEMA + ".ROWS WHERE HASH=? AND INDEXID=? AND KEY=?");
PreparedStatement prepSelectMapByWordId = conn.prepareStatement("SELECT ROWID FROM "+SCHEMA+".MAP WHERE WORDID=?"); PreparedStatement prepSelectMapByWordId = conn.prepareStatement(
PreparedStatement prepSelectRowById = conn.prepareStatement("SELECT KEY, INDEXID FROM "+SCHEMA+".ROWS WHERE ID=?"); "SELECT ROWID FROM " + SCHEMA + ".MAP WHERE WORDID=?");
setting.setPrepSelectMapByWordId(prepSelectMapByWordId); PreparedStatement prepSelectRowById = conn.prepareStatement(
setting.setPrepSelectRowById(prepSelectRowById); "SELECT KEY, INDEXID FROM " + SCHEMA + ".ROWS WHERE ID=?");
} setting.setPrepSelectMapByWordId(prepSelectMapByWordId);
setting.setPrepSelectRowById(prepSelectRowById);
}
private void setColumns(int[] index, ArrayList keys, ArrayList columns) throws SQLException { private void setColumns(int[] index, ArrayList keys, ArrayList columns) throws SQLException {
for (int i = 0; i < keys.size(); i++) { for (int i = 0; i < keys.size(); i++) {
String key = (String) keys.get(i); String key = (String) keys.get(i);
int found = -1; int found = -1;
...@@ -312,10 +323,10 @@ public class FullText implements Trigger { ...@@ -312,10 +323,10 @@ public class FullText implements Trigger {
} }
} }
/** /**
* INTERNAL * INTERNAL
*/ */
public void fire(Connection conn, Object[] oldRow, Object[] newRow) throws SQLException { public void fire(Connection conn, Object[] oldRow, Object[] newRow) throws SQLException {
FullTextSettings setting = FullTextSettings.getInstance(conn); FullTextSettings setting = FullTextSettings.getInstance(conn);
if (oldRow != null) { if (oldRow != null) {
delete(setting, oldRow); delete(setting, oldRow);
...@@ -329,23 +340,23 @@ public class FullText implements Trigger { ...@@ -329,23 +340,23 @@ public class FullText implements Trigger {
StringBuffer buff = new StringBuffer(); StringBuffer buff = new StringBuffer();
for (int i = 0; i < index.keys.length; i++) { for (int i = 0; i < index.keys.length; i++) {
if (i > 0) { if (i > 0) {
buff.append(" AND "); buff.append(" AND ");
} }
int columnIndex = index.keys[i]; int columnIndex = index.keys[i];
buff.append(StringUtils.quoteIdentifier(index.columnNames[columnIndex])); buff.append(StringUtils.quoteIdentifier(index.columnNames[columnIndex]));
Object o = row[columnIndex]; Object o = row[columnIndex];
if (o == null) { if (o == null) {
buff.append(" IS NULL"); buff.append(" IS NULL");
} else { } else {
buff.append("="); buff.append("=");
buff.append(quoteSQL(o, dataTypes[columnIndex])); buff.append(quoteSQL(o, dataTypes[columnIndex]));
} }
} }
String key = buff.toString(); String key = buff.toString();
return key; return key;
} }
private String quoteString(String data) { private String quoteString(String data) {
if (data.indexOf('\'') < 0) { if (data.indexOf('\'') < 0) {
return "'" + data + "'"; return "'" + data + "'";
} }
...@@ -356,17 +367,17 @@ public class FullText implements Trigger { ...@@ -356,17 +367,17 @@ public class FullText implements Trigger {
if (ch == '\'') { if (ch == '\'') {
buff.append(ch); buff.append(ch);
} }
buff.append(ch); buff.append(ch);
} }
buff.append('\''); buff.append('\'');
return buff.toString(); return buff.toString();
} }
private String quoteBinary(byte[] data) { private String quoteBinary(byte[] data) {
return "'" + ByteUtils.convertBytesToString(data) + "'"; return "'" + ByteUtils.convertBytesToString(data) + "'";
} }
private String asString(Object data, int type) throws SQLException { private String asString(Object data, int type) throws SQLException {
if (data == null) { if (data == null) {
return "NULL"; return "NULL";
} }
...@@ -376,94 +387,94 @@ public class FullText implements Trigger { ...@@ -376,94 +387,94 @@ public class FullText implements Trigger {
case Types.INTEGER: case Types.INTEGER:
case Types.BIGINT: case Types.BIGINT:
case Types.DECIMAL: case Types.DECIMAL:
case Types.DOUBLE: case Types.DOUBLE:
case Types.FLOAT: case Types.FLOAT:
case Types.NUMERIC: case Types.NUMERIC:
case Types.REAL: case Types.REAL:
case Types.SMALLINT: case Types.SMALLINT:
case Types.TINYINT: case Types.TINYINT:
case Types.DATE: case Types.DATE:
case Types.TIME: case Types.TIME:
case Types.TIMESTAMP: case Types.TIMESTAMP:
case Types.LONGVARCHAR: case Types.LONGVARCHAR:
case Types.CHAR: case Types.CHAR:
case Types.VARCHAR: case Types.VARCHAR:
return data.toString(); return data.toString();
case Types.VARBINARY: case Types.VARBINARY:
case Types.LONGVARBINARY: case Types.LONGVARBINARY:
case Types.BINARY: case Types.BINARY:
case Types.JAVA_OBJECT: case Types.JAVA_OBJECT:
case Types.CLOB: case Types.CLOB:
case Types.OTHER: case Types.OTHER:
case Types.BLOB: case Types.BLOB:
case Types.STRUCT: case Types.STRUCT:
case Types.REF: case Types.REF:
case Types.NULL: case Types.NULL:
case Types.ARRAY: case Types.ARRAY:
case DataType.TYPE_DATALINK: case DataType.TYPE_DATALINK:
case Types.DISTINCT: case Types.DISTINCT:
throw new SQLException("FULLTEXT", "Unsupported column data type: " + type); throw new SQLException("FULLTEXT", "Unsupported column data type: " + type);
default: default:
return ""; return "";
} }
} }
private String quoteSQL(Object data, int type) throws SQLException { private String quoteSQL(Object data, int type) throws SQLException {
if (data == null) { if (data == null) {
return "NULL"; return "NULL";
} }
switch (type) { switch (type) {
case Types.BIT: case Types.BIT:
case DataType.TYPE_BOOLEAN: case DataType.TYPE_BOOLEAN:
case Types.INTEGER: case Types.INTEGER:
case Types.BIGINT: case Types.BIGINT:
case Types.DECIMAL: case Types.DECIMAL:
case Types.DOUBLE: case Types.DOUBLE:
case Types.FLOAT: case Types.FLOAT:
case Types.NUMERIC: case Types.NUMERIC:
case Types.REAL: case Types.REAL:
case Types.SMALLINT: case Types.SMALLINT:
case Types.TINYINT: case Types.TINYINT:
return data.toString(); return data.toString();
case Types.DATE: case Types.DATE:
case Types.TIME: case Types.TIME:
case Types.TIMESTAMP: case Types.TIMESTAMP:
case Types.LONGVARCHAR: case Types.LONGVARCHAR:
case Types.CHAR: case Types.CHAR:
case Types.VARCHAR: case Types.VARCHAR:
return quoteString(data.toString()); return quoteString(data.toString());
case Types.VARBINARY: case Types.VARBINARY:
case Types.LONGVARBINARY: case Types.LONGVARBINARY:
case Types.BINARY: case Types.BINARY:
return quoteBinary((byte[]) data); return quoteBinary((byte[]) data);
case Types.JAVA_OBJECT: case Types.JAVA_OBJECT:
case Types.CLOB: case Types.CLOB:
case Types.OTHER: case Types.OTHER:
case Types.BLOB: case Types.BLOB:
case Types.STRUCT: case Types.STRUCT:
case Types.REF: case Types.REF:
case Types.NULL: case Types.NULL:
case Types.ARRAY: case Types.ARRAY:
case DataType.TYPE_DATALINK: case DataType.TYPE_DATALINK:
case Types.DISTINCT: case Types.DISTINCT:
throw new SQLException("FULLTEXT", "Unsupported key data type: " + type); throw new SQLException("FULLTEXT", "Unsupported key data type: " + type);
default: default:
return ""; return "";
} }
} }
private static void addWords(FullTextSettings setting, HashSet set, String text) { private static void addWords(FullTextSettings setting, HashSet set, String text) {
StringTokenizer tokenizer = new StringTokenizer(text, " \t\n\r\f+\"*%&/()=?'!,.;:-_#@|^~`{}[]"); StringTokenizer tokenizer = new StringTokenizer(text, " \t\n\r\f+\"*%&/()=?'!,.;:-_#@|^~`{}[]");
while (tokenizer.hasMoreTokens()) { while (tokenizer.hasMoreTokens()) {
String word = tokenizer.nextToken(); String word = tokenizer.nextToken();
word = setting.convertWord(word); word = setting.convertWord(word);
if (word != null) { if (word != null) {
set.add(word); set.add(word);
} }
} }
} }
private int[] getWordIds(FullTextSettings setting, Object[] row) throws SQLException { private int[] getWordIds(FullTextSettings setting, Object[] row) throws SQLException {
HashSet words = new HashSet(); HashSet words = new HashSet();
for (int i = 0; i < index.indexColumns.length; i++) { for (int i = 0; i < index.indexColumns.length; i++) {
int idx = index.indexColumns[i]; int idx = index.indexColumns[i];
...@@ -493,67 +504,67 @@ public class FullText implements Trigger { ...@@ -493,67 +504,67 @@ public class FullText implements Trigger {
return wordIds; return wordIds;
} }
private void insert(FullTextSettings setting, Object[] row) throws SQLException { private void insert(FullTextSettings setting, Object[] row) throws SQLException {
String key = getKey(row); String key = getKey(row);
int hash = key.hashCode(); int hash = key.hashCode();
prepInsertRow.setInt(1, hash); prepInsertRow.setInt(1, hash);
prepInsertRow.setLong(2, index.id); prepInsertRow.setInt(2, index.id);
prepInsertRow.setString(3, key); prepInsertRow.setString(3, key);
prepInsertRow.execute(); prepInsertRow.execute();
ResultSet rs = JdbcUtils.getGeneratedKeys(prepInsertRow); ResultSet rs = JdbcUtils.getGeneratedKeys(prepInsertRow);
rs.next(); rs.next();
long rowId = rs.getLong(1); int rowId = rs.getInt(1);
prepInsertMap.setLong(1, rowId); prepInsertMap.setInt(1, rowId);
int[] wordIds = getWordIds(setting, row); int[] wordIds = getWordIds(setting, row);
for (int i = 0; i < wordIds.length; i++) { for (int i = 0; i < wordIds.length; i++) {
prepInsertMap.setInt(2, wordIds[i]); prepInsertMap.setInt(2, wordIds[i]);
prepInsertMap.execute(); prepInsertMap.execute();
} }
} }
private void delete(FullTextSettings setting, Object[] row) throws SQLException { private void delete(FullTextSettings setting, Object[] row) throws SQLException {
String key = getKey(row); String key = getKey(row);
int hash = key.hashCode(); int hash = key.hashCode();
prepSelectRow.setInt(1, hash); prepSelectRow.setInt(1, hash);
prepSelectRow.setLong(2, index.id); prepSelectRow.setInt(2, index.id);
prepSelectRow.setString(3, key); prepSelectRow.setString(3, key);
ResultSet rs = prepSelectRow.executeQuery(); ResultSet rs = prepSelectRow.executeQuery();
if (rs.next()) { if (rs.next()) {
long rowId = rs.getLong(1); int rowId = rs.getInt(1);
prepDeleteMap.setLong(1, rowId); prepDeleteMap.setInt(1, rowId);
int[] wordIds = getWordIds(setting, row); int[] wordIds = getWordIds(setting, row);
for (int i = 0; i < wordIds.length; i++) { for (int i = 0; i < wordIds.length; i++) {
prepDeleteMap.setInt(2, wordIds[i]); prepDeleteMap.setInt(2, wordIds[i]);
prepDeleteMap.executeUpdate(); prepDeleteMap.executeUpdate();
} }
prepDeleteRow.setInt(1, hash); prepDeleteRow.setInt(1, hash);
prepDeleteRow.setLong(2, index.id); prepDeleteRow.setInt(2, index.id);
prepDeleteRow.setString(3, key); prepDeleteRow.setString(3, key);
prepDeleteRow.executeUpdate(); prepDeleteRow.executeUpdate();
} }
} }
/** /**
* Re-creates the full text index for this database. * Re-creates the full text index for this database.
* *
* @param conn the connection * @param conn the connection
* @param text the search query * @param text the search query
* @param limit the maximum number of rows or 0 for no limit * @param limit the maximum number of rows or 0 for no limit
* @param offset the offset or 0 for no offset * @param offset the offset or 0 for no offset
* @return the result set * @return the result set
*/ */
public static ResultSet search(Connection conn, String text, int limit, int offset) throws SQLException { public static ResultSet search(Connection conn, String text, int limit, int offset) throws SQLException {
SimpleResultSet result = new SimpleResultSet(); SimpleResultSet result = new SimpleResultSet();
result.addColumn(FIELD_QUERY, Types.VARCHAR, 0, 0); result.addColumn(FIELD_QUERY, Types.VARCHAR, 0, 0);
if (text == null) { if (text == null) {
// this is just to query the result set columns // this is just to query the result set columns
return result; return result;
} }
FullTextSettings setting = FullTextSettings.getInstance(conn); FullTextSettings setting = FullTextSettings.getInstance(conn);
HashSet words = new HashSet(); HashSet words = new HashSet();
addWords(setting, words, text); addWords(setting, words, text);
HashSet rIds = null, lastRowIds = null; HashSet rIds = null, lastRowIds = null;
HashMap allWords = setting.getWordList(); HashMap allWords = setting.getWordList();
PreparedStatement prepSelectMapByWordId = setting.getPrepSelectMapByWordId(); PreparedStatement prepSelectMapByWordId = setting.getPrepSelectMapByWordId();
for (Iterator it = words.iterator(); it.hasNext();) { for (Iterator it = words.iterator(); it.hasNext();) {
lastRowIds = rIds; lastRowIds = rIds;
...@@ -566,20 +577,20 @@ public class FullText implements Trigger { ...@@ -566,20 +577,20 @@ public class FullText implements Trigger {
prepSelectMapByWordId.setInt(1, wId.intValue()); prepSelectMapByWordId.setInt(1, wId.intValue());
ResultSet rs = prepSelectMapByWordId.executeQuery(); ResultSet rs = prepSelectMapByWordId.executeQuery();
while (rs.next()) { while (rs.next()) {
Long rId = ObjectUtils.getLong(rs.getLong(1)); Integer rId = ObjectUtils.getInteger(rs.getInt(1));
if (lastRowIds == null || lastRowIds.contains(rId)) { if (lastRowIds == null || lastRowIds.contains(rId)) {
rIds.add(rId); rIds.add(rId);
} }
} }
} }
if (rIds == null || rIds.size() == 0) { if (rIds == null || rIds.size() == 0) {
return result; return result;
} }
PreparedStatement prepSelectRowById = setting.getPrepSelectRowById(); PreparedStatement prepSelectRowById = setting.getPrepSelectRowById();
int rowCount = 0; int rowCount = 0;
for (Iterator it = rIds.iterator(); it.hasNext();) { for (Iterator it = rIds.iterator(); it.hasNext();) {
long rowId = ((Long) it.next()).longValue(); int rowId = ((Integer) it.next()).intValue();
prepSelectRowById.setLong(1, rowId); prepSelectRowById.setInt(1, rowId);
ResultSet rs = prepSelectRowById.executeQuery(); ResultSet rs = prepSelectRowById.executeQuery();
if (!rs.next()) { if (!rs.next()) {
continue; continue;
...@@ -587,14 +598,14 @@ public class FullText implements Trigger { ...@@ -587,14 +598,14 @@ public class FullText implements Trigger {
if (offset > 0) { if (offset > 0) {
offset--; offset--;
} else { } else {
String key = rs.getString(1); String key = rs.getString(1);
long indexId = rs.getLong(2); int indexId = rs.getInt(2);
IndexInfo index = setting.getIndexInfo(indexId); IndexInfo index = setting.getIndexInfo(indexId);
StringBuffer buff = new StringBuffer(); StringBuffer buff = new StringBuffer();
buff.append(StringUtils.quoteIdentifier(index.schemaName)); buff.append(StringUtils.quoteIdentifier(index.schemaName));
buff.append('.'); buff.append('.');
buff.append(StringUtils.quoteIdentifier(index.tableName)); buff.append(StringUtils.quoteIdentifier(index.tableName));
buff.append(" WHERE "); buff.append(" WHERE ");
buff.append(key); buff.append(key);
String query = buff.toString(); String query = buff.toString();
result.addRow(new String[] { query }); result.addRow(new String[] { query });
...@@ -602,9 +613,9 @@ public class FullText implements Trigger { ...@@ -602,9 +613,9 @@ public class FullText implements Trigger {
if (limit > 0 && rowCount >= limit) { if (limit > 0 && rowCount >= limit) {
break; break;
} }
} }
} }
return result; return result;
} }
} }
...@@ -39,16 +39,16 @@ import org.h2.util.StringUtils; ...@@ -39,16 +39,16 @@ import org.h2.util.StringUtils;
/** /**
* This class implements the full text search based on Apache Lucene. * This class implements the full text search based on Apache Lucene.
*/ */
public class FullTextLucene public class FullTextLucene
//#ifdef JDK14 //#ifdef JDK14
implements Trigger implements Trigger
//#endif //#endif
{ {
//#ifdef JDK14 //#ifdef JDK14
private static HashMap indexers = new HashMap(); private static HashMap indexers = new HashMap();
private static final String FIELD_DATA = "data"; private static final String FIELD_DATA = "DATA";
private static final String FIELD_QUERY = "query"; private static final String FIELD_QUERY = "QUERY";
private static final String FIELD_COLUMN_PREFIX = "_"; private static final String FIELD_COLUMN_PREFIX = "_";
private static final String TRIGGER_PREFIX = "FTL_"; private static final String TRIGGER_PREFIX = "FTL_";
private static final String SCHEMA = "FTL"; private static final String SCHEMA = "FTL";
...@@ -60,7 +60,7 @@ implements Trigger ...@@ -60,7 +60,7 @@ implements Trigger
private int[] dataTypes; private int[] dataTypes;
private IndexModifier indexer; private IndexModifier indexer;
//#endif //#endif
/** /**
* Create a new full text index for a table and column list. Each table may only have one index at any time. * Create a new full text index for a table and column list. Each table may only have one index at any time.
* *
...@@ -81,7 +81,7 @@ implements Trigger ...@@ -81,7 +81,7 @@ implements Trigger
createTrigger(conn, schema, table); createTrigger(conn, schema, table);
indexExistingRows(conn, schema, table); indexExistingRows(conn, schema, table);
} }
//#endif //#endif
/** /**
* Re-creates the full text index for this database * Re-creates the full text index for this database
...@@ -102,7 +102,7 @@ implements Trigger ...@@ -102,7 +102,7 @@ implements Trigger
indexExistingRows(conn, schema, table); indexExistingRows(conn, schema, table);
} }
} }
//#endif //#endif
/** /**
* Drops all full text indexes from the database. * Drops all full text indexes from the database.
...@@ -116,7 +116,7 @@ implements Trigger ...@@ -116,7 +116,7 @@ implements Trigger
removeAllTriggers(conn); removeAllTriggers(conn);
removeIndexFiles(conn); removeIndexFiles(conn);
} }
//#endif //#endif
/** /**
* Initializes full text search functionality for this database. This adds the following Java functions to the * Initializes full text search functionality for this database. This adds the following Java functions to the
...@@ -147,7 +147,7 @@ implements Trigger ...@@ -147,7 +147,7 @@ implements Trigger
stat.execute("CREATE ALIAS IF NOT EXISTS FTL_REINDEX FOR \"" + FullTextLucene.class.getName() + ".reindex\""); stat.execute("CREATE ALIAS IF NOT EXISTS FTL_REINDEX FOR \"" + FullTextLucene.class.getName() + ".reindex\"");
stat.execute("CREATE ALIAS IF NOT EXISTS FTL_DROP_ALL FOR \"" + FullTextLucene.class.getName() + ".dropAll\""); stat.execute("CREATE ALIAS IF NOT EXISTS FTL_DROP_ALL FOR \"" + FullTextLucene.class.getName() + ".dropAll\"");
} }
//#endif //#endif
/** /**
* INTERNAL * INTERNAL
...@@ -203,7 +203,7 @@ implements Trigger ...@@ -203,7 +203,7 @@ implements Trigger
indexColumns = new int[indexList.size()]; indexColumns = new int[indexList.size()];
setColumns(indexColumns, indexList, columnList); setColumns(indexColumns, indexList, columnList);
} }
//#endif //#endif
/** /**
* INTERNAL * INTERNAL
...@@ -217,7 +217,7 @@ implements Trigger ...@@ -217,7 +217,7 @@ implements Trigger
insert(newRow); insert(newRow);
} }
} }
//#endif //#endif
/** /**
* Searches from the full text index for this database. * Searches from the full text index for this database.
...@@ -262,7 +262,7 @@ implements Trigger ...@@ -262,7 +262,7 @@ implements Trigger
} }
return rs; return rs;
} }
private static void removeAllTriggers(Connection conn) throws SQLException { private static void removeAllTriggers(Connection conn) throws SQLException {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
ResultSet rs = stat.executeQuery("SELECT * FROM INFORMATION_SCHEMA.TRIGGERS"); ResultSet rs = stat.executeQuery("SELECT * FROM INFORMATION_SCHEMA.TRIGGERS");
...@@ -291,7 +291,7 @@ implements Trigger ...@@ -291,7 +291,7 @@ implements Trigger
} }
FileSystem.getInstance(path).deleteRecursive(path); FileSystem.getInstance(path).deleteRecursive(path);
} }
private String getQuery(Object[] row) throws SQLException { private String getQuery(Object[] row) throws SQLException {
StringBuffer buff = new StringBuffer(); StringBuffer buff = new StringBuffer();
if (schemaName != null) { if (schemaName != null) {
...@@ -462,8 +462,8 @@ implements Trigger ...@@ -462,8 +462,8 @@ implements Trigger
SQLException e2 = new SQLException("FULLTEXT", "Error while indexing document"); SQLException e2 = new SQLException("FULLTEXT", "Error while indexing document");
e2.initCause(e); e2.initCause(e);
return e2; return e2;
} }
private static void createTrigger(Connection conn, String schema, String table) throws SQLException { private static void createTrigger(Connection conn, String schema, String table) throws SQLException {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
String trigger = StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(TRIGGER_PREFIX + table); String trigger = StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(TRIGGER_PREFIX + table);
...@@ -494,7 +494,6 @@ implements Trigger ...@@ -494,7 +494,6 @@ implements Trigger
} }
} }
private static IndexModifier getIndexModifier(Connection conn) throws SQLException { private static IndexModifier getIndexModifier(Connection conn) throws SQLException {
try { try {
String path = getIndexPath(conn); String path = getIndexPath(conn);
...@@ -524,7 +523,7 @@ implements Trigger ...@@ -524,7 +523,7 @@ implements Trigger
rs.close(); rs.close();
return path; return path;
} }
private void setColumns(int[] index, ArrayList keys, ArrayList columns) throws SQLException { private void setColumns(int[] index, ArrayList keys, ArrayList columns) throws SQLException {
for (int i = 0; i < keys.size(); i++) { for (int i = 0; i < keys.size(); i++) {
String key = (String) keys.get(i); String key = (String) keys.get(i);
...@@ -541,6 +540,6 @@ implements Trigger ...@@ -541,6 +540,6 @@ implements Trigger
index[i] = found; index[i] = found;
} }
} }
//#endif //#endif
} }
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
package org.h2.fulltext; package org.h2.fulltext;
public class IndexInfo { public class IndexInfo {
long id; int id;
String schemaName; String schemaName;
String tableName; String tableName;
int[] keys; int[] keys;
......
...@@ -19,9 +19,9 @@ import org.h2.util.FileUtils; ...@@ -19,9 +19,9 @@ import org.h2.util.FileUtils;
import org.h2.util.SmallLRUCache; import org.h2.util.SmallLRUCache;
/** /**
* It is possible to write after close was called, but that means for each write the * It is possible to write after close was called, but that means for each write the
* log file will be opened and closed again (which is slower). * log file will be opened and closed again (which is slower).
* *
* @author Thomas * @author Thomas
*/ */
public class TraceSystem { public class TraceSystem {
...@@ -29,7 +29,7 @@ public class TraceSystem { ...@@ -29,7 +29,7 @@ public class TraceSystem {
// TODO log total and free memory from time to time // TODO log total and free memory from time to time
// max file size is currently 64 MB, // max file size is currently 64 MB,
// and then there could be a .old file of the same size // and then there could be a .old file of the same size
private static final int DEFAULT_MAX_FILE_SIZE = 64 * 1024 * 1024; private static final int DEFAULT_MAX_FILE_SIZE = 64 * 1024 * 1024;
public static final int DEFAULT_TRACE_LEVEL_SYSTEM_OUT = OFF; public static final int DEFAULT_TRACE_LEVEL_SYSTEM_OUT = OFF;
...@@ -49,14 +49,14 @@ public class TraceSystem { ...@@ -49,14 +49,14 @@ public class TraceSystem {
private boolean closed; private boolean closed;
private boolean manualEnabling = true; private boolean manualEnabling = true;
private boolean writingErrorLogged; private boolean writingErrorLogged;
public static void traceThrowable(Throwable e) { public static void traceThrowable(Throwable e) {
PrintWriter writer = DriverManager.getLogWriter(); PrintWriter writer = DriverManager.getLogWriter();
if (writer != null) { if (writer != null) {
e.printStackTrace(writer); e.printStackTrace(writer);
} }
} }
public void setManualEnabling(boolean value) { public void setManualEnabling(boolean value) {
this.manualEnabling = value; this.manualEnabling = value;
} }
...@@ -230,10 +230,17 @@ public class TraceSystem { ...@@ -230,10 +230,17 @@ public class TraceSystem {
try { try {
fileWriter.close(); fileWriter.close();
} catch (IOException e) { } catch (IOException e) {
// ignore exception // ignore
} }
fileWriter = null; fileWriter = null;
} }
try {
if (fileName != null && FileUtils.length(fileName) == 0) {
FileUtils.delete(fileName);
}
} catch (SQLException e) {
// ignore
}
} }
public void close() { public void close() {
...@@ -244,7 +251,7 @@ public class TraceSystem { ...@@ -244,7 +251,7 @@ public class TraceSystem {
protected void finalize() { protected void finalize() {
if (!SysProperties.runFinalize) { if (!SysProperties.runFinalize) {
return; return;
} }
close(); close();
} }
......
...@@ -9,6 +9,7 @@ import java.io.IOException; ...@@ -9,6 +9,7 @@ import java.io.IOException;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.OutputStreamWriter; import java.io.OutputStreamWriter;
import java.io.PrintWriter; import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket; import java.net.ServerSocket;
import java.net.Socket; import java.net.Socket;
import java.sql.SQLException; import java.sql.SQLException;
...@@ -171,20 +172,7 @@ public class FtpControl extends Thread { ...@@ -171,20 +172,7 @@ public class FtpControl extends Thread {
break; break;
case 'M': case 'M':
if ("MKD".equals(command)) { if ("MKD".equals(command)) {
String fileName = getFileName(param); processMakeDir(param);
boolean ok = false;
if (!readonly) {
try {
fs.mkdirs(fileName);
reply(257, StringUtils.quoteIdentifier(param) + " directory");
ok = true;
} catch (SQLException e) {
server.logError(e);
}
}
if (!ok) {
reply(500, "Failed");
}
} else if ("MODE".equals(command)) { } else if ("MODE".equals(command)) {
if ("S".equals(StringUtils.toUpperEnglish(param))) { if ("S".equals(StringUtils.toUpperEnglish(param))) {
reply(200, "Ok"); reply(200, "Ok");
...@@ -216,6 +204,18 @@ public class FtpControl extends Thread { ...@@ -216,6 +204,18 @@ public class FtpControl extends Thread {
data.start(); data.start();
int port = dataSocket.getLocalPort(); int port = dataSocket.getLocalPort();
reply(227, "Passive Mode (" + serverIpAddress + "," + (port >> 8) + "," + (port & 255) + ")"); reply(227, "Passive Mode (" + serverIpAddress + "," + (port >> 8) + "," + (port & 255) + ")");
} else if ("PORT".equals(command)) {
String[] list = StringUtils.arraySplit(param, ',', true);
String host = list[0] + "." + list[1] + "." + list[2] + "." + list[3];
int port = (Integer.parseInt(list[4]) << 8) | Integer.parseInt(list[5]);
InetAddress address = InetAddress.getByName(host);
if (address.equals(control.getInetAddress())) {
data = new FtpData(server, address, port);
reply(200, "Ok");
} else {
server.log("Port REJECTED:" + address + " expected:" + control.getInetAddress());
reply(550, "Failed");
}
} }
break; break;
case 'R': case 'R':
...@@ -260,16 +260,12 @@ public class FtpControl extends Thread { ...@@ -260,16 +260,12 @@ public class FtpControl extends Thread {
} }
restart = 0; restart = 0;
} else { } else {
processList(param, true); // Firefox compatibility (still not good) processList(param, true); // Firefox compatibility (still
// not good)
// reply(426, "Not a file"); // reply(426, "Not a file");
} }
} else if ("RMD".equals(command)) { } else if ("RMD".equals(command)) {
String fileName = getFileName(param); processRemoveDir(param);
if (!readonly && fs.exists(fileName) && fs.isDirectory(fileName) && fs.tryDelete(fileName)) {
reply(250, "Ok");
} else {
reply(500, "Failed");
}
} else if ("REST".equals(command)) { } else if ("REST".equals(command)) {
try { try {
restart = Integer.parseInt(param); restart = Integer.parseInt(param);
...@@ -328,11 +324,43 @@ public class FtpControl extends Thread { ...@@ -328,11 +324,43 @@ public class FtpControl extends Thread {
} }
} }
break; break;
case 'X':
if ("XMKD".equals(command)) {
processMakeDir(param);
} else if ("XRMD".equals(command)) {
processRemoveDir(param);
}
default: default:
break; break;
} }
} }
void processMakeDir(String param) throws IOException {
String fileName = getFileName(param);
boolean ok = false;
if (!readonly) {
try {
fs.mkdirs(fileName);
reply(257, StringUtils.quoteIdentifier(param) + " directory");
ok = true;
} catch (SQLException e) {
server.logError(e);
}
}
if (!ok) {
reply(500, "Failed");
}
}
void processRemoveDir(String param) throws IOException {
String fileName = getFileName(param);
if (!readonly && fs.exists(fileName) && fs.isDirectory(fileName) && fs.tryDelete(fileName)) {
reply(250, "Ok");
} else {
reply(500, "Failed");
}
}
private String getFileName(String file) { private String getFileName(String file) {
return server.getFileName(file.startsWith("/") ? file : currentDir + file); return server.getFileName(file.startsWith("/") ? file : currentDir + file);
} }
...@@ -353,7 +381,8 @@ public class FtpControl extends Thread { ...@@ -353,7 +381,8 @@ public class FtpControl extends Thread {
String list = server.getDirectoryListing(directory, directories); String list = server.getDirectoryListing(directory, directories);
reply(150, "Starting transfer"); reply(150, "Starting transfer");
server.log(list); server.log(list);
// need to use the current locale (UTF-8 would be wrong for the Windows Explorer) // need to use the current locale (UTF-8 would be wrong for the Windows
// Explorer)
data.send(list.getBytes()); data.send(list.getBytes());
reply(226, "Done"); reply(226, "Done");
} }
......
...@@ -21,6 +21,8 @@ public class FtpData extends Thread { ...@@ -21,6 +21,8 @@ public class FtpData extends Thread {
private InetAddress address; private InetAddress address;
private ServerSocket serverSocket; private ServerSocket serverSocket;
private volatile Socket socket; private volatile Socket socket;
private boolean active;
private int port;
public FtpData(FtpServer server, InetAddress address, ServerSocket serverSocket) throws IOException { public FtpData(FtpServer server, InetAddress address, ServerSocket serverSocket) throws IOException {
this.server = server; this.server = server;
...@@ -28,6 +30,13 @@ public class FtpData extends Thread { ...@@ -28,6 +30,13 @@ public class FtpData extends Thread {
this.serverSocket = serverSocket; this.serverSocket = serverSocket;
} }
public FtpData(FtpServer server, InetAddress address, int port) throws IOException {
this.server = server;
this.address = address;
this.port = port;
active = true;
}
public void run() { public void run() {
try { try {
synchronized (this) { synchronized (this) {
...@@ -46,6 +55,14 @@ public class FtpData extends Thread { ...@@ -46,6 +55,14 @@ public class FtpData extends Thread {
} }
} }
private void connect() throws IOException {
if (active) {
socket = new Socket(address, port);
} else {
waitUntilConnected();
}
}
private void waitUntilConnected() { private void waitUntilConnected() {
while (serverSocket != null && socket == null) { while (serverSocket != null && socket == null) {
try { try {
...@@ -63,7 +80,7 @@ public class FtpData extends Thread { ...@@ -63,7 +80,7 @@ public class FtpData extends Thread {
} }
public synchronized void receive(FileSystem fs, String fileName) throws IOException, SQLException { public synchronized void receive(FileSystem fs, String fileName) throws IOException, SQLException {
waitUntilConnected(); connect();
try { try {
InputStream in = socket.getInputStream(); InputStream in = socket.getInputStream();
OutputStream out = fs.openFileOutputStream(fileName, false); OutputStream out = fs.openFileOutputStream(fileName, false);
...@@ -76,7 +93,7 @@ public class FtpData extends Thread { ...@@ -76,7 +93,7 @@ public class FtpData extends Thread {
} }
public synchronized void send(FileSystem fs, String fileName, long skip) throws IOException { public synchronized void send(FileSystem fs, String fileName, long skip) throws IOException {
waitUntilConnected(); connect();
try { try {
OutputStream out = socket.getOutputStream(); OutputStream out = socket.getOutputStream();
InputStream in = fs.openFileInputStream(fileName); InputStream in = fs.openFileInputStream(fileName);
...@@ -90,7 +107,7 @@ public class FtpData extends Thread { ...@@ -90,7 +107,7 @@ public class FtpData extends Thread {
} }
public synchronized void send(byte[] data) throws IOException { public synchronized void send(byte[] data) throws IOException {
waitUntilConnected(); connect();
try { try {
OutputStream out = socket.getOutputStream(); OutputStream out = socket.getOutputStream();
out.write(data); out.write(data);
......
...@@ -120,7 +120,7 @@ public class FtpServer implements Service { ...@@ -120,7 +120,7 @@ public class FtpServer implements Service {
String getFileName(String path) { String getFileName(String path) {
return root + getPath(path); return root + getPath(path);
} }
String getPath(String path) { String getPath(String path) {
if (path.indexOf("..") > 0) { if (path.indexOf("..") > 0) {
path = "/"; path = "/";
......
...@@ -25,7 +25,7 @@ SELECT * FROM FT_SEARCH('World', 0, 0); ...@@ -25,7 +25,7 @@ SELECT * FROM FT_SEARCH('World', 0, 0);
SELECT * FROM FT_SEARCH('World', 1, 0); SELECT * FROM FT_SEARCH('World', 1, 0);
SELECT * FROM FT_SEARCH('World', 0, 2); SELECT * FROM FT_SEARCH('World', 0, 2);
SELECT * FROM FT_SEARCH('World', 2, 1); SELECT * FROM FT_SEARCH('World', 2, 1);
SELECT * FROM FT_SEARCH('1'); SELECT * FROM FT_SEARCH('1', 0, 0);
CALL FT_DROP_ALL(); CALL FT_DROP_ALL();
SELECT * FROM FT_SEARCH('World', 2, 1); SELECT * FROM FT_SEARCH('World', 2, 1);
CALL FT_DROP_ALL(); CALL FT_DROP_ALL();
......
...@@ -150,37 +150,12 @@ java org.h2.test.TestAll timer ...@@ -150,37 +150,12 @@ java org.h2.test.TestAll timer
/* /*
create table test(id int, name varchar); toString: > the parameters for the prepared statements.
insert into test select x, '' from system_range(1, 10000);
-- fast
update test set name = 'y' where cast(id as varchar) like '1%';
-- slow
update test set name = 'x' where id in (select x from system_range(1, 10000) where cast(x as varchar) like '1%');
drop table test;
Optimize IN(...), IN(select), ID=? OR ID=?: create temp table and use join
Bug:
H2 1.0.62 (2007-11-25) has regressed on this query. Parser syntax error is returned (query is OK for derby, oracle, and earlier H2 versions).
SELECT COUNT(*) FROM (
SELECT TT.id,TT.table_name FROM (
SELECT DISTINCT id, table_name FROM information_schema.tables WHERE id=-8 UNION SELECT DISTINCT id, table_name FROM information_schema.tables WHERE id=-8) AS TT
) AS AWR WHERE AWR.id=-8
Remove the final predicate and the query parses & runs fine:
SELECT COUNT(*) FROM (
SELECT TT.id,TT.table_name FROM (
SELECT DISTINCT id, table_name FROM information_schema.tables WHERE id=-8 UNION SELECT DISTINCT id, table_name FROM information_schema.tables WHERE id=-8) AS TT
) AS AWR
autocomplete only just after meaningful key (ctrl+space, space, bs, interpunctation, ...)
write more tests for the command line tools write more tests for the command line tools
avoid creating thousands of trace.db files
Known Problems: Known Problems:
link to history page, bug page link to history page, bug page
Add a link to the google code bug page Add a link to the google code bug page
......
...@@ -16,86 +16,106 @@ public class TestFullText extends TestBase { ...@@ -16,86 +16,106 @@ public class TestFullText extends TestBase {
if (config.memory) { if (config.memory) {
return; return;
} }
test(false);
String luceneFullTextClassName = "org.h2.fulltext.FullTextLucene";
try {
Class.forName(luceneFullTextClassName);
test(true);
} catch (ClassNotFoundException e) {
this.println("Class not found, not tested: " + luceneFullTextClassName);
// ok
}
}
private void test(boolean lucene) throws Exception {
deleteDb("fullText"); deleteDb("fullText");
Connection conn = getConnection("fullText"); Connection conn = getConnection("fullText");
String prefix = lucene ? "FTL_" : "FT_";
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
stat.execute("CREATE ALIAS IF NOT EXISTS FT_INIT FOR \"org.h2.fulltext.FullText.init\""); String className = lucene ? "FullTextLucene" : "FullText";
stat.execute("CALL FT_INIT()"); stat.execute("CREATE ALIAS IF NOT EXISTS " + prefix + "INIT FOR \"org.h2.fulltext." + className + ".init\"");
stat.execute("CALL " + prefix + "INIT()");
stat.execute("DROP TABLE IF EXISTS TEST"); stat.execute("DROP TABLE IF EXISTS TEST");
stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)"); stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
stat.execute("INSERT INTO TEST VALUES(1, 'Hello World')"); stat.execute("INSERT INTO TEST VALUES(1, 'Hello World')");
stat.execute("CALL FT_CREATE_INDEX('PUBLIC', 'TEST', NULL)"); stat.execute("CALL " + prefix + "CREATE_INDEX('PUBLIC', 'TEST', NULL)");
ResultSet rs; ResultSet rs;
rs = stat.executeQuery("SELECT * FROM FT_SEARCH('Hello', 0, 0)"); rs = stat.executeQuery("SELECT * FROM " + prefix + "SEARCH('Hello', 0, 0)");
rs.next(); rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=1"); check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=1");
checkFalse(rs.next()); checkFalse(rs.next());
rs = stat.executeQuery("SELECT * FROM FT_SEARCH('Hallo', 0, 0)"); rs = stat.executeQuery("SELECT * FROM " + prefix + "SEARCH('Hallo', 0, 0)");
checkFalse(rs.next()); checkFalse(rs.next());
stat.execute("INSERT INTO TEST VALUES(2, 'Hallo Welt')"); stat.execute("INSERT INTO TEST VALUES(2, 'Hallo Welt')");
rs = stat.executeQuery("SELECT * FROM FT_SEARCH('Hello', 0, 0)"); rs = stat.executeQuery("SELECT * FROM " + prefix + "SEARCH('Hello', 0, 0)");
rs.next(); rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=1"); check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=1");
checkFalse(rs.next()); checkFalse(rs.next());
rs = stat.executeQuery("SELECT * FROM FT_SEARCH('Hallo', 0, 0)"); rs = stat.executeQuery("SELECT * FROM " + prefix + "SEARCH('Hallo', 0, 0)");
rs.next(); rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=2"); check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=2");
checkFalse(rs.next()); checkFalse(rs.next());
stat.execute("CALL FT_REINDEX()"); stat.execute("CALL " + prefix + "REINDEX()");
rs = stat.executeQuery("SELECT * FROM FT_SEARCH('Hello', 0, 0)"); rs = stat.executeQuery("SELECT * FROM " + prefix + "SEARCH('Hello', 0, 0)");
rs.next(); rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=1"); check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=1");
checkFalse(rs.next()); checkFalse(rs.next());
rs = stat.executeQuery("SELECT * FROM FT_SEARCH('Hallo', 0, 0)"); rs = stat.executeQuery("SELECT * FROM " + prefix + "SEARCH('Hallo', 0, 0)");
rs.next(); rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=2"); check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=2");
checkFalse(rs.next()); checkFalse(rs.next());
stat.execute("INSERT INTO TEST VALUES(3, 'Hello World')"); stat.execute("INSERT INTO TEST VALUES(3, 'Hello World')");
stat.execute("INSERT INTO TEST VALUES(4, 'Hello World')"); stat.execute("INSERT INTO TEST VALUES(4, 'Hello World')");
stat.execute("INSERT INTO TEST VALUES(5, 'Hello World')"); stat.execute("INSERT INTO TEST VALUES(5, 'Hello World')");
rs = stat.executeQuery("SELECT * FROM FT_SEARCH('World', 0, 0)"); rs = stat.executeQuery("SELECT * FROM " + prefix + "SEARCH('World', 0, 0) ORDER BY QUERY");
rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=4");
rs.next(); rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=1"); check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=1");
rs.next(); rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=5");
rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=3"); check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=3");
checkFalse(rs.next());
rs = stat.executeQuery("SELECT * FROM FT_SEARCH('World', 1, 0)");
rs.next(); rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=4"); check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=4");
checkFalse(rs.next());
rs = stat.executeQuery("SELECT * FROM FT_SEARCH('World', 0, 2)");
rs.next(); rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=5"); check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=5");
checkFalse(rs.next());
rs = stat.executeQuery("SELECT * FROM " + prefix + "SEARCH('World', 1, 0)");
rs.next(); rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=3"); check(rs.getString(1).startsWith("\"PUBLIC\".\"TEST\" WHERE \"ID\"="));
checkFalse(rs.next()); checkFalse(rs.next());
rs = stat.executeQuery("SELECT * FROM FT_SEARCH('World', 2, 1)"); rs = stat.executeQuery("SELECT * FROM " + prefix + "SEARCH('World', 0, 2) ORDER BY QUERY");
rs.next(); rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=1"); check(rs.getString(1).startsWith("\"PUBLIC\".\"TEST\" WHERE \"ID\"="));
rs.next(); rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=5"); check(rs.getString(1).startsWith("\"PUBLIC\".\"TEST\" WHERE \"ID\"="));
checkFalse(rs.next());
rs = stat.executeQuery("SELECT * FROM " + prefix + "SEARCH('World', 2, 1) ORDER BY QUERY");
rs.next();
check(rs.getString(1).startsWith("\"PUBLIC\".\"TEST\" WHERE \"ID\"="));
rs.next();
check(rs.getString(1).startsWith("\"PUBLIC\".\"TEST\" WHERE \"ID\"="));
checkFalse(rs.next()); checkFalse(rs.next());
rs = stat.executeQuery("SELECT * FROM FT_SEARCH('1', 0, 0)"); rs = stat.executeQuery("SELECT * FROM " + prefix + "SEARCH('1', 0, 0)");
rs.next(); rs.next();
check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=1"); check(rs.getString(1), "\"PUBLIC\".\"TEST\" WHERE \"ID\"=1");
checkFalse(rs.next()); checkFalse(rs.next());
stat.execute("CALL FT_DROP_ALL()");
rs = stat.executeQuery("SELECT * FROM FT_SEARCH('World', 2, 1)");
stat.execute("CALL FT_DROP_ALL()");
conn.close(); conn.close();
conn = getConnection("fullText");
stat = conn.createStatement();
rs = stat.executeQuery("SELECT * FROM " + prefix + "SEARCH('World', 0, 0)");
stat.execute("CALL " + prefix + "DROP_ALL()");
rs = stat.executeQuery("SELECT * FROM " + prefix + "SEARCH('World', 2, 1)");
stat.execute("CALL " + prefix + "DROP_ALL()");
conn.close();
} }
} }
select count(*) from (select * from dual union select * from dual) where x = 0;
> 0;
select count(*) from (select * from (select * from dual union select * from dual)) where x = 0;
> 0;
select instr('abcisj','s', -1) from dual; select instr('abcisj','s', -1) from dual;
> 5; > 5;
CREATE TABLE TEST(ID INT); CREATE TABLE TEST(ID INT);
...@@ -30,7 +35,7 @@ SELECT X FROM (SELECT X, X AS "XY" FROM DUAL) WHERE X=1; ...@@ -30,7 +35,7 @@ SELECT X FROM (SELECT X, X AS "XY" FROM DUAL) WHERE X=1;
> 1; > 1;
SELECT X FROM (SELECT X, X AS "X Y" FROM DUAL) WHERE X=1; SELECT X FROM (SELECT X, X AS "X Y" FROM DUAL) WHERE X=1;
> 1; > 1;
SELECT X FROM (SELECT X, X AS "X Y" FROM DUAL AS "D Z") WHERE X=1; SELECT X FROM (SELECT X, X AS "X Y" FROM DUAL AS "D Z") WHERE X=1;
> 1; > 1;
select * from (select x from dual union select convert(x, int) from dual) where x=0; select * from (select x from dual union select convert(x, int) from dual) where x=0;
...@@ -109,7 +114,7 @@ drop table table2; ...@@ -109,7 +114,7 @@ drop table table2;
select case when 1=null then 1 else 2 end; select case when 1=null then 1 else 2 end;
> 2; > 2;
select case (1) when 1 then 1 else 2 end; select case (1) when 1 then 1 else 2 end;
> 1; > 1;
create table test(id int); create table test(id int);
...@@ -146,7 +151,7 @@ select count(*) from A left outer join B on B.AID=A.ID inner join C on C.BID=B.I ...@@ -146,7 +151,7 @@ select count(*) from A left outer join B on B.AID=A.ID inner join C on C.BID=B.I
select count(*) from (A left outer join B on B.AID=A.ID) inner join C on C.BID=B.ID where A.id=1; select count(*) from (A left outer join B on B.AID=A.ID) inner join C on C.BID=B.ID where A.id=1;
> 0; > 0;
drop table a, b, c; drop table a, b, c;
create schema a; create schema a;
create table a.test(id int); create table a.test(id int);
insert into a.test values(1); insert into a.test values(1);
...@@ -177,7 +182,7 @@ select TEST_SCHEMA.TEST_SEQ.CURRVAL; ...@@ -177,7 +182,7 @@ select TEST_SCHEMA.TEST_SEQ.CURRVAL;
select TEST_SCHEMA.TEST_SEQ.nextval; select TEST_SCHEMA.TEST_SEQ.nextval;
> 1; > 1;
drop schema TEST_SCHEMA; drop schema TEST_SCHEMA;
create table test(id int); create table test(id int);
create trigger TEST_TRIGGER before insert on test call "org.h2.test.db.TestTriggersConstraints"; create trigger TEST_TRIGGER before insert on test call "org.h2.test.db.TestTriggersConstraints";
comment on trigger TEST_TRIGGER is 'just testing'; comment on trigger TEST_TRIGGER is 'just testing';
...@@ -238,7 +243,7 @@ select remarks from information_schema.sequences where sequence_name = 'WALK'; ...@@ -238,7 +243,7 @@ select remarks from information_schema.sequences where sequence_name = 'WALK';
> Walker; > Walker;
drop schema tests; drop schema tests;
@reconnect; @reconnect;
create constant abc value 1; create constant abc value 1;
comment on constant abc is 'One'; comment on constant abc is 'One';
select remarks from information_schema.constants where constant_name = 'ABC'; select remarks from information_schema.constants where constant_name = 'ABC';
...@@ -249,7 +254,7 @@ select remarks from information_schema.constants where constant_name = 'ABC'; ...@@ -249,7 +254,7 @@ select remarks from information_schema.constants where constant_name = 'ABC';
drop constant abc; drop constant abc;
drop table test; drop table test;
@reconnect; @reconnect;
create table test(id int); create table test(id int);
alter table test add constraint const1 unique(id); alter table test add constraint const1 unique(id);
create index IDX_ID on test(id); create index IDX_ID on test(id);
......
...@@ -41,7 +41,7 @@ public class PrepareTranslation { ...@@ -41,7 +41,7 @@ public class PrepareTranslation {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
new PrepareTranslation().run(args); new PrepareTranslation().run(args);
} }
private void run(String[] args) throws Exception { private void run(String[] args) throws Exception {
String baseDir = "src/docsrc/textbase"; String baseDir = "src/docsrc/textbase";
prepare(baseDir, "src/main/org/h2/res"); prepare(baseDir, "src/main/org/h2/res");
...@@ -479,7 +479,7 @@ public class PrepareTranslation { ...@@ -479,7 +479,7 @@ public class PrepareTranslation {
System.out.println(trans.getName() + ": key " + key + " not found in translation file; added dummy # 'translation'"); System.out.println(trans.getName() + ": key " + key + " not found in translation file; added dummy # 'translation'");
t = "#" + now; t = "#" + now;
} }
p.put(key, t); p.put(key, t);
} }
// remove keys that don't exist in the main file (deleted or typo in the key) // remove keys that don't exist in the main file (deleted or typo in the key)
it = new ArrayList(p.keySet()).iterator(); it = new ArrayList(p.keySet()).iterator();
...@@ -531,7 +531,7 @@ public class PrepareTranslation { ...@@ -531,7 +531,7 @@ public class PrepareTranslation {
translateChunk(buff, separator, sourceLanguage, targetLanguage, keyMap, results); translateChunk(buff, separator, sourceLanguage, targetLanguage, keyMap, results);
return results; return results;
} }
private void translateChunk(StringBuffer buff, int separator, String source, String target, HashMap keyMap, HashMap results) { private void translateChunk(StringBuffer buff, int separator, String source, String target, HashMap keyMap, HashMap results) {
buff.append(separator); buff.append(separator);
String original = buff.toString(); String original = buff.toString();
...@@ -563,18 +563,15 @@ public class PrepareTranslation { ...@@ -563,18 +563,15 @@ public class PrepareTranslation {
keyMap.clear(); keyMap.clear();
buff.setLength(0); buff.setLength(0);
} }
/** /**
* Translate the text using Google * Translate the text using Google Translate
*/ */
String translate(String text, String sourceLanguage, String targetLanguage) throws Exception { String translate(String text, String sourceLanguage, String targetLanguage) throws Exception {
Thread.sleep(4000); Thread.sleep(4000);
String url = "http://translate.google.com/translate_t?langpair=" + sourceLanguage + "|" + targetLanguage + "&text=" String url = "http://translate.google.com/translate_t?langpair=" + sourceLanguage + "|" + targetLanguage + "&text="
+ URLEncoder.encode(text, "UTF-8"); + URLEncoder.encode(text, "UTF-8");
HttpURLConnection conn = (HttpURLConnection) (new URL(url)).openConnection(); HttpURLConnection conn = (HttpURLConnection) (new URL(url)).openConnection();
// conn.setRequestProperty("User-Agent", "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1)");
int todoTest;
// conn.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.10) Gecko/20071115 Firefox/2.0.0.10");
conn.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; Java)"); conn.setRequestProperty("User-Agent", "Mozilla/5.0 (compatible; Java)");
String result = IOUtils.readStringAndClose(IOUtils.getReader(conn.getInputStream()), -1); String result = IOUtils.readStringAndClose(IOUtils.getReader(conn.getInputStream()), -1);
int start = result.indexOf("<div id=result_box"); int start = result.indexOf("<div id=result_box");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论