drop-index command and table.drop_index(index_name)

Closes #626
This commit is contained in:
Simon Willison 2026-07-07 21:00:43 -07:00
commit 7a52214624
8 changed files with 137 additions and 1 deletions

View file

@ -12,6 +12,7 @@ Unreleased
- ``sqlite-utils query`` can now read the SQL query from standard input by passing ``-`` in place of the query, for example ``echo "select * from dogs" | sqlite-utils query dogs.db -``. (:issue:`765`)
- ``sqlite-utils insert`` and ``sqlite-utils upsert`` now accept a ``--code`` option for :ref:`providing a block of Python code <cli_insert_code>` (or a path to a ``.py`` file) that defines a ``rows()`` function or ``rows`` iterable of rows to insert, as an alternative to importing from a file. (:issue:`684`)
- ``sqlite-utils insert`` and ``sqlite-utils upsert`` now accept ``--type column-name type`` to :ref:`override the type automatically chosen when the table is created <cli_insert_csv_tsv_column_types>`. This is useful for CSV or TSV columns such as ZIP codes that look like integers but should be stored as ``TEXT`` to preserve leading zeros. (:issue:`131`)
- New ``table.drop_index(name)`` method and ``sqlite-utils drop-index`` command for dropping an index by name. Both accept ``ignore=True``/``--ignore`` to ignore a missing index. (:issue:`626`)
.. _v4_0:

View file

@ -19,7 +19,7 @@ This page lists the ``--help`` for every ``sqlite-utils`` CLI sub-command.
go_first = [
"query", "memory", "insert", "upsert", "bulk", "search", "transform", "extract",
"schema", "insert-files", "analyze-tables", "convert", "tables", "views", "rows",
"triggers", "indexes", "create-database", "create-table", "create-index",
"triggers", "indexes", "create-database", "create-table", "create-index", "drop-index",
"migrate", "enable-fts", "populate-fts", "rebuild-fts", "disable-fts"
]
refs = {
@ -46,6 +46,7 @@ This page lists the ``--help`` for every ``sqlite-utils`` CLI sub-command.
"add-foreign-keys": "cli_add_foreign_keys",
"index-foreign-keys": "cli_index_foreign_keys",
"create-index": "cli_create_index",
"drop-index": "cli_drop_index",
"enable-wal": "cli_wal",
"enable-counts": "cli_enable_counts",
"bulk": "cli_bulk",
@ -1006,6 +1007,29 @@ See :ref:`cli_create_index`.
-h, --help Show this message and exit.
.. _cli_ref_drop_index:
drop-index
==========
See :ref:`cli_drop_index`.
::
Usage: sqlite-utils drop-index [OPTIONS] PATH TABLE INDEX
Drop an index by index name from the specified table
Example:
sqlite-utils drop-index chickens.db chickens idx_chickens_name
Options:
--ignore Ignore if index does not exist
--load-extension TEXT Path to SQLite extension, with optional :entrypoint
-h, --help Show this message and exit.
.. _cli_ref_migrate:
migrate

View file

@ -2615,6 +2615,19 @@ If your column names are already prefixed with a hyphen you'll need to manually
Add the ``--analyze`` option to run ``ANALYZE`` against the index after it has been created.
.. _cli_drop_index:
Dropping indexes
================
You can drop an index from an existing table using the ``drop-index`` command:
.. code-block:: bash
sqlite-utils drop-index mydb.db mytable idx_mytable_col1
Use ``--ignore`` to ignore the error if the index does not exist on that table.
.. _cli_fts:
Configuring full-text search

View file

@ -2877,6 +2877,14 @@ Use ``if_not_exists=True`` to do nothing if an index with that name already exis
Pass ``analyze=True`` to run ``ANALYZE`` against the new index after creating it.
You can drop an index from a table using ``.drop_index(index_name)``:
.. code-block:: python
db.table("dogs").drop_index("idx_dogs_name")
Use ``ignore=True`` to ignore the error if the index does not exist.
.. _python_api_analyze:
Optimizing index usage with ANALYZE

View file

@ -692,6 +692,34 @@ def create_index(
)
@cli.command(name="drop-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("index")
@click.option("--ignore", help="Ignore if index does not exist", is_flag=True)
@load_extension_option
def drop_index(path, table, index, ignore, load_extension):
"""
Drop an index by index name from the specified table
Example:
\b
sqlite-utils drop-index chickens.db chickens idx_chickens_name
"""
db = sqlite_utils.Database(path)
_register_db_for_cleanup(db)
_load_extensions(db, load_extension)
try:
db.table(table).drop_index(index, ignore=ignore)
except OperationalError as ex:
raise click.ClickException(str(ex))
@cli.command(name="enable-fts")
@click.argument(
"path",

View file

@ -3080,6 +3080,22 @@ class Table(Queryable):
self.db.analyze(created_index_name)
return self
def drop_index(self, index_name: str, ignore: bool = False):
"""
Drop an index on this table.
:param index_name: Name of the index to drop
:param ignore: Set to ``True`` to ignore the error if the index does not exist
"""
if index_name not in {index.name for index in self.indexes}:
if ignore:
return self
raise OperationalError(
"No index named {} on table {}".format(index_name, self.name)
)
self.db.execute("DROP INDEX {}".format(quote_identifier(index_name)))
return self
def add_column(
self,
col_name: str,

View file

@ -291,6 +291,24 @@ def test_create_index(db_path):
)
def test_drop_index(db_path):
db = Database(db_path)
db["Gosh"].create_index(["c1"])
assert [index.name for index in db["Gosh"].indexes] == ["idx_Gosh_c1"]
result = CliRunner().invoke(cli.cli, ["drop-index", db_path, "Gosh", "idx_Gosh_c1"])
assert result.exit_code == 0
assert db["Gosh"].indexes == []
result = CliRunner().invoke(cli.cli, ["drop-index", db_path, "Gosh", "idx_Gosh_c1"])
assert result.exit_code == 1
assert "No index named idx_Gosh_c1" in result.output
result = CliRunner().invoke(
cli.cli, ["drop-index", db_path, "Gosh", "idx_Gosh_c1", "--ignore"]
)
assert result.exit_code == 0
def test_create_index_analyze(db_path):
db = Database(db_path)
assert "sqlite_stat1" not in db.table_names()

View file

@ -809,6 +809,34 @@ def test_create_index_if_not_exists(fresh_db):
dogs.create_index(["name"], if_not_exists=True)
def test_drop_index(fresh_db):
dogs = fresh_db["dogs"]
dogs.insert({"name": "Cleo", "twitter": "cleopaws", "age": 3, "is_good_dog": True})
dogs.create_index(["name"])
assert [index.name for index in dogs.indexes] == ["idx_dogs_name"]
dogs.drop_index("idx_dogs_name")
assert dogs.indexes == []
def test_drop_index_ignore(fresh_db):
dogs = fresh_db["dogs"]
dogs.insert({"name": "Cleo"})
with pytest.raises(OperationalError, match="No index named idx_dogs_name"):
dogs.drop_index("idx_dogs_name")
dogs.drop_index("idx_dogs_name", ignore=True)
def test_drop_index_wrong_table(fresh_db):
dogs = fresh_db["dogs"]
cats = fresh_db["cats"]
dogs.insert({"name": "Cleo"})
cats.insert({"name": "Misty"})
dogs.create_index(["name"])
with pytest.raises(OperationalError, match="No index named idx_dogs_name"):
cats.drop_index("idx_dogs_name")
assert [index.name for index in dogs.indexes] == ["idx_dogs_name"]
def test_create_index_desc(fresh_db):
dogs = fresh_db["dogs"]
dogs.insert({"name": "Cleo", "twitter": "cleopaws", "age": 3, "is good dog": True})