/** * Initializes full text search functionality for this database. This adds the following Java * functions to the database: * * <ul> * <li>FTL_CREATE_INDEX(schemaNameString, tableNameString, columnListString) * <li>FTL_SEARCH(queryString, limitInt, offsetInt): result set * <li>FTL_REINDEX() * <li>FTL_DROP_ALL() * </ul> * * It also adds a schema FTL to the database where bookkeeping information is stored. This * function may be called from a Java application, or by using the SQL statements: * * <pre> * CREATE ALIAS IF NOT EXISTS FTL_INIT FOR * "org.h2.fulltext.FullTextLucene2.init"; * CALL FTL_INIT(); * </pre> * * @param conn the connection */ public static void init(Connection conn) throws SQLException { Statement stat = conn.createStatement(); stat.execute("CREATE SCHEMA IF NOT EXISTS " + SCHEMA); 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 \"" + FullTextLucene2.class.getName() + ".createIndex\""); stat.execute( "CREATE ALIAS IF NOT EXISTS FTL_SEARCH FOR \"" + FullTextLucene2.class.getName() + ".search\""); stat.execute( "CREATE ALIAS IF NOT EXISTS FTL_SEARCH_DATA FOR \"" + FullTextLucene2.class.getName() + ".searchData\""); stat.execute( "CREATE ALIAS IF NOT EXISTS FTL_REINDEX FOR \"" + FullTextLucene2.class.getName() + ".reindex\""); stat.execute( "CREATE ALIAS IF NOT EXISTS FTL_DROP_ALL FOR \"" + FullTextLucene2.class.getName() + ".dropAll\""); try { getIndexAccess(conn); } catch (SQLException e) { throw convertException(e); } }
/** * 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. * * @param conn the connection */ public static void reindex(Connection conn) throws SQLException { init(conn); removeAllTriggers(conn, TRIGGER_PREFIX); removeIndexFiles(conn); Statement stat = conn.createStatement(); ResultSet rs = stat.executeQuery("SELECT * FROM " + SCHEMA + ".INDEXES"); while (rs.next()) { String schema = rs.getString("SCHEMA"); String table = rs.getString("TABLE"); createTrigger(conn, schema, table); indexExistingRows(conn, schema, table); } }
/** * Get the path of the Lucene index for this database. * * @param conn the database connection * @return the path */ protected static String getIndexPath(Connection conn) throws SQLException { Statement stat = conn.createStatement(); ResultSet rs = stat.executeQuery("CALL DATABASE_PATH()"); rs.next(); String path = rs.getString(1); if (path == null) { throw throwException("Fulltext search for in-memory databases is not supported."); } int index = path.lastIndexOf(':'); // position 1 means a windows drive letter is used, ignore that if (index > 1) { path = path.substring(index + 1); } rs.close(); return path; }
/** * Add the existing data to the index. * * @param conn the database connection * @param schema the schema name * @param table the table name */ protected static void indexExistingRows(Connection conn, String schema, String table) throws SQLException { FullTextLucene2.FullTextTrigger existing = new FullTextLucene2.FullTextTrigger(); existing.init(conn, schema, null, table, false, Trigger.INSERT); String sql = "SELECT * FROM " + StringUtils.quoteIdentifier(schema) + "." + StringUtils.quoteIdentifier(table); ResultSet rs = conn.createStatement().executeQuery(sql); int columnCount = rs.getMetaData().getColumnCount(); while (rs.next()) { Object[] row = new Object[columnCount]; for (int i = 0; i < columnCount; i++) { row[i] = rs.getObject(i + 1); } // existing.fire(conn, null, row); existing.insert(row, false); } existing.commitIndex(); }
/** * Create the trigger. * * @param conn the database connection * @param schema the schema name * @param table the table name */ protected static void createTrigger(Connection conn, String schema, String table) 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(FullTextLucene2.FullTextTrigger.class.getName()) .append('\"'); stat.execute(buff.toString()); }
/** * Drops all full text indexes from the database. * * @param conn the connection */ public static void dropAll(Connection conn) throws SQLException { Statement stat = conn.createStatement(); stat.execute("DROP SCHEMA IF EXISTS " + SCHEMA); removeAllTriggers(conn, TRIGGER_PREFIX); removeIndexFiles(conn); }