From b4f09146d342ba72190f9a8543a44ccd6ea06b02 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sun, 3 Jan 2021 12:41:24 -0800 Subject: [PATCH] table.has_counts_triggers property, refs #219 --- docs/python-api.rst | 18 ++++++++++++++---- sqlite_utils/cli.py | 14 ++++++++++++++ sqlite_utils/db.py | 10 ++++++++++ tests/test_introspect.py | 8 ++++++++ 4 files changed, 46 insertions(+), 4 deletions(-) diff --git a/docs/python-api.rst b/docs/python-api.rst index b02fafa..8f943f6 100644 --- a/docs/python-api.rst +++ b/docs/python-api.rst @@ -1469,7 +1469,7 @@ The ``.triggers`` property lists database triggers. It can be used on both datab >>> db.triggers ... similar output to db["authors"].triggers -The ``table.triggers_dict`` property returns the triggers for that table as a dictionary mapping their names to their SQL definitions. +The ``.triggers_dict`` property returns the triggers for that table as a dictionary mapping their names to their SQL definitions. :: @@ -1491,15 +1491,25 @@ The ``detect_fts()`` method returns the associated SQLite FTS table name, if one :: - >> db["authors"].detect_fts() + >>> db["authors"].detect_fts() "authors_fts" The ``.virtual_table_using`` property reveals if a table is a virtual table. It returns ``None`` for regular tables and the upper case version of the type of virtual table otherwise. For example:: - >> db["authors"].enable_fts(["name"]) - >> db["authors_fts"].virtual_table_using + >>> db["authors"].enable_fts(["name"]) + >>> db["authors_fts"].virtual_table_using "FTS5" +The ``.has_counts_triggers`` property shows if a table has been configured with triggers for updating a ``_counts`` table, as described in :ref:`python_api_cached_table_counts`. + +:: + + >>> db["authors"].has_counts_triggers + False + >>> db["authors"].enable_counts() + >>> db["authors"].has_counts_triggers + True + .. _python_api_fts: Enabling full-text search diff --git a/sqlite_utils/cli.py b/sqlite_utils/cli.py index 0388961..0bc03fe 100644 --- a/sqlite_utils/cli.py +++ b/sqlite_utils/cli.py @@ -571,6 +571,20 @@ def enable_counts(path, tables, load_extension): db[table].enable_counts() +@cli.command(name="reset-counts") +@click.argument( + "path", + type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False), + required=True, +) +@load_extension_option +def reset_counts(path, load_extension): + "Reset calculated counts in the _counts table" + db = sqlite_utils.Database(path) + _load_extensions(db, load_extension) + db.reset_counts() + + def insert_upsert_options(fn): for decorator in reversed( ( diff --git a/sqlite_utils/db.py b/sqlite_utils/db.py index f1f3de6..bc954e5 100644 --- a/sqlite_utils/db.py +++ b/sqlite_utils/db.py @@ -1268,6 +1268,16 @@ class Table(Queryable): self.db.conn.executescript(sql) self.db.use_counts_table = True + @property + def has_counts_triggers(self): + trigger_names = { + "{table}{counts_table}_{suffix}".format( + counts_table=self.db._counts_table_name, table=self.name, suffix=suffix + ) + for suffix in ["insert", "delete"] + } + return trigger_names.issubset(self.triggers_dict.keys()) + def enable_fts( self, columns, diff --git a/tests/test_introspect.py b/tests/test_introspect.py index 9a37001..73102f8 100644 --- a/tests/test_introspect.py +++ b/tests/test_introspect.py @@ -154,6 +154,14 @@ def test_triggers_and_triggers_dict(fresh_db): assert fresh_db.triggers_dict == expected_triggers +def test_has_counts_triggers(fresh_db): + authors = fresh_db["authors"] + authors.insert({"name": "Frank Herbert"}) + assert not authors.has_counts_triggers + authors.enable_counts() + assert authors.has_counts_triggers + + @pytest.mark.parametrize( "sql,expected_name,expected_using", [