From 28dc5aac347ffdecb2dff154d23a73883a2ffabf Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Wed, 2 Jun 2021 21:26:46 -0700 Subject: [PATCH] sqlite-utils indexes command, refs #263 --- docs/cli.rst | 27 ++++++++++++++++ sqlite_utils/cli.py | 61 +++++++++++++++++++++++++++++++++++ tests/test_cli.py | 77 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) diff --git a/docs/cli.rst b/docs/cli.rst index e5ffcfc..41d9a0c 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -293,6 +293,33 @@ It takes the same options as the ``tables`` command: * ``--tsv`` * ``--table`` +.. _cli_indexes: + +Listing indexes +=============== + +The ``indexes`` command lists any indexes configured for the database:: + + $ sqlite-utils indexes covid.db --table + table index_name seqno cid name desc coll key + -------------------------------- ------------------------------------------------------ ------- ----- ----------------- ------ ------ ----- + johns_hopkins_csse_daily_reports idx_johns_hopkins_csse_daily_reports_combined_key 0 12 combined_key 0 BINARY 1 + johns_hopkins_csse_daily_reports idx_johns_hopkins_csse_daily_reports_country_or_region 0 1 country_or_region 0 BINARY 1 + johns_hopkins_csse_daily_reports idx_johns_hopkins_csse_daily_reports_province_or_state 0 2 province_or_state 0 BINARY 1 + johns_hopkins_csse_daily_reports idx_johns_hopkins_csse_daily_reports_day 0 0 day 0 BINARY 1 + ny_times_us_counties idx_ny_times_us_counties_date 0 0 date 1 BINARY 1 + ny_times_us_counties idx_ny_times_us_counties_fips 0 3 fips 0 BINARY 1 + ny_times_us_counties idx_ny_times_us_counties_county 0 1 county 0 BINARY 1 + ny_times_us_counties idx_ny_times_us_counties_state 0 2 state 0 BINARY 1 + +It shows indexes across all tables. To see indexes for specific tables, list those after the database:: + + $ sqlite-utils indexes covid.db johns_hopkins_csse_daily_reports --table + +The command defaults to only showing the columns that are explicitly part of the index. To also include auxiliary columns use the ``--aux`` option - these columns will be listed with a ``key`` of ``0``. + +The command takes the same format options as the ``tables`` and ``views`` commands. + .. _cli_triggers: Listing triggers diff --git a/sqlite_utils/cli.py b/sqlite_utils/cli.py index cbc0555..2e3d08e 100644 --- a/sqlite_utils/cli.py +++ b/sqlite_utils/cli.py @@ -1287,6 +1287,67 @@ def triggers( ) +@cli.command() +@click.argument( + "path", + type=click.Path(file_okay=True, dir_okay=False, allow_dash=False), + required=True, +) +@click.argument("tables", nargs=-1) +@click.option("--aux", is_flag=True, help="Include auxiliary columns") +@output_options +@load_extension_option +@click.pass_context +def indexes( + ctx, + path, + tables, + aux, + nl, + arrays, + csv, + tsv, + no_headers, + table, + fmt, + json_cols, + load_extension, +): + "Show indexes for this database" + sql = """ + select + sqlite_master.name as "table", + indexes.name as index_name, + xinfo.* + from sqlite_master + join pragma_index_list(sqlite_master.name) indexes + join pragma_index_xinfo(index_name) xinfo + where + sqlite_master.type = 'table' + """ + if tables: + quote = sqlite_utils.Database(memory=True).quote + sql += " and sqlite_master.name in ({})".format( + ", ".join(quote(table) for table in tables) + ) + if not aux: + sql += " and xinfo.key = 1" + 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", diff --git a/tests/test_cli.py b/tests/test_cli.py index 3807eca..56f1953 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1802,6 +1802,83 @@ def test_search(tmpdir, fts, extra_arg, expected): assert result.output.replace("\r", "") == expected +def test_indexes(tmpdir): + db_path = str(tmpdir / "test.db") + db = Database(db_path) + db.conn.executescript( + """ + create table Gosh (c1 text, c2 text, c3 text); + create index Gosh_idx on Gosh(c2, c3 desc); + """ + ) + result = CliRunner().invoke( + cli.cli, + ["indexes", str(db_path)], + catch_exceptions=False, + ) + assert result.exit_code == 0 + assert json.loads(result.output) == [ + { + "table": "Gosh", + "index_name": "Gosh_idx", + "seqno": 0, + "cid": 1, + "name": "c2", + "desc": 0, + "coll": "BINARY", + "key": 1, + }, + { + "table": "Gosh", + "index_name": "Gosh_idx", + "seqno": 1, + "cid": 2, + "name": "c3", + "desc": 1, + "coll": "BINARY", + "key": 1, + }, + ] + result2 = CliRunner().invoke( + cli.cli, + ["indexes", str(db_path), "--aux"], + catch_exceptions=False, + ) + assert result2.exit_code == 0 + assert json.loads(result2.output) == [ + { + "table": "Gosh", + "index_name": "Gosh_idx", + "seqno": 0, + "cid": 1, + "name": "c2", + "desc": 0, + "coll": "BINARY", + "key": 1, + }, + { + "table": "Gosh", + "index_name": "Gosh_idx", + "seqno": 1, + "cid": 2, + "name": "c3", + "desc": 1, + "coll": "BINARY", + "key": 1, + }, + { + "table": "Gosh", + "index_name": "Gosh_idx", + "seqno": 2, + "cid": -1, + "name": None, + "desc": 0, + "coll": "BINARY", + "key": 0, + }, + ] + + _TRIGGERS_EXPECTED = '[{"name": "blah", "table": "articles", "sql": "CREATE TRIGGER blah AFTER INSERT ON articles\\nBEGIN\\n UPDATE counter SET count = count + 1;\\nEND"}]\n'