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

support drop index for Lucene full-text indices

上级 78a4f621
......@@ -49,6 +49,7 @@ Change Log
</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>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>
<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
QUERY: "PUBLIC"."TEST" WHERE "ID"=1
</pre>
<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>.
The result contains the columns <code>SCHEMA</code> (the schema 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
QUERY: "PUBLIC"."TEST" WHERE "ID"=1
</pre>
<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>.
The result contains the columns <code>SCHEMA</code> (the schema name),
<code>TABLE</code> (the table name),
......
......@@ -106,6 +106,7 @@ public class FullTextLucene extends FullText {
stat.execute("CREATE TABLE IF NOT EXISTS " + SCHEMA
+ ".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_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_DATA FOR \"" + FullTextLucene.class.getName() + ".searchData\"");
stat.execute("CREATE ALIAS IF NOT EXISTS FTL_REINDEX FOR \"" + FullTextLucene.class.getName() + ".reindex\"");
......@@ -138,6 +139,29 @@ public class FullTextLucene extends FullText {
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
* usually not needed, as the index is kept up-to-date automatically.
......@@ -233,21 +257,28 @@ public class FullTextLucene extends FullText {
* @param table the table name
*/
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();
String trigger = StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(TRIGGER_PREFIX + table);
stat.execute("DROP TRIGGER IF EXISTS " + trigger);
StringBuilder buff = new StringBuilder("CREATE TRIGGER IF NOT EXISTS ");
// the trigger is also called on rollback because transaction rollback
// will not undo the changes in the Lucene index
buff.append(trigger).
append(" AFTER INSERT, UPDATE, DELETE, ROLLBACK ON ").
append(StringUtils.quoteIdentifier(schema)).
append('.').
append(StringUtils.quoteIdentifier(table)).
append(" FOR EACH ROW CALL \"").
append(FullTextLucene.FullTextTrigger.class.getName()).
append('\"');
stat.execute(buff.toString());
if (create) {
StringBuilder buff = new StringBuilder("CREATE TRIGGER IF NOT EXISTS ");
// the trigger is also called on rollback because transaction rollback
// will not undo the changes in the Lucene index
buff.append(trigger).
append(" AFTER INSERT, UPDATE, DELETE, ROLLBACK ON ").
append(StringUtils.quoteIdentifier(schema)).
append('.').
append(StringUtils.quoteIdentifier(table)).
append(" FOR EACH ROW CALL \"").
append(FullTextLucene.FullTextTrigger.class.getName()).
append('\"');
stat.execute(buff.toString());
}
}
/**
......
/*
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License,
* Version 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
* Copyright 2004-2011 H2 Group. Multiple-Licensed under the H2 License, Version
* 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). Initial Developer: H2 Group
*/
package org.h2.test.db;
import java.io.Reader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
......@@ -31,10 +32,11 @@ public class TestFullText extends TestBase {
* The words used in this test.
*/
static final String[] KNOWN_WORDS = { "skiing", "balance", "storage", "water", "train" };
private static final String luceneFullTextClassName = "org.h2.fulltext.FullTextLucene";
/**
* Run just this test.
*
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
......@@ -46,15 +48,16 @@ public class TestFullText extends TestBase {
testAutoAnalyze();
testNativeFeatures();
testTransaction(false);
testCreateDrop();
testCreateDropNative();
testStreamLob();
test(false, "VARCHAR");
test(false, "CLOB");
testPerformance(false);
testReopen(false);
String luceneFullTextClassName = "org.h2.fulltext.FullTextLucene";
testDropIndex(false);
try {
Class.forName(luceneFullTextClassName);
testCreateDropLucene();
testUuidPrimaryKey(true);
testMultiThreaded(true);
testMultiThreaded(false);
......@@ -63,6 +66,7 @@ public class TestFullText extends TestBase {
test(true, "CLOB");
testPerformance(true);
testReopen(true);
testDropIndex(true);
} catch (ClassNotFoundException e) {
println("Class not found, not tested: " + luceneFullTextClassName);
// ok
......@@ -170,11 +174,10 @@ public class TestFullText extends TestBase {
Statement stat = conn.createStatement();
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()");
initFullText(stat, lucene);
stat.execute("CREATE TABLE TEST(ID UUID PRIMARY KEY, NAME VARCHAR)");
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')");
ResultSet rs = stat.executeQuery("SELECT * FROM " + prefix + "_SEARCH('Hello', 0, 0)");
assertTrue(rs.next());
......@@ -196,9 +199,7 @@ public class TestFullText extends TestBase {
ArrayList<Connection> connList = new ArrayList<Connection>();
Connection conn = getConnection("fullTextTransaction", connList);
Statement stat = conn.createStatement();
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()");
initFullText(stat, lucene);
stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
stat.execute("INSERT INTO TEST VALUES(1, 'Hello World')");
stat.execute("CALL " + prefix + "_CREATE_INDEX('PUBLIC', 'TEST', NULL)");
......@@ -236,11 +237,8 @@ public class TestFullText extends TestBase {
// getConnection("fullText;MULTI_THREADED=1;LOCK_TIMEOUT=10000");
final Connection conn = getConnection("fullText", connList);
Statement stat = conn.createStatement();
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()");
stat.execute("CREATE ALIAS IF NOT EXISTS " + prefix + "_INIT FOR \"org.h2.fulltext." + className + ".init\"");
stat.execute("CALL " + prefix + "_INIT()");
initFullText(stat, lucene);
initFullText(stat, lucene);
final String tableName = "TEST" + i;
stat.execute("CREATE TABLE " + tableName + "(ID INT PRIMARY KEY, DATA VARCHAR)");
stat.execute("CALL " + prefix + "_CREATE_INDEX('PUBLIC', '" + tableName + "', NULL)");
......@@ -265,7 +263,8 @@ public class TestFullText extends TestBase {
x++;
for (String knownWord : KNOWN_WORDS) {
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());
}
}
......@@ -308,9 +307,11 @@ public class TestFullText extends TestBase {
final int length = 1024 * 1024;
prep.setCharacterStream(1, new Reader() {
int remaining = length;
public void close() {
// ignore
}
public int read(char[] buff, int off, int len) {
if (remaining >= len) {
remaining -= len;
......@@ -330,7 +331,7 @@ public class TestFullText extends TestBase {
deleteDb("fullText");
}
private void testCreateDrop() throws SQLException {
private void testCreateDropNative() throws SQLException {
deleteDb("fullText");
FileUtils.deleteRecursive(getBaseDir() + "/fullText", false);
Connection conn = getConnection("fullText");
......@@ -346,6 +347,27 @@ public class TestFullText extends TestBase {
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 {
if (config.memory) {
return;
......@@ -355,9 +377,7 @@ public class TestFullText extends TestBase {
FileUtils.deleteRecursive(getBaseDir() + "/fullTextReopen", false);
Connection conn = getConnection("fullTextReopen");
Statement stat = conn.createStatement();
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()");
initFullText(stat, lucene);
stat.execute("CREATE TABLE TEST(ID INT PRIMARY KEY, NAME VARCHAR)");
stat.execute("INSERT INTO TEST VALUES(1, 'Hello World')");
stat.execute("CALL " + prefix + "_CREATE_INDEX('PUBLIC', 'TEST', NULL)");
......@@ -387,9 +407,7 @@ public class TestFullText extends TestBase {
Connection conn = getConnection("fullText");
String prefix = lucene ? "FTL" : "FT";
Statement stat = conn.createStatement();
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()");
initFullText(stat, lucene);
stat.execute("DROP TABLE IF EXISTS TEST");
stat.execute("CREATE TABLE TEST AS SELECT * FROM INFORMATION_SCHEMA.HELP");
stat.execute("ALTER TABLE TEST ALTER COLUMN ID INT NOT NULL");
......@@ -523,4 +541,36 @@ public class TestFullText extends TestBase {
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 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论