From 8443d7f3ba8a6762a1894afcadb342820130323e Mon Sep 17 00:00:00 2001 From: Simon Willison Date: Sun, 5 Jul 2026 14:42:30 -0700 Subject: [PATCH] Resolve implicit primary key references in table.foreign_keys For foreign keys declared as "REFERENCES other_table" with no explicit columns, PRAGMA foreign_key_list returns None for the referenced column. The foreign_keys property now resolves these to the other table's primary key columns, so other_column=None unambiguously indicates a compound foreign key. Co-Authored-By: Claude Fable 5 --- sqlite_utils/db.py | 6 ++++++ tests/test_foreign_keys.py | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+) diff --git a/sqlite_utils/db.py b/sqlite_utils/db.py index 1910b84..dbb7445 100644 --- a/sqlite_utils/db.py +++ b/sqlite_utils/db.py @@ -2075,6 +2075,12 @@ class Table(Queryable): other_table = rows[0][1] columns = [row[2] for row in rows] other_columns = [row[3] for row in rows] + if all(c is None for c in other_columns): + # "REFERENCES other_table" with no columns - the pragma + # returns None, meaning the other table's primary key + other_table_pks = self.db.table(other_table).pks + if len(other_table_pks) == len(columns): + other_columns = other_table_pks is_compound = len(rows) > 1 fks.append( ForeignKey( diff --git a/tests/test_foreign_keys.py b/tests/test_foreign_keys.py index afe333f..dc4ea68 100644 --- a/tests/test_foreign_keys.py +++ b/tests/test_foreign_keys.py @@ -408,3 +408,40 @@ def test_transform_preserves_compound_foreign_key_on_delete(): assert fk.is_compound is True assert fk.on_delete == "CASCADE" assert "ON DELETE CASCADE" in db["courses"].schema + + +def test_implicit_primary_key_reference_is_resolved(): + # REFERENCES authors (no column) has "to" of None in the pragma - + # it should be resolved to the primary key of the other table + db = Database(memory=True) + db.executescript(""" + CREATE TABLE authors (author_id INTEGER PRIMARY KEY); + CREATE TABLE books ( + id INTEGER PRIMARY KEY, + author_id INTEGER REFERENCES authors + ); + """) + fk = db["books"].foreign_keys[0] + assert fk.is_compound is False + assert fk.other_column == "author_id" + assert fk.other_columns == ["author_id"] + + +def test_implicit_compound_primary_key_reference_is_resolved(): + db = Database(memory=True) + db.executescript(""" + CREATE TABLE departments ( + campus_name TEXT NOT NULL, + dept_code TEXT NOT NULL, + PRIMARY KEY (campus_name, dept_code) + ); + CREATE TABLE courses ( + course_code TEXT PRIMARY KEY, + campus_name TEXT NOT NULL, + dept_code TEXT NOT NULL, + FOREIGN KEY (campus_name, dept_code) REFERENCES departments + ); + """) + fk = db["courses"].foreign_keys[0] + assert fk.is_compound is True + assert fk.other_columns == ["campus_name", "dept_code"]