drop-table and drop-view now refuse to drop the wrong object type

drop-table on a view name silently dropped the view, and drop-view
on a table name silently dropped the table, because both used
db[name].drop() which dispatches on the actual object type. They
now use db.table() / db.view() and exit with an explanatory error
pointing at the correct command. --ignore still exits cleanly but
no longer drops anything.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UnLnhsH25Nnv7LHhekUfPd
This commit is contained in:
Claude 2026-07-04 18:13:50 +00:00
commit 862944bc38
No known key found for this signature in database
2 changed files with 51 additions and 3 deletions

View file

@ -14,6 +14,7 @@ from sqlite_utils.db import (
DEFAULT,
DescIndex,
NoTable,
NoView,
quote_identifier,
)
from sqlite_utils.plugins import ensure_plugins_loaded, pm, get_plugins
@ -1725,7 +1726,13 @@ def drop_table(path, table, ignore, load_extension):
_register_db_for_cleanup(db)
_load_extensions(db, load_extension)
try:
db[table].drop(ignore=ignore)
db.table(table).drop(ignore=ignore)
except NoTable:
# A view exists with this name
if not ignore:
raise click.ClickException(
'"{}" is a view, not a table - use drop-view to drop it'.format(table)
)
except OperationalError:
raise click.ClickException('Table "{}" does not exist'.format(table))
@ -1797,8 +1804,14 @@ def drop_view(path, view, ignore, load_extension):
_register_db_for_cleanup(db)
_load_extensions(db, load_extension)
try:
db[view].drop(ignore=ignore)
except OperationalError:
db.view(view).drop(ignore=ignore)
except NoView:
if ignore:
return
if view in db.table_names():
raise click.ClickException(
'"{}" is a table, not a view - use drop-table to drop it'.format(view)
)
raise click.ClickException('View "{}" does not exist'.format(view))

View file

@ -1467,6 +1467,24 @@ def test_drop_table_error():
assert result.exit_code == 0
def test_drop_table_on_view_errors():
runner = CliRunner()
with runner.isolated_filesystem():
db = Database("test.db")
db["t"].insert({"id": 1})
db.create_view("v", "select * from t")
result = runner.invoke(cli.cli, ["drop-table", "test.db", "v"])
assert result.exit_code == 1
assert 'Error: "v" is a view, not a table - use drop-view to drop it' == (
result.output.strip()
)
assert "v" in db.view_names()
# --ignore exits cleanly but must still not drop the view
result = runner.invoke(cli.cli, ["drop-table", "test.db", "v", "--ignore"])
assert result.exit_code == 0
assert "v" in db.view_names()
def test_drop_view():
runner = CliRunner()
with runner.isolated_filesystem():
@ -1485,6 +1503,23 @@ def test_drop_view():
assert "hello" not in db.view_names()
def test_drop_view_on_table_errors():
runner = CliRunner()
with runner.isolated_filesystem():
db = Database("test.db")
db["t"].insert({"id": 1})
result = runner.invoke(cli.cli, ["drop-view", "test.db", "t"])
assert result.exit_code == 1
assert 'Error: "t" is a table, not a view - use drop-table to drop it' == (
result.output.strip()
)
assert "t" in db.table_names()
# --ignore exits cleanly but must still not drop the table
result = runner.invoke(cli.cli, ["drop-view", "test.db", "t", "--ignore"])
assert result.exit_code == 0
assert "t" in db.table_names()
def test_drop_view_error():
runner = CliRunner()
with runner.isolated_filesystem():