Skip to content

upsert() raises NOT NULL for an omitted column on an existing row (4.2.1) #878

Description

@glaziermag

On sqlite-utils 4.2.1 and current main, a partial upsert() of an existing row raises IntegrityError when an omitted column is NOT NULL without a default, even though the row already has a valid value for that column. The generated INSERT ... ON CONFLICT omits that column from the insert, so its constraint is checked before the update can run.

Reproduction:

uv run --with sqlite-utils==4.2.1 python - <<'PY'
from sqlite_utils import Database

db = Database(memory=True)
dogs = db.table("dogs")
dogs.insert({"id": 1, "name": "Cleo", "color": "brown"}, pk="id", not_null={"name"})
dogs.upsert({"id": 1, "color": "black"})
print(dogs.get(1))
PY

Expected: {'id': 1, 'name': 'Cleo', 'color': 'black'}.

Actual: sqlite3.IntegrityError: NOT NULL constraint failed: dogs.name. The row remains unchanged, including color='brown'.

The upsert documentation at 6bc1d33 says existing rows are updated and that “Any existing columns that are not referenced in the dictionary passed to .upsert() will be unchanged.”

The relevant SQL is built in Table.build_insert_queries_and_params(). Changing only the constructor to Database(memory=True, use_old_upsert=True) makes the same update succeed and preserves name='Cleo'.

Reproduced with Python 3.12.12 / SQLite 3.50.4, both on the published 4.2.1 package and main at 6bc1d33d583c54bd69fbdd2071117e2d38c354a1. The existing tests/test_upsert.py suite on that commit passes all 18 tests.

Prepared and reproduced by OpenAI Codex.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions