Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions datasette/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -692,11 +692,21 @@ def get_all_foreign_keys(conn):

# Sort foreign keys for deterministic ordering
for table in table_to_foreign_keys:
# other_column is None when a foreign key targets a table's implicit
# primary key, so coerce to "" to keep the sort key totally ordered.
table_to_foreign_keys[table]["incoming"].sort(
key=lambda fk: (fk["other_table"], fk["column"], fk["other_column"])
key=lambda fk: (
fk["other_table"] or "",
fk["column"] or "",
fk["other_column"] or "",
)
)
table_to_foreign_keys[table]["outgoing"].sort(
key=lambda fk: (fk["other_table"], fk["column"], fk["other_column"])
key=lambda fk: (
fk["other_table"] or "",
fk["column"] or "",
fk["other_column"] or "",
)
)

return table_to_foreign_keys
Expand Down
25 changes: 25 additions & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -921,3 +921,28 @@ def test_deep_dict_update(dict1, dict2, expected):
assert result == expected
# Check that the original dict1 was modified
assert dict1 == expected


def test_get_all_foreign_keys_mixed_implicit_and_explicit_targets():
# Two foreign keys from the same table to the same parent, one targeting
# the implicit primary key (other_column is None) and one an explicit
# column. Sorting the collected keys must not crash comparing None to str.
conn = sqlite3.connect(":memory:")
conn.executescript("""
CREATE TABLE parent (id INTEGER PRIMARY KEY, alt TEXT UNIQUE);
CREATE TABLE child (
p_id1 INTEGER,
p_id2 INTEGER,
FOREIGN KEY(p_id1) REFERENCES parent,
FOREIGN KEY(p_id2) REFERENCES parent(alt)
);
""")
result = utils.get_all_foreign_keys(conn)
assert result["parent"]["incoming"] == [
{"other_table": "child", "column": None, "other_column": "p_id1"},
{"other_table": "child", "column": "alt", "other_column": "p_id2"},
]
assert result["child"]["outgoing"] == [
{"other_table": "parent", "column": "p_id1", "other_column": None},
{"other_table": "parent", "column": "p_id2", "other_column": "alt"},
]