Skip to content

Commit 5cba671

Browse files
committed
django/DatabaseIntrospection: implement get_primary_key_column, get_relations
Implemented: * DatabaseIntrospection.get_primary_key_column * DatabaseIntrospection.get_relations However, get_relations tests CANNOT yet be enabled because Cloud Spanner doesn't yet have ON DELETE cascading for FOREIGN KEYS which limits #313, which renders the method useless as it will always return {} instead of being populated with values that are derived from FOREIGN KEYs. Fixes #312 Updates #311
1 parent 622dc1d commit 5cba671

7 files changed

+76
-46
lines changed

django_test_suite.sh

+4-4
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,9 @@ SETTINGS_FILE="$TEST_DBNAME-settings"
2020

2121
checkout_django() {
2222
mkdir -p django_tests && cd django_tests
23-
git clone --depth 1 --single-branch --branch spanner-2.2.x https://github.com/timgraham/django.git
24-
cd django && pip3 install -e .
25-
pip3 install -r tests/requirements/py3.txt
23+
# git clone --depth 1 --single-branch --branch spanner-2.2.x https://github.com/timgraham/django.git
24+
cd django && pip3 install --user -e .
25+
# pip3 install -r tests/requirements/py3.txt
2626
}
2727

2828
create_settings() {
@@ -56,7 +56,7 @@ run_django_tests() {
5656
}
5757

5858
install_spanner_django() {
59-
pip3 install .
59+
pip3 install --user .
6060
}
6161

6262
install_spanner_django

spanner/dbapi/autocommit_off_connection.py

+9-4
Original file line numberDiff line numberDiff line change
@@ -170,17 +170,22 @@ def list_tables(self):
170170
# with a transaction otherwise we get back:
171171
# 400 Unsupported concurrency mode in query using INFORMATION_SCHEMA.
172172
# hence this specialized method.
173-
self.run_prior_DDL_statements()
174-
175-
with self.__dbhandle.snapshot() as snapshot:
176-
res = snapshot.execute_sql("""
173+
return self.run_sql_in_snapshot("""
177174
SELECT
178175
t.table_name
179176
FROM
180177
information_schema.tables AS t
181178
WHERE
182179
t.table_catalog = '' and t.table_schema = ''
183180
""")
181+
182+
def run_sql_in_snapshot(self, sql):
183+
# Some SQL e.g. for INFORMATION_SCHEMA cannot be run in read-write transactions
184+
# hence this method exists to circumvent that limit.
185+
self.run_prior_DDL_statements()
186+
187+
with self.__dbhandle.snapshot() as snapshot:
188+
res = snapshot.execute_sql(sql)
184189
return list(res)
185190

186191
def get_table_column_schema(self, table_name):

spanner/dbapi/autocommit_off_cursor.py

+3-10
Original file line numberDiff line numberDiff line change
@@ -357,18 +357,11 @@ def __run_prior_DDL_statements(self):
357357
return self.__connection.run_prior_DDL_statements()
358358

359359
def list_tables(self):
360-
# We CANNOT list tables with
361-
# SELECT
362-
# t.table_name
363-
# FROM
364-
# information_schema.tables AS t
365-
# WHERE
366-
# t.table_catalog = '' and t.table_schema = ''
367-
# with a transaction otherwise we get back:
368-
# 400 Unsupported concurrency mode in query using INFORMATION_SCHEMA.
369-
# hence this specialized method.
370360
return self.__connection.list_tables()
371361

362+
def run_sql_in_snapshot(self, sql):
363+
return self.__connection.run_sql_in_snapshot(sql)
364+
372365
def get_table_column_schema(self, table_name):
373366
return self.__connection.get_table_column_schema(table_name)
374367

spanner/dbapi/autocommit_on_connection.py

+12-15
Original file line numberDiff line numberDiff line change
@@ -89,27 +89,24 @@ def run_prior_DDL_statements(self):
8989
return self.__handle_update_ddl(ddl_statements)
9090

9191
def list_tables(self):
92-
# We CANNOT list tables with
93-
# SELECT
94-
# t.table_name
95-
# FROM
96-
# information_schema.tables AS t
97-
# WHERE
98-
# t.table_catalog = '' and t.table_schema = ''
99-
# with a transaction otherwise we get back:
100-
# 400 Unsupported concurrency mode in query using INFORMATION_SCHEMA.
101-
# hence this specialized method.
102-
self.run_prior_DDL_statements()
103-
104-
with self.__dbhandle.snapshot() as snapshot:
105-
res = snapshot.execute_sql("""
92+
return self.run_sql_in_snapshot(
93+
"""
10694
SELECT
10795
t.table_name
10896
FROM
10997
information_schema.tables AS t
11098
WHERE
11199
t.table_catalog = '' and t.table_schema = ''
112-
""")
100+
""",
101+
)
102+
103+
def run_sql_in_snapshot(self, sql):
104+
# Some SQL e.g. for INFORMATION_SCHEMA cannot be run in read-write transactions
105+
# hence this method exists to circumvent that limit.
106+
self.run_prior_DDL_statements()
107+
108+
with self.__dbhandle.snapshot() as snapshot:
109+
res = snapshot.execute_sql(sql)
113110
return list(res)
114111

115112
def get_table_column_schema(self, table_name):

spanner/dbapi/autocommit_on_cursor.py

+3-10
Original file line numberDiff line numberDiff line change
@@ -269,18 +269,11 @@ def __run_prior_DDL_statements(self):
269269
return self.__db_handle.run_prior_DDL_statements()
270270

271271
def list_tables(self):
272-
# We CANNOT list tables with
273-
# SELECT
274-
# t.table_name
275-
# FROM
276-
# information_schema.tables AS t
277-
# WHERE
278-
# t.table_catalog = '' and t.table_schema = ''
279-
# with a transaction otherwise we get back:
280-
# 400 Unsupported concurrency mode in query using INFORMATION_SCHEMA.
281-
# hence this specialized method.
282272
return self.__db_handle.list_tables()
283273

274+
def run_sql_in_snapshot(self, sql):
275+
return self.__db_handle.run_sql_in_snapshot(sql)
276+
284277
def get_table_column_schema(self, table_name):
285278
return self.__db_handle.get_table_column_schema(table_name)
286279

spanner/django/features.py

-3
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,6 @@ class DatabaseFeatures(BaseDatabaseFeatures):
265265
# DatabaseIntrospection.get_table_description() isn't fully implemented:
266266
# https://github.com/orijtech/django-spanner/issues/248
267267
'introspection.tests.IntrospectionTests.test_get_table_description_col_lengths',
268-
# DatabaseIntrospection.get_primary_key_column() isn't implemented:
269-
# https://github.com/orijtech/django-spanner/issues/312
270-
'introspection.tests.IntrospectionTests.test_get_primary_key_column',
271268
# DatabaseIntrospection.get_relations() isn't implemented:
272269
# https://github.com/orijtech/django-spanner/issues/311
273270
'introspection.tests.IntrospectionTests.test_get_relations',

spanner/django/introspection.py

+45
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,48 @@ def get_table_description(self, cursor, table_name):
5252
)
5353

5454
return descriptions
55+
56+
def get_relations(self, cursor, table_name):
57+
# TODO: PLEASE DO NOT USE THIS METHOD UNTIL
58+
# https://github.com/orijtech/django-spanner/issues/313
59+
# is resolved so that foreign keys can be supported, as documented in:
60+
# https://github.com/orijtech/django-spanner/issues/311
61+
"""
62+
Return a dictionary of {field_name: (field_name_other_table, other_table)}
63+
representing all relationships in the table.
64+
"""
65+
results = cursor.run_sql_in_snapshot(
66+
'''
67+
SELECT
68+
tc.COLUMN_NAME as col, ccu.COLUMN_NAME as ref_col, ccu.TABLE_NAME as ref_table
69+
FROM
70+
INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS tc
71+
JOIN
72+
INFORMATION_SCHEMA.REFERENTIAL_CONSTRAINTS as rc
73+
ON
74+
tc.CONSTRAINT_NAME = rc.CONSTRAINT_NAME
75+
JOIN
76+
INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE as ccu
77+
ON
78+
rc.UNIQUE_CONSTRAINT_NAME = ccu.CONSTRAINT_NAME
79+
WHERE
80+
tc.TABLE_NAME="%s"''' % self.connection.ops.quote_name(table_name),
81+
)
82+
return {column: (referred_column, referred_table) for (column, referred_column, referred_table) in results}
83+
84+
def get_primary_key_column(self, cursor, table_name):
85+
results = cursor.run_sql_in_snapshot(
86+
'''
87+
SELECT
88+
ccu.COLUMN_NAME
89+
FROM
90+
INFORMATION_SCHEMA.TABLE_CONSTRAINTS as tc
91+
RIGHT JOIN
92+
INFORMATION_SCHEMA.KEY_COLUMN_USAGE
93+
AS
94+
ccu ON tc.CONSTRAINT_NAME = ccu.CONSTRAINT_NAME
95+
WHERE
96+
tc.TABLE_NAME="%s" AND tc.CONSTRAINT_TYPE='PRIMARY KEY' AND tc.TABLE_SCHEMA=''
97+
''' % self.connection.ops.quote_name(table_name),
98+
)
99+
return results[0][0] if results else None

0 commit comments

Comments
 (0)