From 8a5d0d80c37008f30953110d58f474aac2728ee3 Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sun, 24 Feb 2019 11:11:21 -0800 Subject: [PATCH] sqlite-utils create-index command, closes #14 --- docs/cli.rst | 17 ++++++++++++++ sqlite_utils/cli.py | 24 ++++++++++++++++++++ tests/test_cli.py | 54 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+) diff --git a/docs/cli.rst b/docs/cli.rst index d1aabd0..95e22fd 100644 --- a/docs/cli.rst +++ b/docs/cli.rst @@ -221,6 +221,23 @@ After running the above ``dogs.json`` example, try running this:: This will replace the record for id=2 (Pancakes) with a new record with an updated age. +.. _cli_create_index: + +Creating indexes +================ + +You can add an index to an existing table using the ``create-index`` subcommand:: + + $ sqlite-utils create-index mydb.db mytable col1 [col2...] + +This can be used to create indexes against a single column or multiple columns. + +The name of the index will be automatically derived from the table and columns. To specify a different name, use ``--name=name_of_index``. + +Use the ``--unique`` option to create a unique index. + +Use ``--if-not-exists`` to avoid attempting to create the index if one with that name already exists. + .. _cli_fts: Configuring full-text search diff --git a/sqlite_utils/cli.py b/sqlite_utils/cli.py index 3393b53..cfc9676 100644 --- a/sqlite_utils/cli.py +++ b/sqlite_utils/cli.py @@ -134,6 +134,30 @@ def optimize(path, no_vacuum): db.vacuum() +@cli.command(name="create-index") +@click.argument( + "path", + type=click.Path(exists=True, file_okay=True, dir_okay=False, allow_dash=False), + required=True, +) +@click.argument("table") +@click.argument("column", nargs=-1, required=True) +@click.option("--name", help="Explicit name for the new index") +@click.option("--unique", help="Make this a unique index", default=False, is_flag=True) +@click.option( + "--if-not-exists", + help="Ignore if index already exists", + default=False, + is_flag=True, +) +def create_index(path, table, column, name, unique, if_not_exists): + "Add an index to the specified table covering the specified columns" + db = sqlite_utils.Database(path) + db[table].create_index( + column, index_name=name, unique=unique, if_not_exists=if_not_exists + ) + + @cli.command(name="enable-fts") @click.argument( "path", diff --git a/tests/test_cli.py b/tests/test_cli.py index d72c96d..ffc549f 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -1,4 +1,5 @@ from sqlite_utils import cli, Database +from sqlite_utils.db import Index from click.testing import CliRunner import json import os @@ -116,6 +117,59 @@ def test_output_table(db_path, fmt, expected): assert expected == result.output.strip() +def test_create_index(db_path): + db = Database(db_path) + assert [] == db["Gosh"].indexes + result = CliRunner().invoke(cli.cli, ["create-index", db_path, "Gosh", "c1"]) + assert 0 == result.exit_code + assert [ + Index( + seq=0, name="idx_Gosh_c1", unique=0, origin="c", partial=0, columns=["c1"] + ) + ] == db["Gosh"].indexes + # Try with a custom name + result = CliRunner().invoke( + cli.cli, ["create-index", db_path, "Gosh", "c2", "--name", "blah"] + ) + assert 0 == result.exit_code + assert [ + Index(seq=0, name="blah", unique=0, origin="c", partial=0, columns=["c2"]), + Index( + seq=1, name="idx_Gosh_c1", unique=0, origin="c", partial=0, columns=["c1"] + ), + ] == db["Gosh"].indexes + # Try a two-column unique index + create_index_unique_args = [ + "create-index", + db_path, + "Gosh2", + "c1", + "c2", + "--unique", + ] + result = CliRunner().invoke(cli.cli, create_index_unique_args) + assert 0 == result.exit_code + assert [ + Index( + seq=0, + name="idx_Gosh2_c1_c2", + unique=1, + origin="c", + partial=0, + columns=["c1", "c2"], + ) + ] == db["Gosh2"].indexes + # Trying to create the same index should fail + assert -1 == CliRunner().invoke(cli.cli, create_index_unique_args).exit_code + # ... unless we use --if-not-exists + assert ( + 0 + == CliRunner() + .invoke(cli.cli, create_index_unique_args + ["--if-not-exists"]) + .exit_code + ) + + def test_enable_fts(db_path): assert None == Database(db_path)["Gosh"].detect_fts() result = CliRunner().invoke(