Raise InvalidColumns on insert(pk="invalid")

Closes #732
This commit is contained in:
Simon Willison 2026-07-06 20:13:52 -07:00
commit 6225eba5c8
3 changed files with 35 additions and 0 deletions

View file

@ -10,6 +10,7 @@ Unreleased
----------
- JSON output from the command-line tool no longer escapes non-ASCII characters, so ``sqlite-utils data.db "select '日本語' as text"`` now outputs ``[{"text": "日本語"}]``. This matches how values were already stored by ``insert`` and how CSV/TSV output already behaved. A new ``--ascii`` option restores the previous behavior of escaping non-ASCII characters, for output destinations that cannot handle UTF-8 - see :ref:`cli_query_json_ascii`. The option is available on the ``query``, ``rows``, ``search``, ``tables``, ``views``, ``triggers``, ``indexes`` and ``memory`` commands. The ``convert --multi --dry-run`` preview and ``plugins`` output also no longer escape non-ASCII characters. (:issue:`625`)
- ``table.insert_all(..., pk=...)`` now raises ``InvalidColumns`` if ``pk=`` names columns that do not exist in an existing table. Previously this behaved inconsistently, with single-row inserts raising a ``KeyError`` while other row counts succeeded. (:issue:`732`)
.. _v4_0rc3:

View file

@ -4229,6 +4229,24 @@ class Table(Queryable):
if hash_id:
pk = hash_id
if pk and not hash_id and self.exists():
pk_cols = [pk] if isinstance(pk, str) else list(pk)
existing_columns = self.columns_dict
missing_pk_cols = [
col
for col in pk_cols
if resolve_casing(col, existing_columns) not in existing_columns
]
if missing_pk_cols:
raise InvalidColumns(
"Invalid primary key column{} {} for table {} with columns {}".format(
"s" if len(missing_pk_cols) > 1 else "",
missing_pk_cols,
self.name,
list(existing_columns),
)
)
if ignore and replace:
raise ValueError("Use either ignore=True or replace=True, not both")
all_columns = []

View file

@ -3,6 +3,7 @@ from sqlite_utils.db import (
Database,
DescIndex,
AlterError,
InvalidColumns,
NoObviousTable,
OperationalError,
ForeignKey,
@ -925,6 +926,21 @@ def test_insert_thousands_adds_extra_columns_after_first_100_with_alter(fresh_db
assert rows == [{"i": 101, "word": None, "extra": "Should trigger ALTER"}]
@pytest.mark.parametrize("num_rows", (0, 1, 2, 3, 10))
def test_insert_all_pk_not_in_records_raises(fresh_db, num_rows):
# https://github.com/simonw/sqlite-utils/issues/732
fresh_db.conn.execute("CREATE TABLE t (a TEXT, b INT, PRIMARY KEY (a, b))")
rows = [{"a": "x{}".format(i), "b": i} for i in range(num_rows)]
with pytest.raises(InvalidColumns) as ex:
fresh_db["t"].insert_all(rows, pk="not_a_column", alter=True)
assert ex.value.args == (
"Invalid primary key column ['not_a_column'] for table t with columns ['a', 'b']",
)
assert fresh_db["t"].count == 0
def test_insert_ignore(fresh_db):
fresh_db["test"].insert({"id": 1, "bar": 2}, pk="id")
# Should raise an error if we try this again