Skip to content

Commit 0de99d2

Browse files
committed
add null_ok to DatabaseIntrospection.get_table_description()
Introspect INFORMATION_SCHEMA.COLUMNS for COLUMN attributes. INFORMATION_SCHEMA gives us information like: * column_name * is_nullable aka null_ok * spanner_type which can use to fill in the `null_ok` field for spanner.django.introspection.DatabaseIntrospection.get_table_description Updates #248 Updates #308
1 parent 5069c20 commit 0de99d2

6 files changed

+64
-13
lines changed

spanner/dbapi/autocommit_off_connection.py

+4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from .autocommit_off_cursor import Cursor
88
from .exceptions import Error
99
from .periodic_auto_refresh import PeriodicAutoRefreshingTransaction
10+
from .utils import get_table_column_schema as get_table_column_schema_impl
1011

1112

1213
class Connection(object):
@@ -181,3 +182,6 @@ def list_tables(self):
181182
t.table_catalog = '' and t.table_schema = ''
182183
""")
183184
return list(res)
185+
186+
def get_table_column_schema(self, table_name):
187+
return get_table_column_schema_impl(self.__connection, table_name)

spanner/dbapi/autocommit_off_cursor.py

+3
Original file line numberDiff line numberDiff line change
@@ -369,6 +369,9 @@ def list_tables(self):
369369
# hence this specialized method.
370370
return self.__connection.list_tables()
371371

372+
def get_table_column_schema(self, table_name):
373+
return self.__connection.get_table_column_schema(table_name)
374+
372375

373376
class Column:
374377
def __init__(self, name, type_code, display_size=None, internal_size=None,

spanner/dbapi/autocommit_on_connection.py

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
from .autocommit_on_cursor import Cursor
88
from .exceptions import Error
9+
from .utils import get_table_column_schema as get_table_column_schema_impl
910

1011

1112
class Connection(object):
@@ -110,3 +111,6 @@ def list_tables(self):
110111
t.table_catalog = '' and t.table_schema = ''
111112
""")
112113
return list(res)
114+
115+
def get_table_column_schema(self, table_name):
116+
return get_table_column_schema_impl(self.__dbhandle, table_name)

spanner/dbapi/autocommit_on_cursor.py

+3
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,9 @@ def list_tables(self):
281281
# hence this specialized method.
282282
return self.__db_handle.list_tables()
283283

284+
def get_table_column_schema(self, table_name):
285+
return self.__db_handle.get_table_column_schema(table_name)
286+
284287

285288
class Column:
286289
def __init__(self, name, type_code, display_size=None, internal_size=None,

spanner/dbapi/utils.py

+31
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@
44
# license that can be found in the LICENSE file or at
55
# https://developers.google.com/open-source/licenses/bsd
66

7+
from collections import namedtuple
8+
9+
from google.cloud.spanner_v1 import param_types
10+
11+
ColumnDetails = namedtuple('column_details', ['null_ok', 'spanner_type'])
12+
713

814
class PeekIterator(object):
915
"""
@@ -40,3 +46,28 @@ def __next__(self):
4046

4147
def __iter__(self):
4248
return self
49+
50+
51+
def get_table_column_schema(spanner_db, table_name):
52+
with spanner_db.snapshot() as snapshot:
53+
rows = snapshot.execute_sql(
54+
'''SELECT
55+
COLUMN_NAME, IS_NULLABLE, SPANNER_TYPE
56+
FROM
57+
INFORMATION_SCHEMA.COLUMNS
58+
WHERE
59+
TABLE_SCHEMA = ''
60+
AND
61+
TABLE_NAME = @table_name''',
62+
params={'table_name': table_name},
63+
param_types={'table_name': param_types.STRING},
64+
)
65+
66+
column_details = {}
67+
for column_name, is_nullable, spanner_type in rows:
68+
column_details[column_name] = ColumnDetails(
69+
null_ok=is_nullable == 'YES',
70+
spanner_type=spanner_type,
71+
)
72+
73+
return column_details

spanner/django/introspection.py

+19-13
Original file line numberDiff line numberDiff line change
@@ -32,17 +32,23 @@ def get_table_description(self, cursor, table_name):
3232
interface.
3333
"""
3434
cursor.execute("SELECT * FROM %s LIMIT 1" % self.connection.ops.quote_name(table_name))
35-
return [
36-
FieldInfo(
37-
line.name,
38-
line.type_code,
39-
# TODO: Fill these in as they're implemented.
40-
None, # display_size
41-
None, # internal_size
42-
None, # precision
43-
None, # scale
44-
None, # null_ok
45-
None, # default
35+
column_details = cursor.get_table_column_schema(table_name)
36+
descriptions = []
37+
for line in cursor.description:
38+
column_name, type_code = line[0], line[1]
39+
details = column_details[column_name]
40+
descriptions.append(
41+
FieldInfo(
42+
column_name,
43+
type_code,
44+
# TODO: Fill these in as they're implemented.
45+
None, # display_size
46+
None, # internal_size
47+
None, # precision
48+
None, # scale
49+
details.null_ok,
50+
None, # default
51+
)
4652
)
47-
for line in cursor.description
48-
]
53+
54+
return descriptions

0 commit comments

Comments
 (0)