52
52
from sqlalchemy .exc import DBAPIError , NoSuchTableError
53
53
from .dml import Merge
54
54
55
+ RESERVED_WORDS = {
56
+ 'Error' , 'EOI' , 'Whitespace' , 'Comment' , 'CommentBlock' , 'Ident' , 'ColumnPosition' , 'LiteralString' ,
57
+ 'LiteralCodeString' , 'LiteralAtString' , 'PGLiteralHex' , 'MySQLLiteralHex' , 'LiteralInteger' , 'LiteralFloat' ,
58
+ 'HintPrefix' , 'HintSuffix' , 'DoubleEq' , 'Eq' , 'NotEq' , 'Lt' , 'Gt' , 'Lte' , 'Gte' , 'Spaceship' , 'Plus' ,
59
+ 'Minus' , 'Multiply' , 'Divide' , 'IntDiv' , 'Modulo' , 'StringConcat' , 'LParen' , 'RParen' , 'Comma' , 'Dot' ,
60
+ 'Colon' , 'DoubleColon' , 'ColonEqual' , 'SemiColon' , 'Backslash' , 'LBracket' , 'RBracket' , 'Caret' , 'LBrace' ,
61
+ 'RBrace' , 'RArrow' , 'LongRArrow' , 'FatRArrow' , 'HashRArrow' , 'HashLongRArrow' , 'TildeAsterisk' ,
62
+ 'ExclamationMarkTilde' , 'ExclamationMarkTildeAsterisk' , 'BitWiseAnd' , 'BitWiseOr' , 'BitWiseXor' ,
63
+ 'BitWiseNot' , 'ShiftLeft' , 'ShiftRight' , 'Factorial' , 'DoubleExclamationMark' , 'Abs' , 'SquareRoot' ,
64
+ 'CubeRoot' , 'Placeholder' , 'QuestionOr' , 'QuestionAnd' , 'ArrowAt' , 'AtArrow' , 'AtQuestion' , 'AtAt' ,
65
+ 'HashMinus' , 'ACCOUNT' , 'ALL' , 'ALLOWED_IP_LIST' , 'ADD' , 'AFTER' , 'AGGREGATING' , 'ANY' , 'APPEND_ONLY' ,
66
+ 'ARGS' , 'AUTO' , 'SOME' , 'ALTER' , 'ALWAYS' , 'ANALYZE' , 'AND' , 'ARRAY' , 'AS' , 'AST' , 'AT' , 'ASC' ,
67
+ 'ANTI' , 'ASYNC' , 'ATTACH' , 'BEFORE' , 'BETWEEN' , 'BIGINT' , 'BINARY' , 'BREAK' , 'LONGBLOB' , 'MEDIUMBLOB' ,
68
+ 'TINYBLOB' , 'BLOB' , 'BINARY_FORMAT' , 'BITMAP' , 'BLOCKED_IP_LIST' , 'BOOL' , 'BOOLEAN' , 'BOTH' , 'BY' ,
69
+ 'BROTLI' , 'BZ2' , 'CALL' , 'CASE' , 'CAST' , 'CATALOG' , 'CATALOGS' , 'CENTURY' , 'CHANGES' , 'CLUSTER' ,
70
+ 'COMMENT' , 'COMMENTS' , 'COMPACT' , 'CONNECTION' , 'CONNECTIONS' , 'CONSUME' , 'CONTENT_TYPE' , 'CONTINUE' ,
71
+ 'CHAR' , 'COLUMN' , 'COLUMNS' , 'CHARACTER' , 'CONFLICT' , 'COMPRESSION' , 'COPY_OPTIONS' , 'COPY' , 'COUNT' ,
72
+ 'CREDENTIAL' , 'CREATE' , 'CROSS' , 'CSV' , 'CURRENT' , 'CURRENT_TIMESTAMP' , 'DATABASE' , 'DATABASES' , 'DATA' ,
73
+ 'DATE' , 'DATE_ADD' , 'DATE_PART' , 'DATE_SUB' , 'DATE_TRUNC' , 'DATETIME' , 'DAY' , 'DECADE' , 'DECIMAL' ,
74
+ 'DECLARE' , 'DEFAULT' , 'DEFLATE' , 'DELETE' , 'DESC' , 'DETAILED_OUTPUT' , 'DESCRIBE' , 'DISABLE' ,
75
+ 'DISABLE_VARIANT_CHECK' , 'DISTINCT' , 'RESPECT' , 'IGNORE' , 'DIV' , 'DOUBLE_SHA1_PASSWORD' , 'DO' , 'DOUBLE' ,
76
+ 'DOW' , 'WEEK' , 'DELTA' , 'DOY' , 'DOWNLOAD' , 'DOWNSTREAM' , 'DROP' , 'DRY' , 'DYNAMIC' , 'EXCEPT' , 'EXCLUDE' ,
77
+ 'ELSE' , 'EMPTY_FIELD_AS' , 'ENABLE' , 'ENABLE_VIRTUAL_HOST_STYLE' , 'END' , 'ENDPOINT' , 'ENGINE' , 'ENGINES' ,
78
+ 'EPOCH' , 'ERROR_ON_COLUMN_COUNT_MISMATCH' , 'ESCAPE' , 'EXCEPTION_BACKTRACE' , 'EXISTS' , 'EXPLAIN' , 'EXPIRE' ,
79
+ 'EXTRACT' , 'ELSEIF' , 'FALSE' , 'FIELDS' , 'FIELD_DELIMITER' , 'NAN_DISPLAY' , 'NULL_DISPLAY' , 'NULL_IF' ,
80
+ 'FILE_FORMAT' , 'FILE' , 'FILES' , 'FINAL' , 'FLASHBACK' , 'FLOAT' , 'FLOAT32' , 'FLOAT64' , 'FOR' , 'FORCE' ,
81
+ 'FORMAT' , 'FOLLOWING' , 'FORMAT_NAME' , 'FORMATS' , 'FRAGMENTS' , 'FROM' , 'FULL' , 'FUNCTION' , 'FUNCTIONS' ,
82
+ 'TABLE_FUNCTIONS' , 'SET_VAR' , 'FUSE' , 'GET' , 'GENERATED' , 'GEOMETRY' , 'GLOBAL' , 'GRAPH' , 'GROUP' , 'GZIP' ,
83
+ 'HAVING' , 'HIGH' , 'HISTORY' , 'HIVE' , 'HOUR' , 'HOURS' , 'ICEBERG' , 'INTERSECT' , 'IDENTIFIED' , 'IDENTIFIER' ,
84
+ 'IF' , 'IN' , 'INCREMENTAL' , 'INDEX' , 'INFORMATION' , 'INITIALIZE' , 'INNER' , 'INSERT' , 'INT' , 'INT16' ,
85
+ 'INT32' , 'INT64' , 'INT8' , 'INTEGER' , 'INTERVAL' , 'INTO' , 'INVERTED' , 'IMMEDIATE' , 'IS' , 'ISODOW' ,
86
+ 'ISOYEAR' , 'JOIN' , 'JSON' , 'JULIAN' , 'JWT' , 'KEY' , 'KILL' , 'LATERAL' , 'LOCATION_PREFIX' , 'LOCKS' ,
87
+ 'LOGICAL' , 'LOOP' , 'SECONDARY' , 'ROLES' , 'L2DISTANCE' , 'LEADING' , 'LEFT' , 'LET' , 'LIKE' , 'LIMIT' ,
88
+ 'LIST' , 'LOW' , 'LZO' , 'MASKING' , 'MAP' , 'MAX_FILE_SIZE' , 'MASTER_KEY' , 'MEDIUM' , 'MEMO' , 'MEMORY' ,
89
+ 'METRICS' , 'MICROSECONDS' , 'MILLENNIUM' , 'MILLISECONDS' , 'MINUTE' , 'MONTH' , 'MODIFY' , 'MATERIALIZED' ,
90
+ 'MUST_CHANGE_PASSWORD' , 'NON_DISPLAY' , 'NATURAL' , 'NETWORK' , 'DISABLED' , 'NDJSON' , 'NO_PASSWORD' , 'NONE' ,
91
+ 'NOT' , 'NOTENANTSETTING' , 'DEFAULT_ROLE' , 'NULL' , 'NULLABLE' , 'OBJECT' , 'OF' , 'OFFSET' , 'ON' ,
92
+ 'ON_CREATE' , 'ON_SCHEDULE' , 'OPTIMIZE' , 'OPTIONS' , 'OR' , 'ORC' , 'ORDER' , 'OUTPUT_HEADER' , 'OUTER' ,
93
+ 'ON_ERROR' , 'OVER' , 'OVERWRITE' , 'PARTITION' , 'PARQUET' , 'PASSWORD' , 'PASSWORD_MIN_LENGTH' ,
94
+ 'PASSWORD_MAX_LENGTH' , 'PASSWORD_MIN_UPPER_CASE_CHARS' , 'PASSWORD_MIN_LOWER_CASE_CHARS' ,
95
+ 'PASSWORD_MIN_NUMERIC_CHARS' , 'PASSWORD_MIN_SPECIAL_CHARS' , 'PASSWORD_MIN_AGE_DAYS' , 'PASSWORD_MAX_AGE_DAYS' ,
96
+ 'PASSWORD_MAX_RETRIES' , 'PASSWORD_LOCKOUT_TIME_MINS' , 'PASSWORD_HISTORY' , 'PATTERN' , 'PIPELINE' ,
97
+ 'PLAINTEXT_PASSWORD' , 'POLICIES' , 'POLICY' , 'POSITION' , 'PROCESSLIST' , 'PRIORITY' , 'PURGE' , 'PUT' ,
98
+ 'QUARTER' , 'QUERY' , 'QUOTE' , 'RANGE' , 'RAWDEFLATE' , 'READ_ONLY' , 'RECLUSTER' , 'RECORD_DELIMITER' ,
99
+ 'REFERENCE_USAGE' , 'REFRESH' , 'REGEXP' , 'RENAME' , 'REPLACE' , 'RETURN_FAILED_ONLY' , 'REVERSE' , 'MERGE' ,
100
+ 'MATCHED' , 'MISSING_FIELD_AS' , 'NULL_FIELD_AS' , 'UNMATCHED' , 'ROW' , 'ROWS' , 'ROW_TAG' , 'GRANT' , 'REPEAT' ,
101
+ 'ROLE' , 'PRECEDING' , 'PRECISION' , 'PRESIGN' , 'PRIVILEGES' , 'QUALIFY' , 'REMOVE' , 'RETAIN' , 'REVOKE' ,
102
+ 'RECURSIVE' , 'RETURN' , 'RETURNS' , 'RESULTSET' , 'RUN' , 'GRANTS' , 'REFRESH_MODE' , 'RIGHT' , 'RLIKE' , 'RAW' ,
103
+ 'OPTIMIZED' , 'SCHEMA' , 'SCHEMAS' , 'SECOND' , 'MILLISECOND' , 'SELECT' , 'PIVOT' , 'UNPIVOT' , 'SEGMENT' ,
104
+ 'SET' , 'UNSET' , 'SESSION' , 'SETTINGS' , 'STAGES' , 'STATISTIC' , 'SUMMARY' , 'SHA256_PASSWORD' , 'SHOW' ,
105
+ 'SINCE' , 'SIGNED' , 'SINGLE' , 'SIZE_LIMIT' , 'MAX_FILES' , 'SKIP_HEADER' , 'SMALLINT' , 'SNAPPY' , 'SNAPSHOT' ,
106
+ 'SPLIT_SIZE' , 'STAGE' , 'SYNTAX' , 'USAGE' , 'UPDATE' , 'UPLOAD' , 'SEQUENCE' , 'SHARE' , 'SHARES' , 'SUPER' ,
107
+ 'STATUS' , 'STORED' , 'STREAM' , 'STREAMS' , 'STRING' , 'SUBSTRING' , 'SUBSTR' , 'SEMI' , 'SOUNDS' , 'SYNC' ,
108
+ 'SYSTEM' , 'STORAGE_TYPE' , 'TABLE' , 'TABLES' , 'TARGET_LAG' , 'TEXT' , 'LONGTEXT' , 'MEDIUMTEXT' , 'TINYTEXT' ,
109
+ 'TENANTSETTING' , 'TENANTS' , 'TENANT' , 'THEN' , 'TIMESTAMP' , 'TIMEZONE_HOUR' , 'TIMEZONE_MINUTE' , 'TIMEZONE' ,
110
+ 'TINYINT' , 'TO' , 'TOKEN' , 'TRAILING' , 'TRANSIENT' , 'TRIM' , 'TRUE' , 'TRUNCATE' , 'TRY_CAST' , 'TSV' ,
111
+ 'TUPLE' , 'TYPE' , 'UNBOUNDED' , 'UNION' , 'UINT16' , 'UINT32' , 'UINT64' , 'UINT8' , 'UNDROP' , 'UNSIGNED' ,
112
+ 'URL' , 'METHOD' , 'AUTHORIZATION_HEADER' , 'USE' , 'USER' , 'USERS' , 'USING' , 'VACUUM' , 'VALUES' ,
113
+ 'VALIDATION_MODE' , 'VARBINARY' , 'VARCHAR' , 'VARIANT' , 'VERBOSE' , 'VIEW' , 'VIEWS' , 'VIRTUAL' , 'WHEN' ,
114
+ 'WHERE' , 'WHILE' , 'WINDOW' , 'WITH' , 'XML' , 'XOR' , 'XZ' , 'YEAR' , 'ZSTD' , 'NULLIF' , 'COALESCE' , 'RANDOM' ,
115
+ 'IFNULL' , 'NULLS' , 'FIRST' , 'LAST' , 'IGNORE_RESULT' , 'GROUPING' , 'SETS' , 'CUBE' , 'ROLLUP' , 'INDEXES' ,
116
+ 'ADDRESS' , 'OWNERSHIP' , 'READ' , 'WRITE' , 'UDF' , 'HANDLER' , 'LANGUAGE' , 'TASK' , 'TASKS' , 'TOP' ,
117
+ 'WAREHOUSE' , 'SCHEDULE' , 'SUSPEND_TASK_AFTER_NUM_FAILURES' , 'CRON' , 'EXECUTE' , 'SUSPEND' , 'RESUME' , 'PIPE' ,
118
+ 'NOTIFICATION' , 'INTEGRATION' , 'ENABLED' , 'WEBHOOK' , 'ERROR_INTEGRATION' , 'AUTO_INGEST' ,
119
+ 'PIPE_EXECUTION_PAUSED' , 'PREFIX' , 'MODIFIED_AFTER' , 'UNTIL' , 'BEGIN' , 'TRANSACTION' , 'COMMIT' , 'ABORT' ,
120
+ 'ROLLBACK' , 'TEMPORARY' , 'SECONDS' , 'DAYS'
121
+ }
122
+
55
123
56
124
# Type decorators
57
125
class ARRAY (sqltypes .TypeEngine ):
@@ -106,6 +174,45 @@ def process(value):
106
174
107
175
return process
108
176
177
+ def literal_processor (self , dialect ):
178
+ def process (value ):
179
+ if value is not None :
180
+ datetime_str = value .isoformat (" " , timespec = "microseconds" )
181
+ return f"'{ datetime_str } '"
182
+
183
+ return process
184
+
185
+
186
+ class DatabendTime (sqltypes .TIME ):
187
+ __visit_name__ = "TIME"
188
+
189
+ _reg = re .compile (r"(?:\d+)-(?:\d+)-(?:\d+) (\d+):(\d+):(\d+)" )
190
+
191
+ def result_processor (self , dialect , coltype ):
192
+ def process (value ):
193
+ if value is None :
194
+ return None
195
+ if isinstance (value , str ):
196
+ m = self ._reg .match (value )
197
+ if not m :
198
+ raise ValueError (
199
+ "could not parse %r as a datetime value" % (value ,)
200
+ )
201
+ return datetime .time (* [int (x or 0 ) for x in m .groups ()])
202
+ else :
203
+ return value .time ()
204
+
205
+ return process
206
+
207
+ def literal_processor (self , dialect ):
208
+ def process (value ):
209
+ if value is not None :
210
+ from_min_value = datetime .datetime .combine (datetime .date (1000 , 1 , 1 ), value )
211
+ time_str = from_min_value .isoformat (timespec = "microseconds" )
212
+ return f"'{ time_str } '"
213
+
214
+ return process
215
+
109
216
110
217
class DatabendNumeric (sqltypes .Numeric ):
111
218
def result_processor (self , dialect , type_ ):
@@ -125,6 +232,46 @@ def process(value):
125
232
return process
126
233
127
234
235
+ class DatabendInterval (sqltypes .Interval ):
236
+ """Stores interval as a datetime relative to epoch, see base implementation."""
237
+
238
+ _reg = re .compile (r"(\d+)-(\d+)-(\d+) (\d+):(\d+):(\d+)" )
239
+
240
+ def result_processor (self , dialect , coltype ):
241
+ def process (value ):
242
+ if value is None :
243
+ return None
244
+ if isinstance (value , str ):
245
+ m = self ._reg .match (value )
246
+ if not m :
247
+ raise ValueError (
248
+ "could not parse %r as a datetime value" % (value ,)
249
+ )
250
+ groups = m .groups ()
251
+ dt = datetime .datetime (* [
252
+ int (groups [0 ] or self .epoch .year ),
253
+ int (groups [1 ] or self .epoch .month ),
254
+ int (groups [2 ] or self .epoch .day ),
255
+ int (groups [3 ] or 0 ),
256
+ int (groups [4 ] or 0 ),
257
+ int (groups [5 ] or 0 ),
258
+ ])
259
+ else :
260
+ dt = value
261
+ return dt - self .epoch
262
+
263
+ return process
264
+
265
+ def literal_processor (self , dialect ):
266
+ def process (value ):
267
+ if value is not None :
268
+ d = self .epoch + value
269
+ interval_str = d .isoformat (" " , timespec = "microseconds" )
270
+ return f"'{ interval_str } '"
271
+
272
+ return process
273
+
274
+
128
275
# Type converters
129
276
ischema_names = {
130
277
"bigint" : BIGINT ,
@@ -156,10 +303,14 @@ def process(value):
156
303
"varchar" : VARCHAR ,
157
304
"boolean" : BOOLEAN ,
158
305
"binary" : BINARY ,
306
+ "time" : DatabendTime ,
307
+ "interval" : DatabendInterval ,
159
308
}
160
309
161
310
# Column spec
162
311
colspecs = {
312
+ sqltypes .Interval : DatabendInterval ,
313
+ sqltypes .Time : DatabendTime ,
163
314
sqltypes .Date : DatabendDate ,
164
315
sqltypes .DateTime : DatabendDateTime ,
165
316
sqltypes .DECIMAL : DatabendNumeric ,
@@ -168,7 +319,7 @@ def process(value):
168
319
169
320
170
321
class DatabendIdentifierPreparer (PGIdentifierPreparer ):
171
- pass
322
+ reserved_words = { r . lower () for r in RESERVED_WORDS }
172
323
173
324
174
325
class DatabendCompiler (PGCompiler ):
@@ -211,10 +362,14 @@ def visit_concat_op_binary(self, binary, operator, **kw):
211
362
212
363
def render_literal_value (self , value , type_ ):
213
364
value = super (DatabendCompiler , self ).render_literal_value (value , type_ )
214
- if isinstance (type_ , sqltypes .DateTime ):
215
- value = "toDateTime(%s)" % value
216
- if isinstance (type_ , sqltypes .Date ):
217
- value = "toDate(%s)" % value
365
+ # if isinstance(type_, sqltypes.DateTime):
366
+ # return "to_datetime(%s)" % value
367
+ # if isinstance(type_, sqltypes.Date):
368
+ # return "to_date(%s)" % value
369
+ # if isinstance(type_, sqltypes.Time):
370
+ # return "to_datetime(%s)" % value
371
+ # if isinstance(type_, sqltypes.Interval):
372
+ # return "to_datetime(%s)" % value
218
373
return value
219
374
220
375
def limit_clause (self , select , ** kw ):
@@ -334,6 +489,8 @@ def visit_when_merge_unmatched(self, merge_unmatched, **kw):
334
489
", " .join (set_cols ),
335
490
", " .join (map (lambda e : e ._compiler_dispatch (self , ** kw ), sets_vals )),
336
491
)
492
+
493
+
337
494
class DatabendExecutionContext (default .DefaultExecutionContext ):
338
495
@sa_util .memoized_property
339
496
def should_autocommit (self ):
@@ -366,6 +523,9 @@ def visit_NVARCHAR(self, type_, **kw):
366
523
def visit_JSON (self , type_ , ** kw ):
367
524
return "JSON" # or VARIANT
368
525
526
+ def visit_TIME (self , type_ , ** kw ):
527
+ return "DATETIME"
528
+
369
529
370
530
class DatabendDDLCompiler (compiler .DDLCompiler ):
371
531
@@ -635,9 +795,6 @@ def get_table_names(self, connection, schema=None, **kw):
635
795
select table_name
636
796
from information_schema.tables
637
797
where table_schema = :schema_name
638
- """
639
- if self .server_version_info <= (1 , 2 , 410 ):
640
- table_name_query += """
641
798
and engine NOT LIKE '%VIEW%'
642
799
"""
643
800
query = text (
@@ -654,17 +811,19 @@ def get_table_names(self, connection, schema=None, **kw):
654
811
@reflection .cache
655
812
def get_view_names (self , connection , schema = None , ** kw ):
656
813
view_name_query = """
657
- select table_name
658
- from information_schema.views
659
- where table_schema = :schema_name
660
- """
661
- if self .server_version_info <= (1 , 2 , 410 ):
662
- view_name_query = """
663
814
select table_name
664
815
from information_schema.tables
665
816
where table_schema = :schema_name
666
817
and engine LIKE '%VIEW%'
667
- """
818
+ """
819
+ # This handles bug that existed a while, views were not included in information_schema.tables
820
+ # https://github.com/datafuselabs/databend/issues/16039
821
+ if self .server_version_info > (1 , 2 , 410 ) and self .server_version_info <= (1 , 2 , 566 ):
822
+ view_name_query = """
823
+ select table_name
824
+ from information_schema.views
825
+ where table_schema = :schema_name
826
+ """
668
827
query = text (
669
828
view_name_query
670
829
).bindparams (
@@ -694,6 +853,13 @@ def get_table_options(self, connection, table_name, schema=None, **kw):
694
853
FROM system.tables
695
854
WHERE database = :schema_name
696
855
and name = :table_name
856
+
857
+ UNION
858
+
859
+ SELECT engine_full, NULL as cluster_by, NULL as is_transient
860
+ FROM system.views
861
+ WHERE database = :schema_name
862
+ and name = :table_name
697
863
"""
698
864
).bindparams (
699
865
bindparam ("table_name" , type_ = sqltypes .Unicode ),
0 commit comments