databricks-sqlalchemy 2.0.3__py3-none-any.whl → 2.0.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +0,0 @@
1
- """
2
- This module contains tests entirely maintained by Databricks.
3
-
4
- These tests do not rely on SQLAlchemy's custom test runner.
5
- """
@@ -1,44 +0,0 @@
1
- import os
2
- import pytest
3
-
4
-
5
- @pytest.fixture(scope="session")
6
- def host():
7
- return os.getenv("DATABRICKS_SERVER_HOSTNAME")
8
-
9
-
10
- @pytest.fixture(scope="session")
11
- def http_path():
12
- return os.getenv("DATABRICKS_HTTP_PATH")
13
-
14
-
15
- @pytest.fixture(scope="session")
16
- def access_token():
17
- return os.getenv("DATABRICKS_TOKEN")
18
-
19
-
20
- @pytest.fixture(scope="session")
21
- def ingestion_user():
22
- return os.getenv("DATABRICKS_USER")
23
-
24
-
25
- @pytest.fixture(scope="session")
26
- def catalog():
27
- return os.getenv("DATABRICKS_CATALOG")
28
-
29
-
30
- @pytest.fixture(scope="session")
31
- def schema():
32
- return os.getenv("DATABRICKS_SCHEMA", "default")
33
-
34
-
35
- @pytest.fixture(scope="session", autouse=True)
36
- def connection_details(host, http_path, access_token, ingestion_user, catalog, schema):
37
- return {
38
- "host": host,
39
- "http_path": http_path,
40
- "access_token": access_token,
41
- "ingestion_user": ingestion_user,
42
- "catalog": catalog,
43
- "schema": schema,
44
- }
@@ -1,543 +0,0 @@
1
- import datetime
2
- import decimal
3
- from typing import Tuple, Union, List
4
- from unittest import skipIf
5
-
6
- import pytest
7
- from sqlalchemy import (
8
- Column,
9
- MetaData,
10
- Table,
11
- Text,
12
- create_engine,
13
- insert,
14
- select,
15
- text,
16
- )
17
- from sqlalchemy.engine import Engine
18
- from sqlalchemy.engine.reflection import Inspector
19
- from sqlalchemy.orm import DeclarativeBase, Mapped, Session, mapped_column
20
- from sqlalchemy.schema import DropColumnComment, SetColumnComment
21
- from sqlalchemy.types import BOOLEAN, DECIMAL, Date, Integer, String
22
-
23
- try:
24
- from sqlalchemy.orm import declarative_base
25
- except ImportError:
26
- from sqlalchemy.ext.declarative import declarative_base
27
-
28
-
29
- USER_AGENT_TOKEN = "PySQL e2e Tests"
30
-
31
-
32
- def sqlalchemy_1_3():
33
- import sqlalchemy
34
-
35
- return sqlalchemy.__version__.startswith("1.3")
36
-
37
-
38
- def version_agnostic_select(object_to_select, *args, **kwargs):
39
- """
40
- SQLAlchemy==1.3.x requires arguments to select() to be a Python list
41
-
42
- https://docs.sqlalchemy.org/en/20/changelog/migration_14.html#orm-query-is-internally-unified-with-select-update-delete-2-0-style-execution-available
43
- """
44
-
45
- if sqlalchemy_1_3():
46
- return select([object_to_select], *args, **kwargs)
47
- else:
48
- return select(object_to_select, *args, **kwargs)
49
-
50
-
51
- def version_agnostic_connect_arguments(connection_details) -> Tuple[str, dict]:
52
- HOST = connection_details["host"]
53
- HTTP_PATH = connection_details["http_path"]
54
- ACCESS_TOKEN = connection_details["access_token"]
55
- CATALOG = connection_details["catalog"]
56
- SCHEMA = connection_details["schema"]
57
-
58
- ua_connect_args = {"_user_agent_entry": USER_AGENT_TOKEN}
59
-
60
- if sqlalchemy_1_3():
61
- conn_string = f"databricks://token:{ACCESS_TOKEN}@{HOST}"
62
- connect_args = {
63
- **ua_connect_args,
64
- "http_path": HTTP_PATH,
65
- "server_hostname": HOST,
66
- "catalog": CATALOG,
67
- "schema": SCHEMA,
68
- }
69
-
70
- return conn_string, connect_args
71
- else:
72
- return (
73
- f"databricks://token:{ACCESS_TOKEN}@{HOST}?http_path={HTTP_PATH}&catalog={CATALOG}&schema={SCHEMA}",
74
- ua_connect_args,
75
- )
76
-
77
-
78
- @pytest.fixture
79
- def db_engine(connection_details) -> Engine:
80
- conn_string, connect_args = version_agnostic_connect_arguments(connection_details)
81
- return create_engine(conn_string, connect_args=connect_args)
82
-
83
-
84
- def run_query(db_engine: Engine, query: Union[str, Text]):
85
- if not isinstance(query, Text):
86
- _query = text(query) # type: ignore
87
- else:
88
- _query = query # type: ignore
89
- with db_engine.begin() as conn:
90
- return conn.execute(_query).fetchall()
91
-
92
-
93
- @pytest.fixture
94
- def samples_engine(connection_details) -> Engine:
95
- details = connection_details.copy()
96
- details["catalog"] = "samples"
97
- details["schema"] = "nyctaxi"
98
- conn_string, connect_args = version_agnostic_connect_arguments(details)
99
- return create_engine(conn_string, connect_args=connect_args)
100
-
101
-
102
- @pytest.fixture()
103
- def base(db_engine):
104
- return declarative_base()
105
-
106
-
107
- @pytest.fixture()
108
- def session(db_engine):
109
- return Session(db_engine)
110
-
111
-
112
- @pytest.fixture()
113
- def metadata_obj(db_engine):
114
- return MetaData()
115
-
116
-
117
- def test_can_connect(db_engine):
118
- simple_query = "SELECT 1"
119
- result = run_query(db_engine, simple_query)
120
- assert len(result) == 1
121
-
122
-
123
- def test_connect_args(db_engine):
124
- """Verify that extra connect args passed to sqlalchemy.create_engine are passed to DBAPI
125
-
126
- This will most commonly happen when partners supply a user agent entry
127
- """
128
-
129
- conn = db_engine.connect()
130
- connection_headers = conn.connection.thrift_backend._transport._headers
131
- user_agent = connection_headers["User-Agent"]
132
-
133
- expected = f"(sqlalchemy + {USER_AGENT_TOKEN})"
134
- assert expected in user_agent
135
-
136
-
137
- @pytest.mark.skipif(sqlalchemy_1_3(), reason="Pandas requires SQLAlchemy >= 1.4")
138
- @pytest.mark.skip(
139
- reason="DBR is currently limited to 256 parameters per call to .execute(). Test cannot pass."
140
- )
141
- def test_pandas_upload(db_engine, metadata_obj):
142
- import pandas as pd
143
-
144
- SCHEMA = "default"
145
- try:
146
- df = pd.read_excel(
147
- "src/databricks/sqlalchemy/test_local/e2e/demo_data/MOCK_DATA.xlsx"
148
- )
149
- df.to_sql(
150
- "mock_data",
151
- db_engine,
152
- schema=SCHEMA,
153
- index=False,
154
- method="multi",
155
- if_exists="replace",
156
- )
157
-
158
- df_after = pd.read_sql_table("mock_data", db_engine, schema=SCHEMA)
159
- assert len(df) == len(df_after)
160
- except Exception as e:
161
- raise e
162
- finally:
163
- db_engine.execute("DROP TABLE mock_data")
164
-
165
-
166
- def test_create_table_not_null(db_engine, metadata_obj: MetaData):
167
- table_name = "PySQLTest_{}".format(datetime.datetime.utcnow().strftime("%s"))
168
-
169
- SampleTable = Table(
170
- table_name,
171
- metadata_obj,
172
- Column("name", String(255)),
173
- Column("episodes", Integer),
174
- Column("some_bool", BOOLEAN, nullable=False),
175
- )
176
-
177
- metadata_obj.create_all(db_engine)
178
-
179
- columns = db_engine.dialect.get_columns(
180
- connection=db_engine.connect(), table_name=table_name
181
- )
182
-
183
- name_column_description = columns[0]
184
- some_bool_column_description = columns[2]
185
-
186
- assert name_column_description.get("nullable") is True
187
- assert some_bool_column_description.get("nullable") is False
188
-
189
- metadata_obj.drop_all(db_engine)
190
-
191
-
192
- def test_column_comment(db_engine, metadata_obj: MetaData):
193
- table_name = "PySQLTest_{}".format(datetime.datetime.utcnow().strftime("%s"))
194
-
195
- column = Column("name", String(255), comment="some comment")
196
- SampleTable = Table(table_name, metadata_obj, column)
197
-
198
- metadata_obj.create_all(db_engine)
199
- connection = db_engine.connect()
200
-
201
- columns = db_engine.dialect.get_columns(
202
- connection=connection, table_name=table_name
203
- )
204
-
205
- assert columns[0].get("comment") == "some comment"
206
-
207
- column.comment = "other comment"
208
- connection.execute(SetColumnComment(column))
209
-
210
- columns = db_engine.dialect.get_columns(
211
- connection=connection, table_name=table_name
212
- )
213
-
214
- assert columns[0].get("comment") == "other comment"
215
-
216
- connection.execute(DropColumnComment(column))
217
-
218
- columns = db_engine.dialect.get_columns(
219
- connection=connection, table_name=table_name
220
- )
221
-
222
- assert columns[0].get("comment") == None
223
-
224
- metadata_obj.drop_all(db_engine)
225
-
226
-
227
- def test_bulk_insert_with_core(db_engine, metadata_obj, session):
228
- import random
229
-
230
- # Maximum number of parameter is 256. 256/4 == 64
231
- num_to_insert = 64
232
-
233
- table_name = "PySQLTest_{}".format(datetime.datetime.utcnow().strftime("%s"))
234
-
235
- names = ["Bim", "Miki", "Sarah", "Ira"]
236
-
237
- SampleTable = Table(
238
- table_name, metadata_obj, Column("name", String(255)), Column("number", Integer)
239
- )
240
-
241
- rows = [
242
- {"name": names[i % 3], "number": random.choice(range(64))}
243
- for i in range(num_to_insert)
244
- ]
245
-
246
- metadata_obj.create_all(db_engine)
247
- with db_engine.begin() as conn:
248
- conn.execute(insert(SampleTable).values(rows))
249
-
250
- with db_engine.begin() as conn:
251
- rows = conn.execute(version_agnostic_select(SampleTable)).fetchall()
252
-
253
- assert len(rows) == num_to_insert
254
-
255
-
256
- def test_create_insert_drop_table_core(base, db_engine, metadata_obj: MetaData):
257
- """ """
258
-
259
- SampleTable = Table(
260
- "PySQLTest_{}".format(datetime.datetime.utcnow().strftime("%s")),
261
- metadata_obj,
262
- Column("name", String(255)),
263
- Column("episodes", Integer),
264
- Column("some_bool", BOOLEAN),
265
- Column("dollars", DECIMAL(10, 2)),
266
- )
267
-
268
- metadata_obj.create_all(db_engine)
269
-
270
- insert_stmt = insert(SampleTable).values(
271
- name="Bim Adewunmi", episodes=6, some_bool=True, dollars=decimal.Decimal(125)
272
- )
273
-
274
- with db_engine.connect() as conn:
275
- conn.execute(insert_stmt)
276
-
277
- select_stmt = version_agnostic_select(SampleTable)
278
- with db_engine.begin() as conn:
279
- resp = conn.execute(select_stmt)
280
-
281
- result = resp.fetchall()
282
-
283
- assert len(result) == 1
284
-
285
- metadata_obj.drop_all(db_engine)
286
-
287
-
288
- # ORM tests are made following this tutorial
289
- # https://docs.sqlalchemy.org/en/14/orm/quickstart.html
290
-
291
-
292
- @skipIf(False, "Unity catalog must be supported")
293
- def test_create_insert_drop_table_orm(db_engine):
294
- """ORM classes built on the declarative base class must have a primary key.
295
- This is restricted to Unity Catalog.
296
- """
297
-
298
- class Base(DeclarativeBase):
299
- pass
300
-
301
- class SampleObject(Base):
302
- __tablename__ = "PySQLTest_{}".format(datetime.datetime.utcnow().strftime("%s"))
303
-
304
- name: Mapped[str] = mapped_column(String(255), primary_key=True)
305
- episodes: Mapped[int] = mapped_column(Integer)
306
- some_bool: Mapped[bool] = mapped_column(BOOLEAN)
307
-
308
- Base.metadata.create_all(db_engine)
309
-
310
- sample_object_1 = SampleObject(name="Bim Adewunmi", episodes=6, some_bool=True)
311
- sample_object_2 = SampleObject(name="Miki Meek", episodes=12, some_bool=False)
312
-
313
- session = Session(db_engine)
314
- session.add(sample_object_1)
315
- session.add(sample_object_2)
316
- session.flush()
317
-
318
- stmt = version_agnostic_select(SampleObject).where(
319
- SampleObject.name.in_(["Bim Adewunmi", "Miki Meek"])
320
- )
321
-
322
- if sqlalchemy_1_3():
323
- output = [i for i in session.execute(stmt)]
324
- else:
325
- output = [i for i in session.scalars(stmt)]
326
-
327
- assert len(output) == 2
328
-
329
- Base.metadata.drop_all(db_engine)
330
-
331
-
332
- def test_dialect_type_mappings(db_engine, metadata_obj: MetaData):
333
- """Confirms that we get back the same time we declared in a model and inserted using Core"""
334
-
335
- class Base(DeclarativeBase):
336
- pass
337
-
338
- SampleTable = Table(
339
- "PySQLTest_{}".format(datetime.datetime.utcnow().strftime("%s")),
340
- metadata_obj,
341
- Column("string_example", String(255)),
342
- Column("integer_example", Integer),
343
- Column("boolean_example", BOOLEAN),
344
- Column("decimal_example", DECIMAL(10, 2)),
345
- Column("date_example", Date),
346
- )
347
-
348
- string_example = ""
349
- integer_example = 100
350
- boolean_example = True
351
- decimal_example = decimal.Decimal(125)
352
- date_example = datetime.date(2013, 1, 1)
353
-
354
- metadata_obj.create_all(db_engine)
355
-
356
- insert_stmt = insert(SampleTable).values(
357
- string_example=string_example,
358
- integer_example=integer_example,
359
- boolean_example=boolean_example,
360
- decimal_example=decimal_example,
361
- date_example=date_example,
362
- )
363
-
364
- with db_engine.connect() as conn:
365
- conn.execute(insert_stmt)
366
-
367
- select_stmt = version_agnostic_select(SampleTable)
368
- with db_engine.begin() as conn:
369
- resp = conn.execute(select_stmt)
370
-
371
- result = resp.fetchall()
372
- this_row = result[0]
373
-
374
- assert this_row.string_example == string_example
375
- assert this_row.integer_example == integer_example
376
- assert this_row.boolean_example == boolean_example
377
- assert this_row.decimal_example == decimal_example
378
- assert this_row.date_example == date_example
379
-
380
- metadata_obj.drop_all(db_engine)
381
-
382
-
383
- def test_inspector_smoke_test(samples_engine: Engine):
384
- """It does not appear that 3L namespace is supported here"""
385
-
386
- schema, table = "nyctaxi", "trips"
387
-
388
- try:
389
- inspector = Inspector.from_engine(samples_engine)
390
- except Exception as e:
391
- assert False, f"Could not build inspector: {e}"
392
-
393
- # Expect six columns
394
- columns = inspector.get_columns(table, schema=schema)
395
-
396
- # Expect zero views, but the method should return
397
- views = inspector.get_view_names(schema=schema)
398
-
399
- assert (
400
- len(columns) == 6
401
- ), "Dialect did not find the expected number of columns in samples.nyctaxi.trips"
402
- assert len(views) == 0, "Views could not be fetched"
403
-
404
-
405
- @pytest.mark.skip(reason="engine.table_names has been removed in sqlalchemy verison 2")
406
- def test_get_table_names_smoke_test(samples_engine: Engine):
407
- with samples_engine.connect() as conn:
408
- _names = samples_engine.table_names(schema="nyctaxi", connection=conn) # type: ignore
409
- _names is not None, "get_table_names did not succeed"
410
-
411
-
412
- def test_has_table_across_schemas(
413
- db_engine: Engine, samples_engine: Engine, catalog: str, schema: str
414
- ):
415
- """For this test to pass these conditions must be met:
416
- - Table samples.nyctaxi.trips must exist
417
- - Table samples.tpch.customer must exist
418
- - The `catalog` and `schema` environment variables must be set and valid
419
- """
420
-
421
- with samples_engine.connect() as conn:
422
- # 1) Check for table within schema declared at engine creation time
423
- assert samples_engine.dialect.has_table(connection=conn, table_name="trips")
424
-
425
- # 2) Check for table within another schema in the same catalog
426
- assert samples_engine.dialect.has_table(
427
- connection=conn, table_name="customer", schema="tpch"
428
- )
429
-
430
- # 3) Check for a table within a different catalog
431
- # Create a table in a different catalog
432
- with db_engine.connect() as conn:
433
- conn.execute(text("CREATE TABLE test_has_table (numbers_are_cool INT);"))
434
-
435
- try:
436
- # Verify that this table is not found in the samples catalog
437
- assert not samples_engine.dialect.has_table(
438
- connection=conn, table_name="test_has_table"
439
- )
440
- # Verify that this table is found in a separate catalog
441
- assert samples_engine.dialect.has_table(
442
- connection=conn,
443
- table_name="test_has_table",
444
- schema=schema,
445
- catalog=catalog,
446
- )
447
- finally:
448
- conn.execute(text("DROP TABLE test_has_table;"))
449
-
450
-
451
- def test_user_agent_adjustment(db_engine):
452
- # If .connect() is called multiple times on an engine, don't keep pre-pending the user agent
453
- # https://github.com/databricks/databricks-sql-python/issues/192
454
- c1 = db_engine.connect()
455
- c2 = db_engine.connect()
456
-
457
- def get_conn_user_agent(conn):
458
- return conn.connection.dbapi_connection.thrift_backend._transport._headers.get(
459
- "User-Agent"
460
- )
461
-
462
- ua1 = get_conn_user_agent(c1)
463
- ua2 = get_conn_user_agent(c2)
464
- same_ua = ua1 == ua2
465
-
466
- c1.close()
467
- c2.close()
468
-
469
- assert same_ua, f"User agents didn't match \n {ua1} \n {ua2}"
470
-
471
-
472
- @pytest.fixture
473
- def sample_table(metadata_obj: MetaData, db_engine: Engine):
474
- """This fixture creates a sample table and cleans it up after the test is complete."""
475
- from databricks.sqlalchemy._parse import GET_COLUMNS_TYPE_MAP
476
-
477
- table_name = "PySQLTest_{}".format(datetime.datetime.utcnow().strftime("%s"))
478
-
479
- args: List[Column] = [
480
- Column(colname, coltype) for colname, coltype in GET_COLUMNS_TYPE_MAP.items()
481
- ]
482
-
483
- SampleTable = Table(table_name, metadata_obj, *args)
484
-
485
- metadata_obj.create_all(db_engine)
486
-
487
- yield table_name
488
-
489
- metadata_obj.drop_all(db_engine)
490
-
491
-
492
- def test_get_columns(db_engine, sample_table: str):
493
- """Created after PECO-1297 and Github Issue #295 to verify that get_columsn behaves like it should for all known SQLAlchemy types"""
494
-
495
- inspector = Inspector.from_engine(db_engine)
496
-
497
- # this raises an exception if `parse_column_info_from_tgetcolumnsresponse` fails a lookup
498
- columns = inspector.get_columns(sample_table)
499
-
500
- assert True
501
-
502
-
503
- class TestCommentReflection:
504
- @pytest.fixture(scope="class")
505
- def engine(self, connection_details: dict):
506
- HOST = connection_details["host"]
507
- HTTP_PATH = connection_details["http_path"]
508
- ACCESS_TOKEN = connection_details["access_token"]
509
- CATALOG = connection_details["catalog"]
510
- SCHEMA = connection_details["schema"]
511
-
512
- connection_string = f"databricks://token:{ACCESS_TOKEN}@{HOST}?http_path={HTTP_PATH}&catalog={CATALOG}&schema={SCHEMA}"
513
- connect_args = {"_user_agent_entry": USER_AGENT_TOKEN}
514
-
515
- engine = create_engine(connection_string, connect_args=connect_args)
516
- return engine
517
-
518
- @pytest.fixture
519
- def inspector(self, engine: Engine) -> Inspector:
520
- return Inspector.from_engine(engine)
521
-
522
- @pytest.fixture(scope="class")
523
- def table(self, engine):
524
- md = MetaData()
525
- tbl = Table(
526
- "foo",
527
- md,
528
- Column("bar", String, comment="column comment"),
529
- comment="table comment",
530
- )
531
- md.create_all(bind=engine)
532
-
533
- yield tbl
534
-
535
- md.drop_all(bind=engine)
536
-
537
- def test_table_comment_reflection(self, inspector: Inspector, table: Table):
538
- comment = inspector.get_table_comment(table.name)
539
- assert comment == {"text": "table comment"}
540
-
541
- def test_column_comment(self, inspector: Inspector, table: Table):
542
- result = inspector.get_columns(table.name)[0].get("comment")
543
- assert result == "column comment"
@@ -1,96 +0,0 @@
1
- import pytest
2
- from sqlalchemy import Column, MetaData, String, Table, create_engine
3
- from sqlalchemy.schema import (
4
- CreateTable,
5
- DropColumnComment,
6
- DropTableComment,
7
- SetColumnComment,
8
- SetTableComment,
9
- )
10
-
11
-
12
- class DDLTestBase:
13
- engine = create_engine(
14
- "databricks://token:****@****?http_path=****&catalog=****&schema=****"
15
- )
16
-
17
- def compile(self, stmt):
18
- return str(stmt.compile(bind=self.engine))
19
-
20
-
21
- class TestColumnCommentDDL(DDLTestBase):
22
- @pytest.fixture
23
- def metadata(self) -> MetaData:
24
- """Assemble a metadata object with one table containing one column."""
25
- metadata = MetaData()
26
-
27
- column = Column("foo", String, comment="bar")
28
- table = Table("foobar", metadata, column)
29
-
30
- return metadata
31
-
32
- @pytest.fixture
33
- def table(self, metadata) -> Table:
34
- return metadata.tables.get("foobar")
35
-
36
- @pytest.fixture
37
- def column(self, table) -> Column:
38
- return table.columns[0]
39
-
40
- def test_create_table_with_column_comment(self, table):
41
- stmt = CreateTable(table)
42
- output = self.compile(stmt)
43
-
44
- # output is a CREATE TABLE statement
45
- assert "foo STRING COMMENT 'bar'" in output
46
-
47
- def test_alter_table_add_column_comment(self, column):
48
- stmt = SetColumnComment(column)
49
- output = self.compile(stmt)
50
- assert output == "ALTER TABLE foobar ALTER COLUMN foo COMMENT 'bar'"
51
-
52
- def test_alter_table_drop_column_comment(self, column):
53
- stmt = DropColumnComment(column)
54
- output = self.compile(stmt)
55
- assert output == "ALTER TABLE foobar ALTER COLUMN foo COMMENT ''"
56
-
57
-
58
- class TestTableCommentDDL(DDLTestBase):
59
- @pytest.fixture
60
- def metadata(self) -> MetaData:
61
- """Assemble a metadata object with one table containing one column."""
62
- metadata = MetaData()
63
-
64
- col1 = Column("foo", String)
65
- col2 = Column("foo", String)
66
- tbl_w_comment = Table("martin", metadata, col1, comment="foobar")
67
- tbl_wo_comment = Table("prs", metadata, col2)
68
-
69
- return metadata
70
-
71
- @pytest.fixture
72
- def table_with_comment(self, metadata) -> Table:
73
- return metadata.tables.get("martin")
74
-
75
- @pytest.fixture
76
- def table_without_comment(self, metadata) -> Table:
77
- return metadata.tables.get("prs")
78
-
79
- def test_create_table_with_comment(self, table_with_comment):
80
- stmt = CreateTable(table_with_comment)
81
- output = self.compile(stmt)
82
- assert "USING DELTA" in output
83
- assert "COMMENT 'foobar'" in output
84
-
85
- def test_alter_table_add_comment(self, table_without_comment: Table):
86
- table_without_comment.comment = "wireless mechanical keyboard"
87
- stmt = SetTableComment(table_without_comment)
88
- output = self.compile(stmt)
89
-
90
- assert output == "COMMENT ON TABLE prs IS 'wireless mechanical keyboard'"
91
-
92
- def test_alter_table_drop_comment(self, table_with_comment):
93
- """The syntax for COMMENT ON is here: https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-ddl-comment.html"""
94
- stmt = DropTableComment(table_with_comment)
95
- output = self.compile(stmt)
96
- assert output == "COMMENT ON TABLE martin IS NULL"