Option to add triggers when enabling FTS (#57)

--create-triggers CLI option and create_triggers=True in the Python library

* Add an option to create triggers for fts table.
* Add cli option for the create-update-trigger.
* Add tests for the create-update-trigger option.
* Change FTS table escaping to square brackets.
This commit is contained in:
Amjith Ramanujam 2019-09-02 16:42:28 -07:00 committed by Simon Willison
commit 405e092d59
4 changed files with 128 additions and 17 deletions

View file

@ -315,6 +315,7 @@ def test_index_foreign_keys(db_path):
db = Database(db_path)
assert [] == db["books"].indexes
result = CliRunner().invoke(cli.cli, ["index-foreign-keys", db_path])
assert 0 == result.exit_code
assert [["author_id"], ["author_name_ref"]] == [
i.columns for i in db["books"].indexes
]
@ -328,6 +329,43 @@ def test_enable_fts(db_path):
assert 0 == result.exit_code
assert "Gosh_fts" == Database(db_path)["Gosh"].detect_fts()
# Table names with restricted chars are handled correctly.
# colons and dots are restricted characters for table names.
Database(db_path)["http://example.com"].create({"c1": str, "c2": str, "c3": str})
assert None == Database(db_path)["http://example.com"].detect_fts()
result = CliRunner().invoke(
cli.cli, ["enable-fts", db_path, "http://example.com", "c1", "--fts4"]
)
assert 0 == result.exit_code
assert (
"http://example.com_fts" == Database(db_path)["http://example.com"].detect_fts()
)
Database(db_path)["http://example.com"].drop()
def test_enable_fts_with_triggers(db_path):
Database(db_path)["Gosh"].insert_all([{"c1": "baz"}])
exit_code = (
CliRunner()
.invoke(
cli.cli,
["enable-fts", db_path, "Gosh", "c1", "--fts4", "--create-triggers"],
)
.exit_code
)
assert 0 == exit_code
def search(q):
return (
Database(db_path)
.conn.execute("select c1 from Gosh_fts where c1 match ?", [q])
.fetchall()
)
assert [("baz",)] == search("baz")
Database(db_path)["Gosh"].insert_all([{"c1": "martha"}])
assert [("martha",)] == search("martha")
def test_populate_fts(db_path):
Database(db_path)["Gosh"].insert_all([{"c1": "baz"}])

View file

@ -22,6 +22,26 @@ def test_enable_fts(fresh_db):
assert [] == table.search("bar")
def test_enable_fts_escape_table_names(fresh_db):
# Table names with restricted chars are handled correctly.
# colons and dots are restricted characters for table names.
table = fresh_db["http://example.com"]
table.insert_all(search_records)
assert ["http://example.com"] == fresh_db.table_names()
table.enable_fts(["text", "country"], fts_version="FTS4")
assert [
"http://example.com",
"http://example.com_fts",
"http://example.com_fts_segments",
"http://example.com_fts_segdir",
"http://example.com_fts_docsize",
"http://example.com_fts_stat",
] == fresh_db.table_names()
assert [("tanuki are tricksters", "Japan", "foo")] == table.search("tanuki")
assert [("racoons are trash pandas", "USA", "bar")] == table.search("usa")
assert [] == table.search("bar")
def test_populate_fts(fresh_db):
table = fresh_db["populatable"]
table.insert(search_records[0])
@ -34,6 +54,19 @@ def test_populate_fts(fresh_db):
assert [("racoons are trash pandas", "USA", "bar")] == table.search("usa")
def test_populate_fts_escape_table_names(fresh_db):
# Restricted characters such as colon and dots should be escaped.
table = fresh_db["http://example.com"]
table.insert(search_records[0])
table.enable_fts(["text", "country"], fts_version="FTS4")
assert [] == table.search("trash pandas")
table.insert(search_records[1])
assert [] == table.search("trash pandas")
# Now run populate_fts to make this record available
table.populate_fts(["text", "country"])
assert [("racoons are trash pandas", "USA", "bar")] == table.search("usa")
def test_optimize_fts(fresh_db):
for fts_version in ("4", "5"):
table_name = "searchable_{}".format(fts_version)
@ -48,3 +81,14 @@ def test_optimize_fts(fresh_db):
"searchable_5_fts",
):
fresh_db[table_name].optimize()
def test_enable_fts_w_triggers(fresh_db):
table = fresh_db["searchable"]
table.insert(search_records[0])
table.enable_fts(["text", "country"], fts_version="FTS4", create_triggers=True)
assert [("tanuki are tricksters", "Japan", "foo")] == table.search("tanuki")
table.insert(search_records[1])
# Triggers will auto-populate FTS virtual table, not need to call populate_fts()
assert [("racoons are trash pandas", "USA", "bar")] == table.search("usa")
assert [] == table.search("bar")