Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitad8edbc

Browse files
author
Jesse Whitehouse
committed
Refactor dialect's has_table method to extract _describe_table_extended
This passes the HasTableTest test group in the dialect compliancetest suiteSigned-off-by: Jesse Whitehouse <jesse.whitehouse@databricks.com>
1 parent48604ef commitad8edbc

File tree

1 file changed

+88
-27
lines changed

1 file changed

+88
-27
lines changed

‎src/databricks/sqlalchemy/__init__.py‎

Lines changed: 88 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
importsqlalchemy
55
fromsqlalchemyimportevent,DDL
6-
fromsqlalchemy.engineimportEngine,default,reflection
6+
fromsqlalchemy.engineimportEngine,default,reflection,Connection,Row,CursorResult
77
fromsqlalchemy.engine.interfacesimport (
88
ReflectedForeignKeyConstraint,
99
ReflectedPrimaryKeyConstraint,
@@ -31,9 +31,35 @@
3131
classDatabricksImpl(DefaultImpl):
3232
__dialect__="databricks"
3333

34+
3435
DBR_LTE_12_NOT_FOUND_STRING="Table or view not found"
3536
DBR_GT_12_NOT_FOUND_STRING="TABLE_OR_VIEW_NOT_FOUND"
3637

38+
39+
def_match_table_not_found_string(message:str)->bool:
40+
"""Return True if the message contains a substring indicating that a table was not found"""
41+
returnany(
42+
[
43+
DBR_LTE_12_NOT_FOUND_STRINGinmessage,
44+
DBR_GT_12_NOT_FOUND_STRINGinmessage,
45+
]
46+
)
47+
48+
49+
def_describe_table_extended_result_to_dict(result:CursorResult)->dict:
50+
"""Transform the output of DESCRIBE TABLE EXTENDED into a dictionary
51+
52+
The output from DESCRIBE TABLE EXTENDED puts all values in the `data_type` column
53+
Even CONSTRAINT descriptions are contained in the `data_type` column
54+
Some rows have an empty string for their col_name. These are present only for spacing
55+
so we ignore them.
56+
"""
57+
58+
result_dict= {row.col_name:row.data_typeforrowinresultifrow.col_name!=""}
59+
60+
returnresult_dict
61+
62+
3763
COLUMN_TYPE_MAP= {
3864
"boolean":sqlalchemy.types.Boolean,
3965
"smallint":sqlalchemy.types.SmallInteger,
@@ -54,6 +80,7 @@ class DatabricksImpl(DefaultImpl):
5480
"date":sqlalchemy.types.Date,
5581
}
5682

83+
5784
classDatabricksDialect(default.DefaultDialect):
5885
"""This dialect implements only those methods required to pass our e2e tests"""
5986

@@ -156,6 +183,46 @@ def get_columns(self, connection, table_name, schema=None, **kwargs):
156183

157184
returncolumns
158185

186+
def_describe_table_extended(
187+
self,
188+
connection:Connection,
189+
table_name:str,
190+
catalog_name:Optional[str]=None,
191+
schema_name:Optional[str]=None,
192+
expect_result=True,
193+
):
194+
"""Run DESCRIBE TABLE EXTENDED on a table and return a dictionary of the result.
195+
196+
This method is the fastest way to check for the presence of a table in a schema.
197+
198+
If expect_result is False, this method returns None as the output dict isn't required.
199+
200+
Raises NoSuchTableError if the table is not present in the schema.
201+
"""
202+
203+
_target_catalog=catalog_nameorself.catalog
204+
_target_schema=schema_nameorself.schema
205+
_target=f"`{_target_catalog}`.`{_target_schema}`.`{table_name}`"
206+
207+
# sql injection risk?
208+
# DESCRIBE TABLE EXTENDED in DBR doesn't support parameterised inputs :(
209+
stmt=DDL(f"DESCRIBE TABLE EXTENDED{_target}")
210+
211+
try:
212+
result=connection.execute(stmt).all()
213+
exceptDatabaseErrorase:
214+
if_match_table_not_found_string(str(e)):
215+
raisesqlalchemy.exc.NoSuchTableError(
216+
f"No such table{table_name}"
217+
)frome
218+
raisee
219+
220+
ifnotexpect_result:
221+
returnNone
222+
223+
fmt_result=_describe_table_extended_result_to_dict(result)
224+
returnfmt_result
225+
159226
@reflection.cache
160227
defget_pk_constraint(
161228
self,
@@ -169,16 +236,18 @@ def get_pk_constraint(
169236
"""
170237

171238
withself.get_connection_cursor(connection)ascursor:
172-
173239
try:
174240
# DESCRIBE TABLE EXTENDED doesn't support parameterised inputs :(
175-
result=cursor.execute(f"DESCRIBE TABLE EXTENDED{table_name}").fetchall()
241+
result=cursor.execute(
242+
f"DESCRIBE TABLE EXTENDED{table_name}"
243+
).fetchall()
176244
exceptServerOperationErrorase:
177245
ifDBR_GT_12_NOT_FOUND_STRINGinstr(
178246
e
179247
)orDBR_LTE_12_NOT_FOUND_STRINGinstr(e):
180-
raisesqlalchemy.exc.NoSuchTableError(f"No such table{table_name}")frome
181-
248+
raisesqlalchemy.exc.NoSuchTableError(
249+
f"No such table{table_name}"
250+
)frome
182251

183252
# DESCRIBE TABLE EXTENDED doesn't give a deterministic name to the field where
184253
# a primary key constraint will be found in its output. So we cycle through its
@@ -237,7 +306,9 @@ def get_foreign_keys(
237306
ifDBR_GT_12_NOT_FOUND_STRINGinstr(
238307
e
239308
)orDBR_LTE_12_NOT_FOUND_STRINGinstr(e):
240-
raisesqlalchemy.exc.NoSuchTableError(f"No such table{table_name}")frome
309+
raisesqlalchemy.exc.NoSuchTableError(
310+
f"No such table{table_name}"
311+
)frome
241312

242313
# DESCRIBE TABLE EXTENDED doesn't give a deterministic name to the field where
243314
# a foreign key constraint will be found in its output. So we cycle through its
@@ -333,29 +404,20 @@ def do_rollback(self, dbapi_connection):
333404
defhas_table(
334405
self,connection,table_name,schema=None,catalog=None,**kwargs
335406
)->bool:
336-
"""SQLAlchemy docstrings say dialect providers must implement this method"""
337-
338-
_schema=schemaorself.schema
339-
_catalog=catalogorself.catalog
340-
341-
# DBR >12.x uses underscores in error messages
342-
DBR_LTE_12_NOT_FOUND_STRING="Table or view not found"
343-
DBR_GT_12_NOT_FOUND_STRING="TABLE_OR_VIEW_NOT_FOUND"
407+
"""For internal dialect use, check the existence of a particular table
408+
or view in the database.
409+
"""
344410

345411
try:
346-
res=connection.execute(
347-
sqlalchemy.text(
348-
f"DESCRIBE TABLE `{_catalog}`.`{_schema}`.`{table_name}`"
349-
)
412+
self._describe_table_extended(
413+
connection=connection,
414+
table_name=table_name,
415+
catalog_name=catalog,
416+
schema_name=schema,
350417
)
351418
returnTrue
352-
exceptDatabaseErrorase:
353-
ifDBR_GT_12_NOT_FOUND_STRINGinstr(
354-
e
355-
)orDBR_LTE_12_NOT_FOUND_STRINGinstr(e):
356-
returnFalse
357-
else:
358-
raisee
419+
exceptsqlalchemy.exc.NoSuchTableErrorase:
420+
returnFalse
359421

360422
defget_connection_cursor(self,connection):
361423
"""Added for backwards compatibility with 1.3.x"""
@@ -372,8 +434,7 @@ def get_connection_cursor(self, connection):
372434

373435
@reflection.cache
374436
defget_schema_names(self,connection,**kw):
375-
"""Return a list of all schema names available in the database.
376-
"""
437+
"""Return a list of all schema names available in the database."""
377438
stmt=DDL("SHOW SCHEMAS")
378439
result=connection.execute(stmt)
379440
schema_list= [row[0]forrowinresult]

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp