Skip to content

Commit b6f5fd5

Browse files
committed
test_internal_foreign_key_references() fix for sqlite-utils 4.0rc3
Refs simonw/sqlite-utils#769 (comment)
1 parent 9a0b78b commit b6f5fd5

1 file changed

Lines changed: 64 additions & 11 deletions

File tree

tests/test_internal_db.py

Lines changed: 64 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import pytest
2-
import sqlite_utils
2+
3+
from datasette.utils import escape_sqlite
34

45

56
# ensure refresh_schemas() gets called before interacting with internal_db
@@ -76,19 +77,71 @@ async def test_internal_foreign_key_references(ds_client):
7677
internal_db = await ensure_internal(ds_client)
7778

7879
def inner(conn):
79-
db = sqlite_utils.Database(conn)
80-
table_names = db.table_names()
81-
for table in db.tables:
82-
for fk in table.foreign_keys:
83-
other_table = fk.other_table
84-
other_column = fk.other_column
85-
message = 'Column "{}.{}" references other column "{}.{}" which does not exist'.format(
86-
table.name, fk.column, other_table, other_column
80+
table_names = [
81+
row[0]
82+
for row in conn.execute(
83+
"select name from sqlite_master where type = 'table'"
84+
).fetchall()
85+
]
86+
87+
def columns_for_table(table_name):
88+
return {
89+
row[1]
90+
for row in conn.execute(
91+
"PRAGMA table_info({})".format(escape_sqlite(table_name))
92+
).fetchall()
93+
}
94+
95+
def primary_keys_for_table(table_name):
96+
return [
97+
name
98+
for _, name in sorted(
99+
(row[5], row[1])
100+
for row in conn.execute(
101+
"PRAGMA table_info({})".format(escape_sqlite(table_name))
102+
).fetchall()
103+
if row[5]
104+
)
105+
]
106+
107+
columns_by_table = {
108+
table_name: columns_for_table(table_name) for table_name in table_names
109+
}
110+
111+
for table_name in table_names:
112+
foreign_key_rows = conn.execute(
113+
"PRAGMA foreign_key_list({})".format(escape_sqlite(table_name))
114+
).fetchall()
115+
foreign_keys_by_id = {}
116+
for foreign_key in foreign_key_rows:
117+
foreign_keys_by_id.setdefault(foreign_key[0], []).append(foreign_key)
118+
119+
for foreign_key_rows in foreign_keys_by_id.values():
120+
foreign_key_rows.sort(key=lambda row: row[1])
121+
other_table = foreign_key_rows[0][2]
122+
other_columns = [row[4] for row in foreign_key_rows]
123+
message = 'Column "{}.{}" references other table "{}" which does not exist'.format(
124+
table_name, foreign_key_rows[0][3], other_table
87125
)
88126
assert other_table in table_names, message + " (bad table)"
89-
assert other_column in db[other_table].columns_dict, (
90-
message + " (bad column)"
127+
if all(other_column is None for other_column in other_columns):
128+
other_columns = primary_keys_for_table(other_table)
129+
length_message = 'Foreign key from "{}" to "{}" has {} columns but references {} columns'.format(
130+
table_name,
131+
other_table,
132+
len(foreign_key_rows),
133+
len(other_columns),
91134
)
135+
assert len(other_columns) == len(foreign_key_rows), length_message
136+
137+
for foreign_key, other_column in zip(foreign_key_rows, other_columns):
138+
column = foreign_key[3]
139+
message = 'Column "{}.{}" references other column "{}.{}" which does not exist'.format(
140+
table_name, column, other_table, other_column
141+
)
142+
assert other_column in columns_by_table[other_table], (
143+
message + " (bad column)"
144+
)
92145

93146
await internal_db.execute_fn(inner)
94147

0 commit comments

Comments
 (0)