提交 a78addfd authored 作者: noelgrandin's avatar noelgrandin

support drop index for Lucene full-text indices

上级 78a4f621
...@@ -49,6 +49,7 @@ Change Log ...@@ -49,6 +49,7 @@ Change Log
</li><li>Improved OSGi support. H2 now registers itself as a DataSourceFactory service. Fixes issue 365. </li><li>Improved OSGi support. H2 now registers itself as a DataSourceFactory service. Fixes issue 365.
</li><li>Add a DISK_SPACE_USED system function. Fixes issue 270. </li><li>Add a DISK_SPACE_USED system function. Fixes issue 270.
</li><li>Fix a compile-time ambiguity when compiling with JDK7, thanks to a patch from Lukas Eder. </li><li>Fix a compile-time ambiguity when compiling with JDK7, thanks to a patch from Lukas Eder.
</li><li>Supporting dropping an index for Lucene full-text indexes.
</li></ul> </li></ul>
<h2>Version 1.3.170 (2012-11-30)</h2> <h2>Version 1.3.170 (2012-11-30)</h2>
......
...@@ -1215,6 +1215,12 @@ This will produce a result set that contains the query needed to retrieve the da ...@@ -1215,6 +1215,12 @@ This will produce a result set that contains the query needed to retrieve the da
QUERY: "PUBLIC"."TEST" WHERE "ID"=1 QUERY: "PUBLIC"."TEST" WHERE "ID"=1
</pre> </pre>
<p> <p>
To drop an index on a table:
</p>
<pre>
CALL FT_DROP_INDEX('PUBLIC', 'TEST');
</pre>
<p>
To get the raw data, use <code>FT_SEARCH_DATA('Hello', 0, 0);</code>. To get the raw data, use <code>FT_SEARCH_DATA('Hello', 0, 0);</code>.
The result contains the columns <code>SCHEMA</code> (the schema name), The result contains the columns <code>SCHEMA</code> (the schema name),
<code>TABLE</code> (the table name), <code>TABLE</code> (the table name),
...@@ -1270,6 +1276,13 @@ This will produce a result set that contains the query needed to retrieve the da ...@@ -1270,6 +1276,13 @@ This will produce a result set that contains the query needed to retrieve the da
QUERY: "PUBLIC"."TEST" WHERE "ID"=1 QUERY: "PUBLIC"."TEST" WHERE "ID"=1
</pre> </pre>
<p> <p>
To drop an index on a table:
(be warned that this will re-index all of the full-text indices for the entire database)
</p>
<pre>
CALL FTL_DROP_INDEX('PUBLIC', 'TEST');
</pre>
<p>
To get the raw data, use <code>FTL_SEARCH_DATA('Hello', 0, 0);</code>. To get the raw data, use <code>FTL_SEARCH_DATA('Hello', 0, 0);</code>.
The result contains the columns <code>SCHEMA</code> (the schema name), The result contains the columns <code>SCHEMA</code> (the schema name),
<code>TABLE</code> (the table name), <code>TABLE</code> (the table name),
......
...@@ -106,6 +106,7 @@ public class FullTextLucene extends FullText { ...@@ -106,6 +106,7 @@ public class FullTextLucene extends FullText {
stat.execute("CREATE TABLE IF NOT EXISTS " + SCHEMA stat.execute("CREATE TABLE IF NOT EXISTS " + SCHEMA
+ ".INDEXES(SCHEMA VARCHAR, TABLE VARCHAR, COLUMNS VARCHAR, PRIMARY KEY(SCHEMA, TABLE))"); + ".INDEXES(SCHEMA VARCHAR, TABLE VARCHAR, COLUMNS VARCHAR, PRIMARY KEY(SCHEMA, TABLE))");
stat.execute("CREATE ALIAS IF NOT EXISTS FTL_CREATE_INDEX FOR \"" + FullTextLucene.class.getName() + ".createIndex\""); stat.execute("CREATE ALIAS IF NOT EXISTS FTL_CREATE_INDEX FOR \"" + FullTextLucene.class.getName() + ".createIndex\"");
stat.execute("CREATE ALIAS IF NOT EXISTS FTL_DROP_INDEX FOR \"" + FullTextLucene.class.getName() + ".dropIndex\"");
stat.execute("CREATE ALIAS IF NOT EXISTS FTL_SEARCH FOR \"" + FullTextLucene.class.getName() + ".search\""); stat.execute("CREATE ALIAS IF NOT EXISTS FTL_SEARCH FOR \"" + FullTextLucene.class.getName() + ".search\"");
stat.execute("CREATE ALIAS IF NOT EXISTS FTL_SEARCH_DATA FOR \"" + FullTextLucene.class.getName() + ".searchData\""); stat.execute("CREATE ALIAS IF NOT EXISTS FTL_SEARCH_DATA FOR \"" + FullTextLucene.class.getName() + ".searchData\"");
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\"");
...@@ -138,6 +139,29 @@ public class FullTextLucene extends FullText { ...@@ -138,6 +139,29 @@ public class FullTextLucene extends FullText {
indexExistingRows(conn, schema, table); indexExistingRows(conn, schema, table);
} }
/**
* Drop an existing full text index for a table. This method returns
* silently if no index for this table exists.
*
* @param conn the connection
* @param schema the schema name of the table (case sensitive)
* @param table the table name (case sensitive)
*/
public static void dropIndex(Connection conn, String schema, String table) throws SQLException {
init(conn);
PreparedStatement prep = conn.prepareStatement("DELETE FROM " + SCHEMA
+ ".INDEXES WHERE SCHEMA=? AND TABLE=?");
prep.setString(1, schema);
prep.setString(2, table);
int rowCount = prep.executeUpdate();
if (rowCount == 0) {
return;
}
reindex(conn);
}
/** /**
* Re-creates the full text index for this database. Calling this method is * Re-creates the full text index for this database. Calling this method is
* usually not needed, as the index is kept up-to-date automatically. * usually not needed, as the index is kept up-to-date automatically.
...@@ -233,9 +257,15 @@ public class FullTextLucene extends FullText { ...@@ -233,9 +257,15 @@ public class FullTextLucene extends FullText {
* @param table the table name * @param table the table name
*/ */
protected static void createTrigger(Connection conn, String schema, String table) throws SQLException { protected static void createTrigger(Connection conn, String schema, String table) throws SQLException {
createOrDropTrigger(conn, schema, table, true);
}
private static void createOrDropTrigger(Connection conn,
String schema, String table, boolean create) 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);
stat.execute("DROP TRIGGER IF EXISTS " + trigger); stat.execute("DROP TRIGGER IF EXISTS " + trigger);
if (create) {
StringBuilder buff = new StringBuilder("CREATE TRIGGER IF NOT EXISTS "); StringBuilder buff = new StringBuilder("CREATE TRIGGER IF NOT EXISTS ");
// the trigger is also called on rollback because transaction rollback // the trigger is also called on rollback because transaction rollback
// will not undo the changes in the Lucene index // will not undo the changes in the Lucene index
...@@ -249,6 +279,7 @@ public class FullTextLucene extends FullText { ...@@ -249,6 +279,7 @@ public class FullTextLucene extends FullText {
append('\"'); append('\"');
stat.execute(buff.toString()); stat.execute(buff.toString());
} }
}
/** /**
* Get the index writer/searcher wrapper for the given connection. * Get the index writer/searcher wrapper for the given connection.
......
/* /*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, * Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, Version
* Version 1.0, and under the Eclipse Public License, Version 1.0 * 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). * (http://h2database.com/html/license.html). Initial Developer: H2 Group
* Initial Developer: H2 Group
*/ */
package org.h2.test.db; package org.h2.test.db;
import java.io.Reader; import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.sql.PreparedStatement;
import java.sql.ResultSet; import java.sql.ResultSet;
...@@ -31,6 +32,7 @@ public class TestFullText extends TestBase { ...@@ -31,6 +32,7 @@ public class TestFullText extends TestBase {
* The words used in this test. * The words used in this test.
*/ */
static final String[] KNOWN_WORDS = { "skiing", "balance", "storage", "water", "train" }; static final String[] KNOWN_WORDS = { "skiing", "balance", "storage", "water", "train" };
private static final String luceneFullTextClassName = "org.h2.fulltext.FullTextLucene";
/** /**
* Run just this test. * Run just this test.
...@@ -46,15 +48,16 @@ public class TestFullText extends TestBase { ...@@ -46,15 +48,16 @@ public class TestFullText extends TestBase {
testAutoAnalyze(); testAutoAnalyze();
testNativeFeatures(); testNativeFeatures();
testTransaction(false); testTransaction(false);
testCreateDrop(); testCreateDropNative();
testStreamLob(); testStreamLob();
test(false, "VARCHAR"); test(false, "VARCHAR");
test(false, "CLOB"); test(false, "CLOB");
testPerformance(false); testPerformance(false);
testReopen(false); testReopen(false);
String luceneFullTextClassName = "org.h2.fulltext.FullTextLucene"; testDropIndex(false);
try { try {
Class.forName(luceneFullTextClassName); Class.forName(luceneFullTextClassName);
testCreateDropLucene();
testUuidPrimaryKey(true); testUuidPrimaryKey(true);
testMultiThreaded(true); testMultiThreaded(true);
testMultiThreaded(false); testMultiThreaded(false);
...@@ -63,6 +66,7 @@ public class TestFullText extends TestBase { ...@@ -63,6 +66,7 @@ public class TestFullText extends TestBase {
test(true, "CLOB"); test(true, "CLOB");
testPerformance(true); testPerformance(true);
testReopen(true); testReopen(true);
testDropIndex(true);
} catch (ClassNotFoundException e) { } catch (ClassNotFoundException e) {
println("Class not found, not tested: " + luceneFullTextClassName); println("Class not found, not tested: " + luceneFullTextClassName);
// ok // ok
...@@ -170,11 +174,10 @@ public class TestFullText extends TestBase { ...@@ -170,11 +174,10 @@ public class TestFullText extends TestBase {
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
String prefix = lucene ? "FTL" : "FT"; String prefix = lucene ? "FTL" : "FT";
String className = lucene ? "FullTextLucene" : "FullText"; String className = lucene ? "FullTextLucene" : "FullText";
stat.execute("CREATE ALIAS IF NOT EXISTS " + prefix + "_INIT FOR \"org.h2.fulltext." + className + ".init\""); initFullText(stat, lucene);
stat.execute("CALL " + prefix + "_INIT()");
stat.execute("CREATE TABLE TEST(ID UUID PRIMARY KEY, NAME VARCHAR)"); stat.execute("CREATE TABLE TEST(ID UUID PRIMARY KEY, NAME VARCHAR)");
String id = UUID.randomUUID().toString(); String id = UUID.randomUUID().toString();
stat.execute("INSERT INTO TEST VALUES('"+ id +"', 'Hello World')"); stat.execute("INSERT INTO TEST VALUES('" + id + "', 'Hello World')");
stat.execute("CALL " + prefix + "_CREATE_INDEX('PUBLIC', 'TEST', 'NAME')"); stat.execute("CALL " + prefix + "_CREATE_INDEX('PUBLIC', 'TEST', 'NAME')");
ResultSet rs = stat.executeQuery("SELECT * FROM " + prefix + "_SEARCH('Hello', 0, 0)"); ResultSet rs = stat.executeQuery("SELECT * FROM " + prefix + "_SEARCH('Hello', 0, 0)");
assertTrue(rs.next()); assertTrue(rs.next());
...@@ -196,9 +199,7 @@ public class TestFullText extends TestBase { ...@@ -196,9 +199,7 @@ public class TestFullText extends TestBase {
ArrayList<Connection> connList = new ArrayList<Connection>(); ArrayList<Connection> connList = new ArrayList<Connection>();
Connection conn = getConnection("fullTextTransaction", connList); Connection conn = getConnection("fullTextTransaction", connList);
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
String className = lucene ? "FullTextLucene" : "FullText"; initFullText(stat, lucene);
stat.execute("CREATE ALIAS IF NOT EXISTS " + prefix + "_INIT FOR \"org.h2.fulltext." + className + ".init\"");
stat.execute("CALL " + prefix + "_INIT()");
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 " + prefix + "_CREATE_INDEX('PUBLIC', 'TEST', NULL)"); stat.execute("CALL " + prefix + "_CREATE_INDEX('PUBLIC', 'TEST', NULL)");
...@@ -236,11 +237,8 @@ public class TestFullText extends TestBase { ...@@ -236,11 +237,8 @@ public class TestFullText extends TestBase {
// getConnection("fullText;MULTI_THREADED=1;LOCK_TIMEOUT=10000"); // getConnection("fullText;MULTI_THREADED=1;LOCK_TIMEOUT=10000");
final Connection conn = getConnection("fullText", connList); final Connection conn = getConnection("fullText", connList);
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
String className = lucene ? "FullTextLucene" : "FullText"; initFullText(stat, lucene);
stat.execute("CREATE ALIAS IF NOT EXISTS " + prefix + "_INIT FOR \"org.h2.fulltext." + className + ".init\""); initFullText(stat, lucene);
stat.execute("CALL " + prefix + "_INIT()");
stat.execute("CREATE ALIAS IF NOT EXISTS " + prefix + "_INIT FOR \"org.h2.fulltext." + className + ".init\"");
stat.execute("CALL " + prefix + "_INIT()");
final String tableName = "TEST" + i; final String tableName = "TEST" + i;
stat.execute("CREATE TABLE " + tableName + "(ID INT PRIMARY KEY, DATA VARCHAR)"); stat.execute("CREATE TABLE " + tableName + "(ID INT PRIMARY KEY, DATA VARCHAR)");
stat.execute("CALL " + prefix + "_CREATE_INDEX('PUBLIC', '" + tableName + "', NULL)"); stat.execute("CALL " + prefix + "_CREATE_INDEX('PUBLIC', '" + tableName + "', NULL)");
...@@ -265,7 +263,8 @@ public class TestFullText extends TestBase { ...@@ -265,7 +263,8 @@ public class TestFullText extends TestBase {
x++; x++;
for (String knownWord : KNOWN_WORDS) { for (String knownWord : KNOWN_WORDS) {
trace("searching for " + knownWord + " with " + Thread.currentThread()); trace("searching for " + knownWord + " with " + Thread.currentThread());
ResultSet rs = stat.executeQuery("SELECT * FROM " + prefix + "_SEARCH('" + knownWord + "', 0, 0)"); ResultSet rs = stat.executeQuery("SELECT * FROM " + prefix + "_SEARCH('" + knownWord
+ "', 0, 0)");
assertTrue(rs.next()); assertTrue(rs.next());
} }
} }
...@@ -308,9 +307,11 @@ public class TestFullText extends TestBase { ...@@ -308,9 +307,11 @@ public class TestFullText extends TestBase {
final int length = 1024 * 1024; final int length = 1024 * 1024;
prep.setCharacterStream(1, new Reader() { prep.setCharacterStream(1, new Reader() {
int remaining = length; int remaining = length;
public void close() { public void close() {
// ignore // ignore
} }
public int read(char[] buff, int off, int len) { public int read(char[] buff, int off, int len) {
if (remaining >= len) { if (remaining >= len) {
remaining -= len; remaining -= len;
...@@ -330,7 +331,7 @@ public class TestFullText extends TestBase { ...@@ -330,7 +331,7 @@ public class TestFullText extends TestBase {
deleteDb("fullText"); deleteDb("fullText");
} }
private void testCreateDrop() throws SQLException { private void testCreateDropNative() throws SQLException {
deleteDb("fullText"); deleteDb("fullText");
FileUtils.deleteRecursive(getBaseDir() + "/fullText", false); FileUtils.deleteRecursive(getBaseDir() + "/fullText", false);
Connection conn = getConnection("fullText"); Connection conn = getConnection("fullText");
...@@ -346,6 +347,27 @@ public class TestFullText extends TestBase { ...@@ -346,6 +347,27 @@ public class TestFullText extends TestBase {
FileUtils.deleteRecursive(getBaseDir() + "/fullText", false); FileUtils.deleteRecursive(getBaseDir() + "/fullText", false);
} }
private void testCreateDropLucene() throws SQLException, SecurityException, NoSuchMethodException,
ClassNotFoundException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
deleteDb("fullText");
FileUtils.deleteRecursive(getBaseDir() + "/fullText", false);
Connection conn = getConnection("fullText");
Statement stat = conn.createStatement();
initFullText(stat, true);
stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
Method createIndexMethod = Class.forName(luceneFullTextClassName).getMethod("createIndex",
new Class[] { java.sql.Connection.class, String.class, String.class, String.class });
Method dropIndexMethod = Class.forName(luceneFullTextClassName).getMethod("dropIndex",
new Class[] { java.sql.Connection.class, String.class, String.class });
for (int i = 0; i < 10; i++) {
createIndexMethod.invoke(null, conn, "PUBLIC", "TEST", null);
dropIndexMethod.invoke(null, conn, "PUBLIC", "TEST");
}
conn.close();
deleteDb("fullText");
FileUtils.deleteRecursive(getBaseDir() + "/fullText", false);
}
private void testReopen(boolean lucene) throws SQLException { private void testReopen(boolean lucene) throws SQLException {
if (config.memory) { if (config.memory) {
return; return;
...@@ -355,9 +377,7 @@ public class TestFullText extends TestBase { ...@@ -355,9 +377,7 @@ public class TestFullText extends TestBase {
FileUtils.deleteRecursive(getBaseDir() + "/fullTextReopen", false); FileUtils.deleteRecursive(getBaseDir() + "/fullTextReopen", false);
Connection conn = getConnection("fullTextReopen"); Connection conn = getConnection("fullTextReopen");
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
String className = lucene ? "FullTextLucene" : "FullText"; initFullText(stat, lucene);
stat.execute("CREATE ALIAS IF NOT EXISTS " + prefix + "_INIT FOR \"org.h2.fulltext." + className + ".init\"");
stat.execute("CALL " + prefix + "_INIT()");
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 " + prefix + "_CREATE_INDEX('PUBLIC', 'TEST', NULL)"); stat.execute("CALL " + prefix + "_CREATE_INDEX('PUBLIC', 'TEST', NULL)");
...@@ -387,9 +407,7 @@ public class TestFullText extends TestBase { ...@@ -387,9 +407,7 @@ public class TestFullText extends TestBase {
Connection conn = getConnection("fullText"); Connection conn = getConnection("fullText");
String prefix = lucene ? "FTL" : "FT"; String prefix = lucene ? "FTL" : "FT";
Statement stat = conn.createStatement(); Statement stat = conn.createStatement();
String className = lucene ? "FullTextLucene" : "FullText"; initFullText(stat, lucene);
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 AS SELECT * FROM INFORMATION_SCHEMA.HELP"); stat.execute("CREATE TABLE TEST AS SELECT * FROM INFORMATION_SCHEMA.HELP");
stat.execute("ALTER TABLE TEST ALTER COLUMN ID INT NOT NULL"); stat.execute("ALTER TABLE TEST ALTER COLUMN ID INT NOT NULL");
...@@ -523,4 +541,36 @@ public class TestFullText extends TestBase { ...@@ -523,4 +541,36 @@ public class TestFullText extends TestBase {
close(connList); close(connList);
} }
private void testDropIndex(boolean lucene) throws SQLException {
if (config.memory) {
return;
}
deleteDb("fullTextDropIndex");
String prefix = lucene ? "FTL" : "FT";
FileUtils.deleteRecursive(getBaseDir() + "/fullTextDropIndex", false);
Connection conn = getConnection("fullTextDropIndex");
Statement stat = conn.createStatement();
initFullText(stat, lucene);
stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME1 VARCHAR, NAME2 VARCHAR)");
stat.execute("INSERT INTO TEST VALUES(1, 'Hello World', 'Hello Again')");
stat.execute("CALL " + prefix + "_CREATE_INDEX('PUBLIC', 'TEST', 'NAME1')");
stat.execute("UPDATE TEST SET NAME1=NULL WHERE ID=1");
stat.execute("UPDATE TEST SET NAME1='Hello World' WHERE ID=1");
stat.execute("CALL " + prefix + "_DROP_INDEX('PUBLIC', 'TEST')");
stat.execute("CALL " + prefix + "_CREATE_INDEX('PUBLIC', 'TEST', 'NAME1, NAME2')");
stat.execute("UPDATE TEST SET NAME2=NULL WHERE ID=1");
stat.execute("UPDATE TEST SET NAME2='Hello World' WHERE ID=1");
conn.close();
conn.close();
FileUtils.deleteRecursive(getBaseDir() + "/fullTextDropIndex", false);
}
private static void initFullText(Statement stat, boolean lucene) throws SQLException {
String prefix = lucene ? "FTL" : "FT";
String className = lucene ? "FullTextLucene" : "FullText";
stat.execute("CREATE ALIAS IF NOT EXISTS " + prefix + "_INIT FOR \"org.h2.fulltext." + className + ".init\"");
stat.execute("CALL " + prefix + "_INIT()");
}
} }
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论