package marubinotto.h2.fulltext; import static marubinotto.h2.fulltext.FullTextSearch.SCHEMA; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.Map; import java.util.Set; import java.util.TreeSet; import org.h2.api.Trigger; import org.h2.util.New; public class IndexUpdateTrigger implements Trigger { protected FullTextSearchContext context; protected IndexedTableInfo tableInfo; private PreparedStatement prepInsertWord, prepInsertRow, prepInsertMap, prepDeleteRow, prepDeleteMap, prepSelectRow; public void init( Connection conn, String schemaName, String triggerName, String tableName, boolean before, int type) throws SQLException { this.context = FullTextSearchContext.getContext(conn); this.tableInfo = IndexedTableInfo.newInstance(conn, schemaName, tableName); this.context.addIndexedTableInfo(this.tableInfo); this.prepInsertWord = conn.prepareStatement( "INSERT INTO " + SCHEMA + ".WORDS(NAME) VALUES(?)"); this.prepInsertRow = conn.prepareStatement( "INSERT INTO " + SCHEMA + ".ROWS(HASH, INDEXID, KEY) VALUES(?, ?, ?)"); this.prepInsertMap = conn.prepareStatement( "INSERT INTO " + SCHEMA + ".MAP(ROWID, WORDID) VALUES(?, ?)"); this.prepDeleteRow = conn.prepareStatement( "DELETE FROM " + SCHEMA + ".ROWS WHERE HASH=? AND INDEXID=? AND KEY=?"); this.prepDeleteMap = conn.prepareStatement( "DELETE FROM " + SCHEMA + ".MAP WHERE ROWID=? AND WORDID=?"); this.prepSelectRow = conn.prepareStatement( "SELECT ID FROM " + SCHEMA + ".ROWS WHERE HASH=? AND INDEXID=? AND KEY=?"); } public void fire(Connection conn, Object[] oldRow, Object[] newRow) throws SQLException { if (oldRow != null) { if (newRow != null) { // update if (this.tableInfo.haveIndexedColumnsChanged(oldRow, newRow)) { delete(oldRow); insert(newRow); } } else { // delete delete(oldRow); } } else if (newRow != null) { // insert insert(newRow); } } /** * Entries will be added to WORDS, ROWS, MAP */ protected void insert(Object[] row) throws SQLException { // Add an entry to ROWS String condition = this.tableInfo.createConditionSqlWithKeys(row); int hash = condition.hashCode(); this.prepInsertRow.setInt(1, hash); this.prepInsertRow.setInt(2, this.tableInfo.id); this.prepInsertRow.setString(3, condition); this.prepInsertRow.execute(); ResultSet rs = this.prepInsertRow.getGeneratedKeys(); rs.next(); int rowId = rs.getInt(1); // Add entries to MAP this.prepInsertMap.setInt(1, rowId); Set<Integer> wordIds = getWordIds(row); for (int id : wordIds) { this.prepInsertMap.setInt(2, id); this.prepInsertMap.execute(); } } /** * Entries will be deleted from MAP, ROWS * - WORDS will remain unchanged */ protected void delete(Object[] row) throws SQLException { String condition = this.tableInfo.createConditionSqlWithKeys(row); int hash = condition.hashCode(); this.prepSelectRow.setInt(1, hash); this.prepSelectRow.setInt(2, this.tableInfo.id); this.prepSelectRow.setString(3, condition); ResultSet rs = this.prepSelectRow.executeQuery(); if (rs.next()) { int rowId = rs.getInt(1); this.prepDeleteMap.setInt(1, rowId); Set<Integer> wordIds = getWordIds(row); for (int id : wordIds) { this.prepDeleteMap.setInt(2, id); this.prepDeleteMap.executeUpdate(); } this.prepDeleteRow.setInt(1, hash); this.prepDeleteRow.setInt(2, this.tableInfo.id); this.prepDeleteRow.setString(3, condition); this.prepDeleteRow.executeUpdate(); } } public void close() throws SQLException { this.context.removeIndexedTableInfo(this.tableInfo); } public void remove() throws SQLException { this.context.removeIndexedTableInfo(this.tableInfo); } public Set<Integer> getWordIds(Object[] row) throws SQLException { Set<String> words = New.hashSet(); this.context.splitIntoWords(this.tableInfo, row, words); Map<String, Integer> allWords = this.context.getWordList(); Set<Integer> wordIds = new TreeSet<Integer>(); for (String word : words) { Integer wordId = allWords.get(word); if (wordId == null) { // New word this.prepInsertWord.setString(1, word); this.prepInsertWord.execute(); ResultSet rs = this.prepInsertWord.getGeneratedKeys(); rs.next(); wordId = rs.getInt(1); allWords.put(word, wordId); } wordIds.add(wordId); } return wordIds; } }