'sqlite-utils triggers' command, closes #218

This commit is contained in:
Simon Willison 2021-01-02 19:03:15 -08:00
commit 5b246d17a0
3 changed files with 114 additions and 1 deletions

View file

@ -261,7 +261,7 @@ The ``--nl``, ``--csv``, ``--tsv`` and ``--table`` options are all available.
Listing views
=============
The `views` command shows any views defined in the database::
The ``views`` command shows any views defined in the database::
$ sqlite-utils views sf-trees.db --table --counts --columns --schema
view count columns schema
@ -279,6 +279,34 @@ It takes the same options as the ``tables`` command:
* ``--tsv``
* ``--table``
.. _cli_triggers:
Listing triggers
================
The ``triggers`` command shows any triggers configured for the database::
% sqlite-utils triggers global-power-plants.db --table
name table sql
--------------- --------- -----------------------------------------------------------------
plants_insert plants CREATE TRIGGER [plants_insert] AFTER INSERT ON [plants]
BEGIN
INSERT OR REPLACE INTO [_counts]
VALUES (
'plants',
COALESCE(
(SELECT count FROM [_counts] WHERE [table] = 'plants'),
0
) + 1
);
END
It defaults to showing triggers for all tables. To see triggers for one or more specific tables pass their names as arguments::
% sqlite-utils triggers global-power-plants.db plants
The command takes the same format options as the ``tables`` and ``views`` commands.
.. _cli_analyze_tables:
Analyzing tables

View file

@ -1100,6 +1100,53 @@ def rows(
)
@cli.command()
@click.argument(
"path",
type=click.Path(file_okay=True, dir_okay=False, allow_dash=False),
required=True,
)
@click.argument("tables", nargs=-1)
@output_options
@load_extension_option
@click.pass_context
def triggers(
ctx,
path,
tables,
nl,
arrays,
csv,
tsv,
no_headers,
table,
fmt,
json_cols,
load_extension,
):
"Show triggers configured in this database"
sql = "select name, tbl_name as [table], sql from sqlite_master where type = 'trigger'"
if tables:
quote = sqlite_utils.Database(memory=True).escape
sql += " and [table] in ({})".format(
", ".join(quote(table) for table in tables)
)
ctx.invoke(
query,
path=path,
sql=sql,
nl=nl,
arrays=arrays,
csv=csv,
tsv=tsv,
no_headers=no_headers,
table=table,
fmt=fmt,
json_cols=json_cols,
load_extension=load_extension,
)
@cli.command()
@click.argument(
"path",

View file

@ -5,6 +5,7 @@ import json
import os
import pytest
from sqlite_utils.utils import sqlite3, find_spatialite
import textwrap
from .utils import collapse_whitespace
@ -1736,3 +1737,40 @@ def test_search(tmpdir, fts, extra_arg, expected):
)
assert result.exit_code == 0
assert result.output == expected
_TRIGGERS_EXPECTED = '[{"name": "blah", "table": "articles", "sql": "CREATE TRIGGER blah AFTER INSERT ON articles\\nBEGIN\\n UPDATE counter SET count = count + 1;\\nEND"}]\n'
@pytest.mark.parametrize(
"extra_args,expected",
[([], _TRIGGERS_EXPECTED), (["articles"], _TRIGGERS_EXPECTED), (["counter"], "")],
)
def test_triggers(tmpdir, extra_args, expected):
db_path = str(tmpdir / "test.db")
db = Database(db_path)
db["articles"].insert(
{"id": 1, "title": "Title the first"},
pk="id",
)
db["counter"].insert({"count": 1})
db.conn.execute(
textwrap.dedent(
"""
CREATE TRIGGER blah AFTER INSERT ON articles
BEGIN
UPDATE counter SET count = count + 1;
END
"""
)
)
args = ["triggers", db_path]
if extra_args:
args.extend(extra_args)
result = CliRunner().invoke(
cli.cli,
args,
catch_exceptions=False,
)
assert result.exit_code == 0
assert result.output == expected