15
15
from pandas import DataFrame
16
16
from sqlalchemy import inspect
17
17
from sqlalchemy .engine .reflection import Inspector
18
+ from sqlalchemy .exc import NoSuchTableError
18
19
19
20
from noteable_magics .sql .connection import Connection
20
21
from noteable_magics .sql .gate_messaging_types import (
@@ -399,21 +400,17 @@ def run(self, invoked_as: str, args: List[str]) -> Tuple[DataFrame, bool]:
399
400
400
401
inspector = self .get_inspector ()
401
402
402
- is_view = relation_name in inspector .get_view_names (schema )
403
-
404
- if not is_view :
405
- # Ensure is a table
406
- if relation_name not in inspector .get_table_names (schema ):
407
- if schema :
408
- msg = f'Relation { schema } .{ relation_name } does not exist'
409
- else :
410
- msg = f'Relation { relation_name } does not exist'
411
- raise MetaCommandException (msg )
412
- rtype = 'Table'
413
- else :
414
- rtype = 'View'
403
+ try :
404
+ # In some dialects (BigQuery), this will raise NoSuchTableError if
405
+ # the table doesn't exist. Yay, sane.
415
406
416
- column_dicts = inspector .get_columns (relation_name , schema = schema )
407
+ # On some dialects (sigh, CockroachDB, what are you doing??),
408
+ # this call may succeed returning empty list even if
409
+ # the named relation does not exist. But the call to get_pk_constraint()
410
+ # down below will then raise NoSuchTableError.
411
+ column_dicts = inspector .get_columns (relation_name , schema = schema )
412
+ except NoSuchTableError :
413
+ self ._raise_from_no_such_table (schema , relation_name )
417
414
418
415
# 'Pivot' the dicts from get_columns()
419
416
names = []
@@ -454,11 +451,19 @@ def run(self, invoked_as: str, args: List[str]) -> Tuple[DataFrame, bool]:
454
451
455
452
displayable_rname = displayable_relation_name (schema , relation_name )
456
453
454
+ if is_view := relation_name in inspector .get_view_names (schema ):
455
+ rtype = 'View'
456
+ else :
457
+ rtype = 'Table'
458
+
457
459
main_relation_df = set_dataframe_metadata (
458
460
DataFrame (data = data ), title = f'{ rtype } "{ displayable_rname } " Structure'
459
461
)
460
462
461
- display (main_relation_df )
463
+ # Keep a list of things to call display() on. Only do so at the very
464
+ # end if we don't hit any exceptions.
465
+
466
+ displayables = [main_relation_df ]
462
467
463
468
if is_view :
464
469
view_definition = inspector .get_view_definition (relation_name , schema )
@@ -472,7 +477,7 @@ def run(self, invoked_as: str, args: List[str]) -> Tuple[DataFrame, bool]:
472
477
html_buf .append ('<br />' )
473
478
html_buf .append (f'<pre>{ view_definition } </pre>' )
474
479
475
- display (HTML ('\n ' .join (html_buf )))
480
+ displayables . append (HTML ('\n ' .join (html_buf )))
476
481
else :
477
482
# Is a table. Let's go get indices, foreign keys, other table constraints.
478
483
# If meaningful dataframe returned for any of these, transform to
@@ -484,7 +489,11 @@ def run(self, invoked_as: str, args: List[str]) -> Tuple[DataFrame, bool]:
484
489
):
485
490
secondary_df = secondary_function (inspector , relation_name , schema )
486
491
if len (secondary_df ):
487
- display (secondary_dataframe_to_html (secondary_df ))
492
+ displayables .append (secondary_dataframe_to_html (secondary_df ))
493
+
494
+ # Make it this far? Display what we should display.
495
+ for displayable in displayables :
496
+ display (displayable )
488
497
489
498
return main_relation_df , False
490
499
@@ -598,7 +607,7 @@ def all_table_and_views(self, inspector) -> List[Tuple[str, str, str]]:
598
607
default_schema = inspector .default_schema_name
599
608
all_schemas = set (inspector .get_schema_names ())
600
609
all_schemas .difference_update (self .AVOID_SCHEMAS )
601
- if default_schema not in all_schemas :
610
+ if default_schema and default_schema not in all_schemas :
602
611
all_schemas .add (default_schema )
603
612
604
613
for schema_name in sorted (all_schemas ):
@@ -971,7 +980,10 @@ def index_dataframe(
971
980
uniques : List [bool ] = []
972
981
973
982
# Primary key index is ... treated special by SQLA for some reason. Sigh.
974
- primary_index_dict = inspector .get_pk_constraint (table_name , schema )
983
+ try :
984
+ primary_index_dict = inspector .get_pk_constraint (table_name , schema )
985
+ except NoSuchTableError :
986
+ _raise_from_no_such_table (schema , table_name )
975
987
976
988
# If it returned something truthy with nonempty constrained_columns, then
977
989
# we assume it described a real primary key constraint here.
@@ -1164,7 +1176,8 @@ def __init__(self, underlying_inspector: Inspector):
1164
1176
1165
1177
# Direct passthrough attributes / methods
1166
1178
@property
1167
- def default_schema_name (self ) -> str :
1179
+ def default_schema_name (self ) -> Optional [str ]:
1180
+ # BigQuery, Trino dialects may end up returning None.
1168
1181
return self .underlying_inspector .default_schema_name
1169
1182
1170
1183
def get_schema_names (self ) -> List [str ]:
@@ -1183,16 +1196,19 @@ def get_foreign_keys(self, table_name: str, schema: Optional[str] = None) -> Lis
1183
1196
return self .underlying_inspector .get_foreign_keys (table_name , schema = schema )
1184
1197
1185
1198
def get_check_constraints (self , table_name : str , schema : Optional [str ] = None ) -> List [dict ]:
1186
- return self .underlying_inspector .get_check_constraints (table_name , schema = schema )
1199
+ try :
1200
+ return self .underlying_inspector .get_check_constraints (table_name , schema = schema )
1201
+ except NotImplementedError :
1202
+ return []
1187
1203
1188
1204
def get_indexes (self , table_name : str , schema : Optional [str ] = None ) -> List [dict ]:
1189
1205
return self .underlying_inspector .get_indexes (table_name , schema = schema )
1190
1206
1191
1207
def get_unique_constraints (self , table_name : str , schema : Optional [str ] = None ) -> List [dict ]:
1192
- return self . underlying_inspector . get_unique_constraints ( table_name , schema = schema )
1193
-
1194
- def get_check_constraints ( self , table_name : str , schema : Optional [ str ] = None ) -> List [ dict ] :
1195
- return self . underlying_inspector . get_check_constraints ( table_name , schema = schema )
1208
+ try :
1209
+ return self . underlying_inspector . get_unique_constraints ( table_name , schema = schema )
1210
+ except NotImplementedError :
1211
+ return []
1196
1212
1197
1213
# Now the value-adding filtering methods.
1198
1214
def get_table_names (self , schema : Optional [str ] = None ) -> List [str ]:
@@ -1211,3 +1227,12 @@ def _strip_schema(self, names: List[str], schema: Optional[str] = None) -> List[
1211
1227
# Remove "schema." from the start of each name if starts with.
1212
1228
# (name[False:] is equiv to name[0:], 'cause python bools are subclasses of ints)
1213
1229
return [name [name .startswith (prefix ) and len (prefix ) :] for name in names ]
1230
+
1231
+
1232
+ def _raise_from_no_such_table (schema : str , relation_name : str ):
1233
+ """Raise a MetaCommandException when eaten a NoSuchTableException"""
1234
+ if schema :
1235
+ msg = f'Relation { schema } .{ relation_name } does not exist'
1236
+ else :
1237
+ msg = f'Relation { relation_name } does not exist'
1238
+ raise MetaCommandException (msg )
0 commit comments