sqlglot.generator
1from __future__ import annotations 2 3import logging 4import re 5import typing as t 6from collections import defaultdict 7from functools import reduce 8 9from sqlglot import exp 10from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages 11from sqlglot.helper import apply_index_offset, csv, seq_get 12from sqlglot.jsonpath import ALL_JSON_PATH_PARTS, JSON_PATH_PART_TRANSFORMS 13from sqlglot.time import format_time 14from sqlglot.tokens import TokenType 15 16if t.TYPE_CHECKING: 17 from sqlglot._typing import E 18 from sqlglot.dialects.dialect import DialectType 19 20logger = logging.getLogger("sqlglot") 21 22ESCAPED_UNICODE_RE = re.compile(r"\\(\d+)") 23 24 25class _Generator(type): 26 def __new__(cls, clsname, bases, attrs): 27 klass = super().__new__(cls, clsname, bases, attrs) 28 29 # Remove transforms that correspond to unsupported JSONPathPart expressions 30 for part in ALL_JSON_PATH_PARTS - klass.SUPPORTED_JSON_PATH_PARTS: 31 klass.TRANSFORMS.pop(part, None) 32 33 return klass 34 35 36class Generator(metaclass=_Generator): 37 """ 38 Generator converts a given syntax tree to the corresponding SQL string. 39 40 Args: 41 pretty: Whether to format the produced SQL string. 42 Default: False. 43 identify: Determines when an identifier should be quoted. Possible values are: 44 False (default): Never quote, except in cases where it's mandatory by the dialect. 45 True or 'always': Always quote. 46 'safe': Only quote identifiers that are case insensitive. 47 normalize: Whether to normalize identifiers to lowercase. 48 Default: False. 49 pad: The pad size in a formatted string. For example, this affects the indentation of 50 a projection in a query, relative to its nesting level. 51 Default: 2. 52 indent: The indentation size in a formatted string. For example, this affects the 53 indentation of subqueries and filters under a `WHERE` clause. 54 Default: 2. 55 normalize_functions: How to normalize function names. Possible values are: 56 "upper" or True (default): Convert names to uppercase. 57 "lower": Convert names to lowercase. 58 False: Disables function name normalization. 59 unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. 60 Default ErrorLevel.WARN. 61 max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. 62 This is only relevant if unsupported_level is ErrorLevel.RAISE. 63 Default: 3 64 leading_comma: Whether the comma is leading or trailing in select expressions. 65 This is only relevant when generating in pretty mode. 66 Default: False 67 max_text_width: The max number of characters in a segment before creating new lines in pretty mode. 68 The default is on the smaller end because the length only represents a segment and not the true 69 line length. 70 Default: 80 71 comments: Whether to preserve comments in the output SQL code. 72 Default: True 73 """ 74 75 TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = { 76 **JSON_PATH_PART_TRANSFORMS, 77 exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}", 78 exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}", 79 exp.CaseSpecificColumnConstraint: lambda _, 80 e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC", 81 exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}", 82 exp.CharacterSetProperty: lambda self, 83 e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}", 84 exp.ClusteredColumnConstraint: lambda self, 85 e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})", 86 exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}", 87 exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}", 88 exp.CopyGrantsProperty: lambda *_: "COPY GRANTS", 89 exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}", 90 exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}", 91 exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}", 92 exp.EphemeralColumnConstraint: lambda self, 93 e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}", 94 exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}", 95 exp.ExecuteAsProperty: lambda self, e: self.naked_property(e), 96 exp.ExternalProperty: lambda *_: "EXTERNAL", 97 exp.GlobalProperty: lambda *_: "GLOBAL", 98 exp.HeapProperty: lambda *_: "HEAP", 99 exp.IcebergProperty: lambda *_: "ICEBERG", 100 exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})", 101 exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}", 102 exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}", 103 exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}", 104 exp.JSONExtract: lambda self, e: self.func( 105 "JSON_EXTRACT", e.this, e.expression, *e.expressions 106 ), 107 exp.JSONExtractScalar: lambda self, e: self.func( 108 "JSON_EXTRACT_SCALAR", e.this, e.expression, *e.expressions 109 ), 110 exp.LanguageProperty: lambda self, e: self.naked_property(e), 111 exp.LocationProperty: lambda self, e: self.naked_property(e), 112 exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG", 113 exp.MaterializedProperty: lambda *_: "MATERIALIZED", 114 exp.NonClusteredColumnConstraint: lambda self, 115 e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})", 116 exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX", 117 exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION", 118 exp.OnCommitProperty: lambda _, 119 e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS", 120 exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}", 121 exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}", 122 exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}", 123 exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}", 124 exp.RemoteWithConnectionModelProperty: lambda self, 125 e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}", 126 exp.ReturnsProperty: lambda self, e: self.naked_property(e), 127 exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}", 128 exp.SetConfigProperty: lambda self, e: self.sql(e, "this"), 129 exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET", 130 exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}", 131 exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}", 132 exp.SqlReadWriteProperty: lambda _, e: e.name, 133 exp.SqlSecurityProperty: lambda _, 134 e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}", 135 exp.StabilityProperty: lambda _, e: e.name, 136 exp.TemporaryProperty: lambda *_: "TEMPORARY", 137 exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}", 138 exp.Timestamp: lambda self, e: self.func("TIMESTAMP", e.this, e.expression), 139 exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}", 140 exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}", 141 exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions), 142 exp.TransientProperty: lambda *_: "TRANSIENT", 143 exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE", 144 exp.UnloggedProperty: lambda *_: "UNLOGGED", 145 exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]), 146 exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}", 147 exp.VolatileProperty: lambda *_: "VOLATILE", 148 exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}", 149 exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}", 150 } 151 152 # Whether null ordering is supported in order by 153 # True: Full Support, None: No support, False: No support in window specifications 154 NULL_ORDERING_SUPPORTED: t.Optional[bool] = True 155 156 # Whether ignore nulls is inside the agg or outside. 157 # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER 158 IGNORE_NULLS_IN_FUNC = False 159 160 # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported 161 LOCKING_READS_SUPPORTED = False 162 163 # Always do union distinct or union all 164 EXPLICIT_UNION = False 165 166 # Wrap derived values in parens, usually standard but spark doesn't support it 167 WRAP_DERIVED_VALUES = True 168 169 # Whether create function uses an AS before the RETURN 170 CREATE_FUNCTION_RETURN_AS = True 171 172 # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed 173 MATCHED_BY_SOURCE = True 174 175 # Whether the INTERVAL expression works only with values like '1 day' 176 SINGLE_STRING_INTERVAL = False 177 178 # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs 179 INTERVAL_ALLOWS_PLURAL_FORM = True 180 181 # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH") 182 LIMIT_FETCH = "ALL" 183 184 # Whether limit and fetch allows expresions or just limits 185 LIMIT_ONLY_LITERALS = False 186 187 # Whether a table is allowed to be renamed with a db 188 RENAME_TABLE_WITH_DB = True 189 190 # The separator for grouping sets and rollups 191 GROUPINGS_SEP = "," 192 193 # The string used for creating an index on a table 194 INDEX_ON = "ON" 195 196 # Whether join hints should be generated 197 JOIN_HINTS = True 198 199 # Whether table hints should be generated 200 TABLE_HINTS = True 201 202 # Whether query hints should be generated 203 QUERY_HINTS = True 204 205 # What kind of separator to use for query hints 206 QUERY_HINT_SEP = ", " 207 208 # Whether comparing against booleans (e.g. x IS TRUE) is supported 209 IS_BOOL_ALLOWED = True 210 211 # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement 212 DUPLICATE_KEY_UPDATE_WITH_SET = True 213 214 # Whether to generate the limit as TOP <value> instead of LIMIT <value> 215 LIMIT_IS_TOP = False 216 217 # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ... 218 RETURNING_END = True 219 220 # Whether to generate the (+) suffix for columns used in old-style join conditions 221 COLUMN_JOIN_MARKS_SUPPORTED = False 222 223 # Whether to generate an unquoted value for EXTRACT's date part argument 224 EXTRACT_ALLOWS_QUOTES = True 225 226 # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax 227 TZ_TO_WITH_TIME_ZONE = False 228 229 # Whether the NVL2 function is supported 230 NVL2_SUPPORTED = True 231 232 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax 233 SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE") 234 235 # Whether VALUES statements can be used as derived tables. 236 # MySQL 5 and Redshift do not allow this, so when False, it will convert 237 # SELECT * VALUES into SELECT UNION 238 VALUES_AS_TABLE = True 239 240 # Whether the word COLUMN is included when adding a column with ALTER TABLE 241 ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True 242 243 # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery) 244 UNNEST_WITH_ORDINALITY = True 245 246 # Whether FILTER (WHERE cond) can be used for conditional aggregation 247 AGGREGATE_FILTER_SUPPORTED = True 248 249 # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds 250 SEMI_ANTI_JOIN_WITH_SIDE = True 251 252 # Whether to include the type of a computed column in the CREATE DDL 253 COMPUTED_COLUMN_WITH_TYPE = True 254 255 # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY 256 SUPPORTS_TABLE_COPY = True 257 258 # Whether parentheses are required around the table sample's expression 259 TABLESAMPLE_REQUIRES_PARENS = True 260 261 # Whether a table sample clause's size needs to be followed by the ROWS keyword 262 TABLESAMPLE_SIZE_IS_ROWS = True 263 264 # The keyword(s) to use when generating a sample clause 265 TABLESAMPLE_KEYWORDS = "TABLESAMPLE" 266 267 # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI 268 TABLESAMPLE_WITH_METHOD = True 269 270 # The keyword to use when specifying the seed of a sample clause 271 TABLESAMPLE_SEED_KEYWORD = "SEED" 272 273 # Whether COLLATE is a function instead of a binary operator 274 COLLATE_IS_FUNC = False 275 276 # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle) 277 DATA_TYPE_SPECIFIERS_ALLOWED = False 278 279 # Whether conditions require booleans WHERE x = 0 vs WHERE x 280 ENSURE_BOOLS = False 281 282 # Whether the "RECURSIVE" keyword is required when defining recursive CTEs 283 CTE_RECURSIVE_KEYWORD_REQUIRED = True 284 285 # Whether CONCAT requires >1 arguments 286 SUPPORTS_SINGLE_ARG_CONCAT = True 287 288 # Whether LAST_DAY function supports a date part argument 289 LAST_DAY_SUPPORTS_DATE_PART = True 290 291 # Whether named columns are allowed in table aliases 292 SUPPORTS_TABLE_ALIAS_COLUMNS = True 293 294 # Whether UNPIVOT aliases are Identifiers (False means they're Literals) 295 UNPIVOT_ALIASES_ARE_IDENTIFIERS = True 296 297 # What delimiter to use for separating JSON key/value pairs 298 JSON_KEY_VALUE_PAIR_SEP = ":" 299 300 # INSERT OVERWRITE TABLE x override 301 INSERT_OVERWRITE = " OVERWRITE TABLE" 302 303 # Whether the SELECT .. INTO syntax is used instead of CTAS 304 SUPPORTS_SELECT_INTO = False 305 306 # Whether UNLOGGED tables can be created 307 SUPPORTS_UNLOGGED_TABLES = False 308 309 # Whether the CREATE TABLE LIKE statement is supported 310 SUPPORTS_CREATE_TABLE_LIKE = True 311 312 # Whether the LikeProperty needs to be specified inside of the schema clause 313 LIKE_PROPERTY_INSIDE_SCHEMA = False 314 315 # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be 316 # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args 317 MULTI_ARG_DISTINCT = True 318 319 # Whether the JSON extraction operators expect a value of type JSON 320 JSON_TYPE_REQUIRED_FOR_EXTRACTION = False 321 322 # Whether bracketed keys like ["foo"] are supported in JSON paths 323 JSON_PATH_BRACKETED_KEY_SUPPORTED = True 324 325 # Whether to escape keys using single quotes in JSON paths 326 JSON_PATH_SINGLE_QUOTE_ESCAPE = False 327 328 # The JSONPathPart expressions supported by this dialect 329 SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy() 330 331 # Whether any(f(x) for x in array) can be implemented by this dialect 332 CAN_IMPLEMENT_ARRAY_ANY = False 333 334 # Whether the function TO_NUMBER is supported 335 SUPPORTS_TO_NUMBER = True 336 337 # Whether or not union modifiers apply to the outer union or select. 338 # SELECT * FROM x UNION SELECT * FROM y LIMIT 1 339 # True means limit 1 happens after the union, False means it it happens on y. 340 OUTER_UNION_MODIFIERS = True 341 342 TYPE_MAPPING = { 343 exp.DataType.Type.NCHAR: "CHAR", 344 exp.DataType.Type.NVARCHAR: "VARCHAR", 345 exp.DataType.Type.MEDIUMTEXT: "TEXT", 346 exp.DataType.Type.LONGTEXT: "TEXT", 347 exp.DataType.Type.TINYTEXT: "TEXT", 348 exp.DataType.Type.MEDIUMBLOB: "BLOB", 349 exp.DataType.Type.LONGBLOB: "BLOB", 350 exp.DataType.Type.TINYBLOB: "BLOB", 351 exp.DataType.Type.INET: "INET", 352 exp.DataType.Type.ROWVERSION: "VARBINARY", 353 } 354 355 STAR_MAPPING = { 356 "except": "EXCEPT", 357 "replace": "REPLACE", 358 } 359 360 TIME_PART_SINGULARS = { 361 "MICROSECONDS": "MICROSECOND", 362 "SECONDS": "SECOND", 363 "MINUTES": "MINUTE", 364 "HOURS": "HOUR", 365 "DAYS": "DAY", 366 "WEEKS": "WEEK", 367 "MONTHS": "MONTH", 368 "QUARTERS": "QUARTER", 369 "YEARS": "YEAR", 370 } 371 372 AFTER_HAVING_MODIFIER_TRANSFORMS = { 373 "cluster": lambda self, e: self.sql(e, "cluster"), 374 "distribute": lambda self, e: self.sql(e, "distribute"), 375 "qualify": lambda self, e: self.sql(e, "qualify"), 376 "sort": lambda self, e: self.sql(e, "sort"), 377 "windows": lambda self, e: ( 378 self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True) 379 if e.args.get("windows") 380 else "" 381 ), 382 } 383 384 TOKEN_MAPPING: t.Dict[TokenType, str] = {} 385 386 STRUCT_DELIMITER = ("<", ">") 387 388 PARAMETER_TOKEN = "@" 389 NAMED_PLACEHOLDER_TOKEN = ":" 390 391 PROPERTIES_LOCATION = { 392 exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE, 393 exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA, 394 exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA, 395 exp.BackupProperty: exp.Properties.Location.POST_SCHEMA, 396 exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME, 397 exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA, 398 exp.ChecksumProperty: exp.Properties.Location.POST_NAME, 399 exp.CollateProperty: exp.Properties.Location.POST_SCHEMA, 400 exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA, 401 exp.Cluster: exp.Properties.Location.POST_SCHEMA, 402 exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA, 403 exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME, 404 exp.DefinerProperty: exp.Properties.Location.POST_CREATE, 405 exp.DictRange: exp.Properties.Location.POST_SCHEMA, 406 exp.DictProperty: exp.Properties.Location.POST_SCHEMA, 407 exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA, 408 exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA, 409 exp.EngineProperty: exp.Properties.Location.POST_SCHEMA, 410 exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA, 411 exp.ExternalProperty: exp.Properties.Location.POST_CREATE, 412 exp.FallbackProperty: exp.Properties.Location.POST_NAME, 413 exp.FileFormatProperty: exp.Properties.Location.POST_WITH, 414 exp.FreespaceProperty: exp.Properties.Location.POST_NAME, 415 exp.GlobalProperty: exp.Properties.Location.POST_CREATE, 416 exp.HeapProperty: exp.Properties.Location.POST_WITH, 417 exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA, 418 exp.IcebergProperty: exp.Properties.Location.POST_CREATE, 419 exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA, 420 exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME, 421 exp.JournalProperty: exp.Properties.Location.POST_NAME, 422 exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA, 423 exp.LikeProperty: exp.Properties.Location.POST_SCHEMA, 424 exp.LocationProperty: exp.Properties.Location.POST_SCHEMA, 425 exp.LockProperty: exp.Properties.Location.POST_SCHEMA, 426 exp.LockingProperty: exp.Properties.Location.POST_ALIAS, 427 exp.LogProperty: exp.Properties.Location.POST_NAME, 428 exp.MaterializedProperty: exp.Properties.Location.POST_CREATE, 429 exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME, 430 exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION, 431 exp.OnProperty: exp.Properties.Location.POST_SCHEMA, 432 exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION, 433 exp.Order: exp.Properties.Location.POST_SCHEMA, 434 exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA, 435 exp.PartitionedByProperty: exp.Properties.Location.POST_WITH, 436 exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA, 437 exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA, 438 exp.Property: exp.Properties.Location.POST_WITH, 439 exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA, 440 exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA, 441 exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA, 442 exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA, 443 exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA, 444 exp.SampleProperty: exp.Properties.Location.POST_SCHEMA, 445 exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA, 446 exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA, 447 exp.Set: exp.Properties.Location.POST_SCHEMA, 448 exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA, 449 exp.SetProperty: exp.Properties.Location.POST_CREATE, 450 exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA, 451 exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION, 452 exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION, 453 exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA, 454 exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA, 455 exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE, 456 exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA, 457 exp.TemporaryProperty: exp.Properties.Location.POST_CREATE, 458 exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA, 459 exp.TransientProperty: exp.Properties.Location.POST_CREATE, 460 exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA, 461 exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA, 462 exp.UnloggedProperty: exp.Properties.Location.POST_CREATE, 463 exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA, 464 exp.VolatileProperty: exp.Properties.Location.POST_CREATE, 465 exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION, 466 exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME, 467 exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA, 468 } 469 470 # Keywords that can't be used as unquoted identifier names 471 RESERVED_KEYWORDS: t.Set[str] = set() 472 473 # Expressions whose comments are separated from them for better formatting 474 WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 475 exp.Create, 476 exp.Delete, 477 exp.Drop, 478 exp.From, 479 exp.Insert, 480 exp.Join, 481 exp.Select, 482 exp.Union, 483 exp.Update, 484 exp.Where, 485 exp.With, 486 ) 487 488 # Expressions that should not have their comments generated in maybe_comment 489 EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 490 exp.Binary, 491 exp.Union, 492 ) 493 494 # Expressions that can remain unwrapped when appearing in the context of an INTERVAL 495 UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = ( 496 exp.Column, 497 exp.Literal, 498 exp.Neg, 499 exp.Paren, 500 ) 501 502 PARAMETERIZABLE_TEXT_TYPES = { 503 exp.DataType.Type.NVARCHAR, 504 exp.DataType.Type.VARCHAR, 505 exp.DataType.Type.CHAR, 506 exp.DataType.Type.NCHAR, 507 } 508 509 # Expressions that need to have all CTEs under them bubbled up to them 510 EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set() 511 512 SENTINEL_LINE_BREAK = "__SQLGLOT__LB__" 513 514 __slots__ = ( 515 "pretty", 516 "identify", 517 "normalize", 518 "pad", 519 "_indent", 520 "normalize_functions", 521 "unsupported_level", 522 "max_unsupported", 523 "leading_comma", 524 "max_text_width", 525 "comments", 526 "dialect", 527 "unsupported_messages", 528 "_escaped_quote_end", 529 "_escaped_identifier_end", 530 ) 531 532 def __init__( 533 self, 534 pretty: t.Optional[bool] = None, 535 identify: str | bool = False, 536 normalize: bool = False, 537 pad: int = 2, 538 indent: int = 2, 539 normalize_functions: t.Optional[str | bool] = None, 540 unsupported_level: ErrorLevel = ErrorLevel.WARN, 541 max_unsupported: int = 3, 542 leading_comma: bool = False, 543 max_text_width: int = 80, 544 comments: bool = True, 545 dialect: DialectType = None, 546 ): 547 import sqlglot 548 from sqlglot.dialects import Dialect 549 550 self.pretty = pretty if pretty is not None else sqlglot.pretty 551 self.identify = identify 552 self.normalize = normalize 553 self.pad = pad 554 self._indent = indent 555 self.unsupported_level = unsupported_level 556 self.max_unsupported = max_unsupported 557 self.leading_comma = leading_comma 558 self.max_text_width = max_text_width 559 self.comments = comments 560 self.dialect = Dialect.get_or_raise(dialect) 561 562 # This is both a Dialect property and a Generator argument, so we prioritize the latter 563 self.normalize_functions = ( 564 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 565 ) 566 567 self.unsupported_messages: t.List[str] = [] 568 self._escaped_quote_end: str = ( 569 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 570 ) 571 self._escaped_identifier_end: str = ( 572 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 573 ) 574 575 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 576 """ 577 Generates the SQL string corresponding to the given syntax tree. 578 579 Args: 580 expression: The syntax tree. 581 copy: Whether to copy the expression. The generator performs mutations so 582 it is safer to copy. 583 584 Returns: 585 The SQL string corresponding to `expression`. 586 """ 587 if copy: 588 expression = expression.copy() 589 590 expression = self.preprocess(expression) 591 592 self.unsupported_messages = [] 593 sql = self.sql(expression).strip() 594 595 if self.pretty: 596 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 597 598 if self.unsupported_level == ErrorLevel.IGNORE: 599 return sql 600 601 if self.unsupported_level == ErrorLevel.WARN: 602 for msg in self.unsupported_messages: 603 logger.warning(msg) 604 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 605 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 606 607 return sql 608 609 def preprocess(self, expression: exp.Expression) -> exp.Expression: 610 """Apply generic preprocessing transformations to a given expression.""" 611 if ( 612 not expression.parent 613 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 614 and any(node.parent is not expression for node in expression.find_all(exp.With)) 615 ): 616 from sqlglot.transforms import move_ctes_to_top_level 617 618 expression = move_ctes_to_top_level(expression) 619 620 if self.ENSURE_BOOLS: 621 from sqlglot.transforms import ensure_bools 622 623 expression = ensure_bools(expression) 624 625 return expression 626 627 def unsupported(self, message: str) -> None: 628 if self.unsupported_level == ErrorLevel.IMMEDIATE: 629 raise UnsupportedError(message) 630 self.unsupported_messages.append(message) 631 632 def sep(self, sep: str = " ") -> str: 633 return f"{sep.strip()}\n" if self.pretty else sep 634 635 def seg(self, sql: str, sep: str = " ") -> str: 636 return f"{self.sep(sep)}{sql}" 637 638 def pad_comment(self, comment: str) -> str: 639 comment = " " + comment if comment[0].strip() else comment 640 comment = comment + " " if comment[-1].strip() else comment 641 return comment 642 643 def maybe_comment( 644 self, 645 sql: str, 646 expression: t.Optional[exp.Expression] = None, 647 comments: t.Optional[t.List[str]] = None, 648 separated: bool = False, 649 ) -> str: 650 comments = ( 651 ((expression and expression.comments) if comments is None else comments) # type: ignore 652 if self.comments 653 else None 654 ) 655 656 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 657 return sql 658 659 comments_sql = " ".join( 660 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 661 ) 662 663 if not comments_sql: 664 return sql 665 666 comments_sql = self._replace_line_breaks(comments_sql) 667 668 if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS): 669 return ( 670 f"{self.sep()}{comments_sql}{sql}" 671 if not sql or sql[0].isspace() 672 else f"{comments_sql}{self.sep()}{sql}" 673 ) 674 675 return f"{sql} {comments_sql}" 676 677 def wrap(self, expression: exp.Expression | str) -> str: 678 this_sql = self.indent( 679 ( 680 self.sql(expression) 681 if isinstance(expression, exp.UNWRAPPED_QUERIES) 682 else self.sql(expression, "this") 683 ), 684 level=1, 685 pad=0, 686 ) 687 return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}" 688 689 def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str: 690 original = self.identify 691 self.identify = False 692 result = func(*args, **kwargs) 693 self.identify = original 694 return result 695 696 def normalize_func(self, name: str) -> str: 697 if self.normalize_functions == "upper" or self.normalize_functions is True: 698 return name.upper() 699 if self.normalize_functions == "lower": 700 return name.lower() 701 return name 702 703 def indent( 704 self, 705 sql: str, 706 level: int = 0, 707 pad: t.Optional[int] = None, 708 skip_first: bool = False, 709 skip_last: bool = False, 710 ) -> str: 711 if not self.pretty: 712 return sql 713 714 pad = self.pad if pad is None else pad 715 lines = sql.split("\n") 716 717 return "\n".join( 718 ( 719 line 720 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 721 else f"{' ' * (level * self._indent + pad)}{line}" 722 ) 723 for i, line in enumerate(lines) 724 ) 725 726 def sql( 727 self, 728 expression: t.Optional[str | exp.Expression], 729 key: t.Optional[str] = None, 730 comment: bool = True, 731 ) -> str: 732 if not expression: 733 return "" 734 735 if isinstance(expression, str): 736 return expression 737 738 if key: 739 value = expression.args.get(key) 740 if value: 741 return self.sql(value) 742 return "" 743 744 transform = self.TRANSFORMS.get(expression.__class__) 745 746 if callable(transform): 747 sql = transform(self, expression) 748 elif isinstance(expression, exp.Expression): 749 exp_handler_name = f"{expression.key}_sql" 750 751 if hasattr(self, exp_handler_name): 752 sql = getattr(self, exp_handler_name)(expression) 753 elif isinstance(expression, exp.Func): 754 sql = self.function_fallback_sql(expression) 755 elif isinstance(expression, exp.Property): 756 sql = self.property_sql(expression) 757 else: 758 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 759 else: 760 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 761 762 return self.maybe_comment(sql, expression) if self.comments and comment else sql 763 764 def uncache_sql(self, expression: exp.Uncache) -> str: 765 table = self.sql(expression, "this") 766 exists_sql = " IF EXISTS" if expression.args.get("exists") else "" 767 return f"UNCACHE TABLE{exists_sql} {table}" 768 769 def cache_sql(self, expression: exp.Cache) -> str: 770 lazy = " LAZY" if expression.args.get("lazy") else "" 771 table = self.sql(expression, "this") 772 options = expression.args.get("options") 773 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 774 sql = self.sql(expression, "expression") 775 sql = f" AS{self.sep()}{sql}" if sql else "" 776 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 777 return self.prepend_ctes(expression, sql) 778 779 def characterset_sql(self, expression: exp.CharacterSet) -> str: 780 if isinstance(expression.parent, exp.Cast): 781 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 782 default = "DEFAULT " if expression.args.get("default") else "" 783 return f"{default}CHARACTER SET={self.sql(expression, 'this')}" 784 785 def column_parts(self, expression: exp.Column) -> str: 786 return ".".join( 787 self.sql(part) 788 for part in ( 789 expression.args.get("catalog"), 790 expression.args.get("db"), 791 expression.args.get("table"), 792 expression.args.get("this"), 793 ) 794 if part 795 ) 796 797 def column_sql(self, expression: exp.Column) -> str: 798 join_mark = " (+)" if expression.args.get("join_mark") else "" 799 800 if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED: 801 join_mark = "" 802 self.unsupported("Outer join syntax using the (+) operator is not supported.") 803 804 return f"{self.column_parts(expression)}{join_mark}" 805 806 def columnposition_sql(self, expression: exp.ColumnPosition) -> str: 807 this = self.sql(expression, "this") 808 this = f" {this}" if this else "" 809 position = self.sql(expression, "position") 810 return f"{position}{this}" 811 812 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 813 column = self.sql(expression, "this") 814 kind = self.sql(expression, "kind") 815 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 816 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 817 kind = f"{sep}{kind}" if kind else "" 818 constraints = f" {constraints}" if constraints else "" 819 position = self.sql(expression, "position") 820 position = f" {position}" if position else "" 821 822 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 823 kind = "" 824 825 return f"{exists}{column}{kind}{constraints}{position}" 826 827 def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str: 828 this = self.sql(expression, "this") 829 kind_sql = self.sql(expression, "kind").strip() 830 return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql 831 832 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 833 this = self.sql(expression, "this") 834 if expression.args.get("not_null"): 835 persisted = " PERSISTED NOT NULL" 836 elif expression.args.get("persisted"): 837 persisted = " PERSISTED" 838 else: 839 persisted = "" 840 return f"AS {this}{persisted}" 841 842 def autoincrementcolumnconstraint_sql(self, _) -> str: 843 return self.token_sql(TokenType.AUTO_INCREMENT) 844 845 def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str: 846 if isinstance(expression.this, list): 847 this = self.wrap(self.expressions(expression, key="this", flat=True)) 848 else: 849 this = self.sql(expression, "this") 850 851 return f"COMPRESS {this}" 852 853 def generatedasidentitycolumnconstraint_sql( 854 self, expression: exp.GeneratedAsIdentityColumnConstraint 855 ) -> str: 856 this = "" 857 if expression.this is not None: 858 on_null = " ON NULL" if expression.args.get("on_null") else "" 859 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 860 861 start = expression.args.get("start") 862 start = f"START WITH {start}" if start else "" 863 increment = expression.args.get("increment") 864 increment = f" INCREMENT BY {increment}" if increment else "" 865 minvalue = expression.args.get("minvalue") 866 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 867 maxvalue = expression.args.get("maxvalue") 868 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 869 cycle = expression.args.get("cycle") 870 cycle_sql = "" 871 872 if cycle is not None: 873 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 874 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 875 876 sequence_opts = "" 877 if start or increment or cycle_sql: 878 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 879 sequence_opts = f" ({sequence_opts.strip()})" 880 881 expr = self.sql(expression, "expression") 882 expr = f"({expr})" if expr else "IDENTITY" 883 884 return f"GENERATED{this} AS {expr}{sequence_opts}" 885 886 def generatedasrowcolumnconstraint_sql( 887 self, expression: exp.GeneratedAsRowColumnConstraint 888 ) -> str: 889 start = "START" if expression.args.get("start") else "END" 890 hidden = " HIDDEN" if expression.args.get("hidden") else "" 891 return f"GENERATED ALWAYS AS ROW {start}{hidden}" 892 893 def periodforsystemtimeconstraint_sql( 894 self, expression: exp.PeriodForSystemTimeConstraint 895 ) -> str: 896 return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})" 897 898 def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str: 899 return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL" 900 901 def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str: 902 return f"AS {self.sql(expression, 'this')}" 903 904 def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str: 905 desc = expression.args.get("desc") 906 if desc is not None: 907 return f"PRIMARY KEY{' DESC' if desc else ' ASC'}" 908 return "PRIMARY KEY" 909 910 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 911 this = self.sql(expression, "this") 912 this = f" {this}" if this else "" 913 index_type = expression.args.get("index_type") 914 index_type = f" USING {index_type}" if index_type else "" 915 on_conflict = self.sql(expression, "on_conflict") 916 on_conflict = f" {on_conflict}" if on_conflict else "" 917 return f"UNIQUE{this}{index_type}{on_conflict}" 918 919 def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str: 920 return self.sql(expression, "this") 921 922 def create_sql(self, expression: exp.Create) -> str: 923 kind = self.sql(expression, "kind") 924 properties = expression.args.get("properties") 925 properties_locs = self.locate_properties(properties) if properties else defaultdict() 926 927 this = self.createable_sql(expression, properties_locs) 928 929 properties_sql = "" 930 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 931 exp.Properties.Location.POST_WITH 932 ): 933 properties_sql = self.sql( 934 exp.Properties( 935 expressions=[ 936 *properties_locs[exp.Properties.Location.POST_SCHEMA], 937 *properties_locs[exp.Properties.Location.POST_WITH], 938 ] 939 ) 940 ) 941 942 begin = " BEGIN" if expression.args.get("begin") else "" 943 end = " END" if expression.args.get("end") else "" 944 945 expression_sql = self.sql(expression, "expression") 946 if expression_sql: 947 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 948 949 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 950 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 951 postalias_props_sql = self.properties( 952 exp.Properties( 953 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 954 ), 955 wrapped=False, 956 ) 957 expression_sql = f" AS {postalias_props_sql}{expression_sql}" 958 else: 959 expression_sql = f" AS{expression_sql}" 960 961 postindex_props_sql = "" 962 if properties_locs.get(exp.Properties.Location.POST_INDEX): 963 postindex_props_sql = self.properties( 964 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 965 wrapped=False, 966 prefix=" ", 967 ) 968 969 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 970 indexes = f" {indexes}" if indexes else "" 971 index_sql = indexes + postindex_props_sql 972 973 replace = " OR REPLACE" if expression.args.get("replace") else "" 974 unique = " UNIQUE" if expression.args.get("unique") else "" 975 976 postcreate_props_sql = "" 977 if properties_locs.get(exp.Properties.Location.POST_CREATE): 978 postcreate_props_sql = self.properties( 979 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 980 sep=" ", 981 prefix=" ", 982 wrapped=False, 983 ) 984 985 modifiers = "".join((replace, unique, postcreate_props_sql)) 986 987 postexpression_props_sql = "" 988 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 989 postexpression_props_sql = self.properties( 990 exp.Properties( 991 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 992 ), 993 sep=" ", 994 prefix=" ", 995 wrapped=False, 996 ) 997 998 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 999 no_schema_binding = ( 1000 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 1001 ) 1002 1003 clone = self.sql(expression, "clone") 1004 clone = f" {clone}" if clone else "" 1005 1006 expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 1007 return self.prepend_ctes(expression, expression_sql) 1008 1009 def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str: 1010 start = self.sql(expression, "start") 1011 start = f"START WITH {start}" if start else "" 1012 increment = self.sql(expression, "increment") 1013 increment = f" INCREMENT BY {increment}" if increment else "" 1014 minvalue = self.sql(expression, "minvalue") 1015 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 1016 maxvalue = self.sql(expression, "maxvalue") 1017 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 1018 owned = self.sql(expression, "owned") 1019 owned = f" OWNED BY {owned}" if owned else "" 1020 1021 cache = expression.args.get("cache") 1022 if cache is None: 1023 cache_str = "" 1024 elif cache is True: 1025 cache_str = " CACHE" 1026 else: 1027 cache_str = f" CACHE {cache}" 1028 1029 options = self.expressions(expression, key="options", flat=True, sep=" ") 1030 options = f" {options}" if options else "" 1031 1032 return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip() 1033 1034 def clone_sql(self, expression: exp.Clone) -> str: 1035 this = self.sql(expression, "this") 1036 shallow = "SHALLOW " if expression.args.get("shallow") else "" 1037 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 1038 return f"{shallow}{keyword} {this}" 1039 1040 def describe_sql(self, expression: exp.Describe) -> str: 1041 style = expression.args.get("style") 1042 style = f" {style}" if style else "" 1043 return f"DESCRIBE{style} {self.sql(expression, 'this')}" 1044 1045 def heredoc_sql(self, expression: exp.Heredoc) -> str: 1046 tag = self.sql(expression, "tag") 1047 return f"${tag}${self.sql(expression, 'this')}${tag}$" 1048 1049 def prepend_ctes(self, expression: exp.Expression, sql: str) -> str: 1050 with_ = self.sql(expression, "with") 1051 if with_: 1052 sql = f"{with_}{self.sep()}{sql}" 1053 return sql 1054 1055 def with_sql(self, expression: exp.With) -> str: 1056 sql = self.expressions(expression, flat=True) 1057 recursive = ( 1058 "RECURSIVE " 1059 if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive") 1060 else "" 1061 ) 1062 1063 return f"WITH {recursive}{sql}" 1064 1065 def cte_sql(self, expression: exp.CTE) -> str: 1066 alias = self.sql(expression, "alias") 1067 1068 materialized = expression.args.get("materialized") 1069 if materialized is False: 1070 materialized = "NOT MATERIALIZED " 1071 elif materialized: 1072 materialized = "MATERIALIZED " 1073 1074 return f"{alias} AS {materialized or ''}{self.wrap(expression)}" 1075 1076 def tablealias_sql(self, expression: exp.TableAlias) -> str: 1077 alias = self.sql(expression, "this") 1078 columns = self.expressions(expression, key="columns", flat=True) 1079 columns = f"({columns})" if columns else "" 1080 1081 if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS: 1082 columns = "" 1083 self.unsupported("Named columns are not supported in table alias.") 1084 1085 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 1086 alias = "_t" 1087 1088 return f"{alias}{columns}" 1089 1090 def bitstring_sql(self, expression: exp.BitString) -> str: 1091 this = self.sql(expression, "this") 1092 if self.dialect.BIT_START: 1093 return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}" 1094 return f"{int(this, 2)}" 1095 1096 def hexstring_sql(self, expression: exp.HexString) -> str: 1097 this = self.sql(expression, "this") 1098 if self.dialect.HEX_START: 1099 return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}" 1100 return f"{int(this, 16)}" 1101 1102 def bytestring_sql(self, expression: exp.ByteString) -> str: 1103 this = self.sql(expression, "this") 1104 if self.dialect.BYTE_START: 1105 return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}" 1106 return this 1107 1108 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 1109 this = self.sql(expression, "this") 1110 escape = expression.args.get("escape") 1111 1112 if self.dialect.UNICODE_START: 1113 escape = f" UESCAPE {self.sql(escape)}" if escape else "" 1114 return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}" 1115 1116 if escape: 1117 pattern = re.compile(rf"{escape.name}(\d+)") 1118 else: 1119 pattern = ESCAPED_UNICODE_RE 1120 1121 this = pattern.sub(r"\\u\1", this) 1122 return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}" 1123 1124 def rawstring_sql(self, expression: exp.RawString) -> str: 1125 string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False) 1126 return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}" 1127 1128 def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str: 1129 this = self.sql(expression, "this") 1130 specifier = self.sql(expression, "expression") 1131 specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else "" 1132 return f"{this}{specifier}" 1133 1134 def datatype_sql(self, expression: exp.DataType) -> str: 1135 type_value = expression.this 1136 1137 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 1138 type_sql = self.sql(expression, "kind") 1139 else: 1140 type_sql = ( 1141 self.TYPE_MAPPING.get(type_value, type_value.value) 1142 if isinstance(type_value, exp.DataType.Type) 1143 else type_value 1144 ) 1145 1146 nested = "" 1147 interior = self.expressions(expression, flat=True) 1148 values = "" 1149 1150 if interior: 1151 if expression.args.get("nested"): 1152 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 1153 if expression.args.get("values") is not None: 1154 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 1155 values = self.expressions(expression, key="values", flat=True) 1156 values = f"{delimiters[0]}{values}{delimiters[1]}" 1157 elif type_value == exp.DataType.Type.INTERVAL: 1158 nested = f" {interior}" 1159 else: 1160 nested = f"({interior})" 1161 1162 type_sql = f"{type_sql}{nested}{values}" 1163 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 1164 exp.DataType.Type.TIMETZ, 1165 exp.DataType.Type.TIMESTAMPTZ, 1166 ): 1167 type_sql = f"{type_sql} WITH TIME ZONE" 1168 1169 return type_sql 1170 1171 def directory_sql(self, expression: exp.Directory) -> str: 1172 local = "LOCAL " if expression.args.get("local") else "" 1173 row_format = self.sql(expression, "row_format") 1174 row_format = f" {row_format}" if row_format else "" 1175 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}" 1176 1177 def delete_sql(self, expression: exp.Delete) -> str: 1178 this = self.sql(expression, "this") 1179 this = f" FROM {this}" if this else "" 1180 using = self.sql(expression, "using") 1181 using = f" USING {using}" if using else "" 1182 where = self.sql(expression, "where") 1183 returning = self.sql(expression, "returning") 1184 limit = self.sql(expression, "limit") 1185 tables = self.expressions(expression, key="tables") 1186 tables = f" {tables}" if tables else "" 1187 if self.RETURNING_END: 1188 expression_sql = f"{this}{using}{where}{returning}{limit}" 1189 else: 1190 expression_sql = f"{returning}{this}{using}{where}{limit}" 1191 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}") 1192 1193 def drop_sql(self, expression: exp.Drop) -> str: 1194 this = self.sql(expression, "this") 1195 expressions = self.expressions(expression, flat=True) 1196 expressions = f" ({expressions})" if expressions else "" 1197 kind = expression.args["kind"] 1198 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1199 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1200 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1201 cascade = " CASCADE" if expression.args.get("cascade") else "" 1202 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1203 purge = " PURGE" if expression.args.get("purge") else "" 1204 return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{expressions}{cascade}{constraints}{purge}" 1205 1206 def except_sql(self, expression: exp.Except) -> str: 1207 return self.set_operations(expression) 1208 1209 def except_op(self, expression: exp.Except) -> str: 1210 return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}" 1211 1212 def fetch_sql(self, expression: exp.Fetch) -> str: 1213 direction = expression.args.get("direction") 1214 direction = f" {direction}" if direction else "" 1215 count = expression.args.get("count") 1216 count = f" {count}" if count else "" 1217 if expression.args.get("percent"): 1218 count = f"{count} PERCENT" 1219 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1220 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}" 1221 1222 def filter_sql(self, expression: exp.Filter) -> str: 1223 if self.AGGREGATE_FILTER_SUPPORTED: 1224 this = self.sql(expression, "this") 1225 where = self.sql(expression, "expression").strip() 1226 return f"{this} FILTER({where})" 1227 1228 agg = expression.this 1229 agg_arg = agg.this 1230 cond = expression.expression.this 1231 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1232 return self.sql(agg) 1233 1234 def hint_sql(self, expression: exp.Hint) -> str: 1235 if not self.QUERY_HINTS: 1236 self.unsupported("Hints are not supported") 1237 return "" 1238 1239 return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */" 1240 1241 def indexparameters_sql(self, expression: exp.IndexParameters) -> str: 1242 using = self.sql(expression, "using") 1243 using = f" USING {using}" if using else "" 1244 columns = self.expressions(expression, key="columns", flat=True) 1245 columns = f"({columns})" if columns else "" 1246 partition_by = self.expressions(expression, key="partition_by", flat=True) 1247 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1248 where = self.sql(expression, "where") 1249 include = self.expressions(expression, key="include", flat=True) 1250 if include: 1251 include = f" INCLUDE ({include})" 1252 with_storage = self.expressions(expression, key="with_storage", flat=True) 1253 with_storage = f" WITH ({with_storage})" if with_storage else "" 1254 tablespace = self.sql(expression, "tablespace") 1255 tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else "" 1256 1257 return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}" 1258 1259 def index_sql(self, expression: exp.Index) -> str: 1260 unique = "UNIQUE " if expression.args.get("unique") else "" 1261 primary = "PRIMARY " if expression.args.get("primary") else "" 1262 amp = "AMP " if expression.args.get("amp") else "" 1263 name = self.sql(expression, "this") 1264 name = f"{name} " if name else "" 1265 table = self.sql(expression, "table") 1266 table = f"{self.INDEX_ON} {table}" if table else "" 1267 1268 index = "INDEX " if not table else "" 1269 1270 params = self.sql(expression, "params") 1271 return f"{unique}{primary}{amp}{index}{name}{table}{params}" 1272 1273 def identifier_sql(self, expression: exp.Identifier) -> str: 1274 text = expression.name 1275 lower = text.lower() 1276 text = lower if self.normalize and not expression.quoted else text 1277 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1278 if ( 1279 expression.quoted 1280 or self.dialect.can_identify(text, self.identify) 1281 or lower in self.RESERVED_KEYWORDS 1282 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1283 ): 1284 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1285 return text 1286 1287 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1288 input_format = self.sql(expression, "input_format") 1289 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1290 output_format = self.sql(expression, "output_format") 1291 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1292 return self.sep().join((input_format, output_format)) 1293 1294 def national_sql(self, expression: exp.National, prefix: str = "N") -> str: 1295 string = self.sql(exp.Literal.string(expression.name)) 1296 return f"{prefix}{string}" 1297 1298 def partition_sql(self, expression: exp.Partition) -> str: 1299 return f"PARTITION({self.expressions(expression, flat=True)})" 1300 1301 def properties_sql(self, expression: exp.Properties) -> str: 1302 root_properties = [] 1303 with_properties = [] 1304 1305 for p in expression.expressions: 1306 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1307 if p_loc == exp.Properties.Location.POST_WITH: 1308 with_properties.append(p) 1309 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1310 root_properties.append(p) 1311 1312 return self.root_properties( 1313 exp.Properties(expressions=root_properties) 1314 ) + self.with_properties(exp.Properties(expressions=with_properties)) 1315 1316 def root_properties(self, properties: exp.Properties) -> str: 1317 if properties.expressions: 1318 return self.sep() + self.expressions(properties, indent=False, sep=" ") 1319 return "" 1320 1321 def properties( 1322 self, 1323 properties: exp.Properties, 1324 prefix: str = "", 1325 sep: str = ", ", 1326 suffix: str = "", 1327 wrapped: bool = True, 1328 ) -> str: 1329 if properties.expressions: 1330 expressions = self.expressions(properties, sep=sep, indent=False) 1331 if expressions: 1332 expressions = self.wrap(expressions) if wrapped else expressions 1333 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1334 return "" 1335 1336 def with_properties(self, properties: exp.Properties) -> str: 1337 return self.properties(properties, prefix=self.seg("WITH")) 1338 1339 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1340 properties_locs = defaultdict(list) 1341 for p in properties.expressions: 1342 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1343 if p_loc != exp.Properties.Location.UNSUPPORTED: 1344 properties_locs[p_loc].append(p) 1345 else: 1346 self.unsupported(f"Unsupported property {p.key}") 1347 1348 return properties_locs 1349 1350 def property_name(self, expression: exp.Property, string_key: bool = False) -> str: 1351 if isinstance(expression.this, exp.Dot): 1352 return self.sql(expression, "this") 1353 return f"'{expression.name}'" if string_key else expression.name 1354 1355 def property_sql(self, expression: exp.Property) -> str: 1356 property_cls = expression.__class__ 1357 if property_cls == exp.Property: 1358 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1359 1360 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1361 if not property_name: 1362 self.unsupported(f"Unsupported property {expression.key}") 1363 1364 return f"{property_name}={self.sql(expression, 'this')}" 1365 1366 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1367 if self.SUPPORTS_CREATE_TABLE_LIKE: 1368 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1369 options = f" {options}" if options else "" 1370 1371 like = f"LIKE {self.sql(expression, 'this')}{options}" 1372 if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema): 1373 like = f"({like})" 1374 1375 return like 1376 1377 if expression.expressions: 1378 self.unsupported("Transpilation of LIKE property options is unsupported") 1379 1380 select = exp.select("*").from_(expression.this).limit(0) 1381 return f"AS {self.sql(select)}" 1382 1383 def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str: 1384 no = "NO " if expression.args.get("no") else "" 1385 protection = " PROTECTION" if expression.args.get("protection") else "" 1386 return f"{no}FALLBACK{protection}" 1387 1388 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1389 no = "NO " if expression.args.get("no") else "" 1390 local = expression.args.get("local") 1391 local = f"{local} " if local else "" 1392 dual = "DUAL " if expression.args.get("dual") else "" 1393 before = "BEFORE " if expression.args.get("before") else "" 1394 after = "AFTER " if expression.args.get("after") else "" 1395 return f"{no}{local}{dual}{before}{after}JOURNAL" 1396 1397 def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str: 1398 freespace = self.sql(expression, "this") 1399 percent = " PERCENT" if expression.args.get("percent") else "" 1400 return f"FREESPACE={freespace}{percent}" 1401 1402 def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str: 1403 if expression.args.get("default"): 1404 property = "DEFAULT" 1405 elif expression.args.get("on"): 1406 property = "ON" 1407 else: 1408 property = "OFF" 1409 return f"CHECKSUM={property}" 1410 1411 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1412 if expression.args.get("no"): 1413 return "NO MERGEBLOCKRATIO" 1414 if expression.args.get("default"): 1415 return "DEFAULT MERGEBLOCKRATIO" 1416 1417 percent = " PERCENT" if expression.args.get("percent") else "" 1418 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}" 1419 1420 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1421 default = expression.args.get("default") 1422 minimum = expression.args.get("minimum") 1423 maximum = expression.args.get("maximum") 1424 if default or minimum or maximum: 1425 if default: 1426 prop = "DEFAULT" 1427 elif minimum: 1428 prop = "MINIMUM" 1429 else: 1430 prop = "MAXIMUM" 1431 return f"{prop} DATABLOCKSIZE" 1432 units = expression.args.get("units") 1433 units = f" {units}" if units else "" 1434 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}" 1435 1436 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1437 autotemp = expression.args.get("autotemp") 1438 always = expression.args.get("always") 1439 default = expression.args.get("default") 1440 manual = expression.args.get("manual") 1441 never = expression.args.get("never") 1442 1443 if autotemp is not None: 1444 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1445 elif always: 1446 prop = "ALWAYS" 1447 elif default: 1448 prop = "DEFAULT" 1449 elif manual: 1450 prop = "MANUAL" 1451 elif never: 1452 prop = "NEVER" 1453 return f"BLOCKCOMPRESSION={prop}" 1454 1455 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1456 no = expression.args.get("no") 1457 no = " NO" if no else "" 1458 concurrent = expression.args.get("concurrent") 1459 concurrent = " CONCURRENT" if concurrent else "" 1460 target = self.sql(expression, "target") 1461 target = f" {target}" if target else "" 1462 return f"WITH{no}{concurrent} ISOLATED LOADING{target}" 1463 1464 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1465 if isinstance(expression.this, list): 1466 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1467 if expression.this: 1468 modulus = self.sql(expression, "this") 1469 remainder = self.sql(expression, "expression") 1470 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1471 1472 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1473 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1474 return f"FROM ({from_expressions}) TO ({to_expressions})" 1475 1476 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1477 this = self.sql(expression, "this") 1478 1479 for_values_or_default = expression.expression 1480 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1481 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1482 else: 1483 for_values_or_default = " DEFAULT" 1484 1485 return f"PARTITION OF {this}{for_values_or_default}" 1486 1487 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1488 kind = expression.args.get("kind") 1489 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1490 for_or_in = expression.args.get("for_or_in") 1491 for_or_in = f" {for_or_in}" if for_or_in else "" 1492 lock_type = expression.args.get("lock_type") 1493 override = " OVERRIDE" if expression.args.get("override") else "" 1494 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}" 1495 1496 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1497 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1498 statistics = expression.args.get("statistics") 1499 statistics_sql = "" 1500 if statistics is not None: 1501 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1502 return f"{data_sql}{statistics_sql}" 1503 1504 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1505 sql = "WITH(SYSTEM_VERSIONING=ON" 1506 1507 if expression.this: 1508 history_table = self.sql(expression, "this") 1509 sql = f"{sql}(HISTORY_TABLE={history_table}" 1510 1511 if expression.expression: 1512 data_consistency_check = self.sql(expression, "expression") 1513 sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}" 1514 1515 sql = f"{sql})" 1516 1517 return f"{sql})" 1518 1519 def insert_sql(self, expression: exp.Insert) -> str: 1520 hint = self.sql(expression, "hint") 1521 overwrite = expression.args.get("overwrite") 1522 1523 if isinstance(expression.this, exp.Directory): 1524 this = " OVERWRITE" if overwrite else " INTO" 1525 else: 1526 this = self.INSERT_OVERWRITE if overwrite else " INTO" 1527 1528 stored = self.sql(expression, "stored") 1529 stored = f" {stored}" if stored else "" 1530 alternative = expression.args.get("alternative") 1531 alternative = f" OR {alternative}" if alternative else "" 1532 ignore = " IGNORE" if expression.args.get("ignore") else "" 1533 is_function = expression.args.get("is_function") 1534 if is_function: 1535 this = f"{this} FUNCTION" 1536 this = f"{this} {self.sql(expression, 'this')}" 1537 1538 exists = " IF EXISTS" if expression.args.get("exists") else "" 1539 where = self.sql(expression, "where") 1540 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1541 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1542 on_conflict = self.sql(expression, "conflict") 1543 on_conflict = f" {on_conflict}" if on_conflict else "" 1544 by_name = " BY NAME" if expression.args.get("by_name") else "" 1545 returning = self.sql(expression, "returning") 1546 1547 if self.RETURNING_END: 1548 expression_sql = f"{expression_sql}{on_conflict}{returning}" 1549 else: 1550 expression_sql = f"{returning}{expression_sql}{on_conflict}" 1551 1552 sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{where}{expression_sql}" 1553 return self.prepend_ctes(expression, sql) 1554 1555 def intersect_sql(self, expression: exp.Intersect) -> str: 1556 return self.set_operations(expression) 1557 1558 def intersect_op(self, expression: exp.Intersect) -> str: 1559 return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}" 1560 1561 def introducer_sql(self, expression: exp.Introducer) -> str: 1562 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 1563 1564 def kill_sql(self, expression: exp.Kill) -> str: 1565 kind = self.sql(expression, "kind") 1566 kind = f" {kind}" if kind else "" 1567 this = self.sql(expression, "this") 1568 this = f" {this}" if this else "" 1569 return f"KILL{kind}{this}" 1570 1571 def pseudotype_sql(self, expression: exp.PseudoType) -> str: 1572 return expression.name 1573 1574 def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str: 1575 return expression.name 1576 1577 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1578 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1579 1580 constraint = self.sql(expression, "constraint") 1581 constraint = f" ON CONSTRAINT {constraint}" if constraint else "" 1582 1583 conflict_keys = self.expressions(expression, key="conflict_keys", flat=True) 1584 conflict_keys = f"({conflict_keys}) " if conflict_keys else " " 1585 action = self.sql(expression, "action") 1586 1587 expressions = self.expressions(expression, flat=True) 1588 if expressions: 1589 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1590 expressions = f" {set_keyword}{expressions}" 1591 1592 return f"{conflict}{constraint}{conflict_keys}{action}{expressions}" 1593 1594 def returning_sql(self, expression: exp.Returning) -> str: 1595 return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}" 1596 1597 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1598 fields = self.sql(expression, "fields") 1599 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1600 escaped = self.sql(expression, "escaped") 1601 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1602 items = self.sql(expression, "collection_items") 1603 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1604 keys = self.sql(expression, "map_keys") 1605 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1606 lines = self.sql(expression, "lines") 1607 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1608 null = self.sql(expression, "null") 1609 null = f" NULL DEFINED AS {null}" if null else "" 1610 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}" 1611 1612 def withtablehint_sql(self, expression: exp.WithTableHint) -> str: 1613 return f"WITH ({self.expressions(expression, flat=True)})" 1614 1615 def indextablehint_sql(self, expression: exp.IndexTableHint) -> str: 1616 this = f"{self.sql(expression, 'this')} INDEX" 1617 target = self.sql(expression, "target") 1618 target = f" FOR {target}" if target else "" 1619 return f"{this}{target} ({self.expressions(expression, flat=True)})" 1620 1621 def historicaldata_sql(self, expression: exp.HistoricalData) -> str: 1622 this = self.sql(expression, "this") 1623 kind = self.sql(expression, "kind") 1624 expr = self.sql(expression, "expression") 1625 return f"{this} ({kind} => {expr})" 1626 1627 def table_parts(self, expression: exp.Table) -> str: 1628 return ".".join( 1629 self.sql(part) 1630 for part in ( 1631 expression.args.get("catalog"), 1632 expression.args.get("db"), 1633 expression.args.get("this"), 1634 ) 1635 if part is not None 1636 ) 1637 1638 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1639 table = self.table_parts(expression) 1640 only = "ONLY " if expression.args.get("only") else "" 1641 partition = self.sql(expression, "partition") 1642 partition = f" {partition}" if partition else "" 1643 version = self.sql(expression, "version") 1644 version = f" {version}" if version else "" 1645 alias = self.sql(expression, "alias") 1646 alias = f"{sep}{alias}" if alias else "" 1647 hints = self.expressions(expression, key="hints", sep=" ") 1648 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1649 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 1650 pivots = f" {pivots}" if pivots else "" 1651 joins = self.indent( 1652 self.expressions(expression, key="joins", sep="", flat=True), skip_first=True 1653 ) 1654 laterals = self.expressions(expression, key="laterals", sep="") 1655 1656 file_format = self.sql(expression, "format") 1657 if file_format: 1658 pattern = self.sql(expression, "pattern") 1659 pattern = f", PATTERN => {pattern}" if pattern else "" 1660 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1661 1662 ordinality = expression.args.get("ordinality") or "" 1663 if ordinality: 1664 ordinality = f" WITH ORDINALITY{alias}" 1665 alias = "" 1666 1667 when = self.sql(expression, "when") 1668 if when: 1669 table = f"{table} {when}" 1670 1671 return f"{only}{table}{partition}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}" 1672 1673 def tablesample_sql( 1674 self, 1675 expression: exp.TableSample, 1676 sep: str = " AS ", 1677 tablesample_keyword: t.Optional[str] = None, 1678 ) -> str: 1679 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1680 table = expression.this.copy() 1681 table.set("alias", None) 1682 this = self.sql(table) 1683 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1684 else: 1685 this = self.sql(expression, "this") 1686 alias = "" 1687 1688 method = self.sql(expression, "method") 1689 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1690 numerator = self.sql(expression, "bucket_numerator") 1691 denominator = self.sql(expression, "bucket_denominator") 1692 field = self.sql(expression, "bucket_field") 1693 field = f" ON {field}" if field else "" 1694 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1695 seed = self.sql(expression, "seed") 1696 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1697 1698 size = self.sql(expression, "size") 1699 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1700 size = f"{size} ROWS" 1701 1702 percent = self.sql(expression, "percent") 1703 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1704 percent = f"{percent} PERCENT" 1705 1706 expr = f"{bucket}{percent}{size}" 1707 if self.TABLESAMPLE_REQUIRES_PARENS: 1708 expr = f"({expr})" 1709 1710 return ( 1711 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1712 ) 1713 1714 def pivot_sql(self, expression: exp.Pivot) -> str: 1715 expressions = self.expressions(expression, flat=True) 1716 1717 if expression.this: 1718 this = self.sql(expression, "this") 1719 if not expressions: 1720 return f"UNPIVOT {this}" 1721 1722 on = f"{self.seg('ON')} {expressions}" 1723 using = self.expressions(expression, key="using", flat=True) 1724 using = f"{self.seg('USING')} {using}" if using else "" 1725 group = self.sql(expression, "group") 1726 return f"PIVOT {this}{on}{using}{group}" 1727 1728 alias = self.sql(expression, "alias") 1729 alias = f" AS {alias}" if alias else "" 1730 direction = "UNPIVOT" if expression.unpivot else "PIVOT" 1731 field = self.sql(expression, "field") 1732 include_nulls = expression.args.get("include_nulls") 1733 if include_nulls is not None: 1734 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1735 else: 1736 nulls = "" 1737 return f"{direction}{nulls}({expressions} FOR {field}){alias}" 1738 1739 def version_sql(self, expression: exp.Version) -> str: 1740 this = f"FOR {expression.name}" 1741 kind = expression.text("kind") 1742 expr = self.sql(expression, "expression") 1743 return f"{this} {kind} {expr}" 1744 1745 def tuple_sql(self, expression: exp.Tuple) -> str: 1746 return f"({self.expressions(expression, flat=True)})" 1747 1748 def update_sql(self, expression: exp.Update) -> str: 1749 this = self.sql(expression, "this") 1750 set_sql = self.expressions(expression, flat=True) 1751 from_sql = self.sql(expression, "from") 1752 where_sql = self.sql(expression, "where") 1753 returning = self.sql(expression, "returning") 1754 order = self.sql(expression, "order") 1755 limit = self.sql(expression, "limit") 1756 if self.RETURNING_END: 1757 expression_sql = f"{from_sql}{where_sql}{returning}" 1758 else: 1759 expression_sql = f"{returning}{from_sql}{where_sql}" 1760 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1761 return self.prepend_ctes(expression, sql) 1762 1763 def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str: 1764 values_as_table = values_as_table and self.VALUES_AS_TABLE 1765 1766 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1767 if values_as_table or not expression.find_ancestor(exp.From, exp.Join): 1768 args = self.expressions(expression) 1769 alias = self.sql(expression, "alias") 1770 values = f"VALUES{self.seg('')}{args}" 1771 values = ( 1772 f"({values})" 1773 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1774 else values 1775 ) 1776 return f"{values} AS {alias}" if alias else values 1777 1778 # Converts `VALUES...` expression into a series of select unions. 1779 alias_node = expression.args.get("alias") 1780 column_names = alias_node and alias_node.columns 1781 1782 selects: t.List[exp.Query] = [] 1783 1784 for i, tup in enumerate(expression.expressions): 1785 row = tup.expressions 1786 1787 if i == 0 and column_names: 1788 row = [ 1789 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1790 ] 1791 1792 selects.append(exp.Select(expressions=row)) 1793 1794 if self.pretty: 1795 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1796 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1797 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1798 query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1799 return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False)) 1800 1801 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1802 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1803 return f"({unions}){alias}" 1804 1805 def var_sql(self, expression: exp.Var) -> str: 1806 return self.sql(expression, "this") 1807 1808 def into_sql(self, expression: exp.Into) -> str: 1809 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1810 unlogged = " UNLOGGED" if expression.args.get("unlogged") else "" 1811 return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}" 1812 1813 def from_sql(self, expression: exp.From) -> str: 1814 return f"{self.seg('FROM')} {self.sql(expression, 'this')}" 1815 1816 def group_sql(self, expression: exp.Group) -> str: 1817 group_by_all = expression.args.get("all") 1818 if group_by_all is True: 1819 modifier = " ALL" 1820 elif group_by_all is False: 1821 modifier = " DISTINCT" 1822 else: 1823 modifier = "" 1824 1825 group_by = self.op_expressions(f"GROUP BY{modifier}", expression) 1826 1827 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1828 grouping_sets = ( 1829 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1830 ) 1831 1832 cube = expression.args.get("cube", []) 1833 if seq_get(cube, 0) is True: 1834 return f"{group_by}{self.seg('WITH CUBE')}" 1835 else: 1836 cube_sql = self.expressions(expression, key="cube", indent=False) 1837 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1838 1839 rollup = expression.args.get("rollup", []) 1840 if seq_get(rollup, 0) is True: 1841 return f"{group_by}{self.seg('WITH ROLLUP')}" 1842 else: 1843 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1844 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1845 1846 groupings = csv( 1847 grouping_sets, 1848 cube_sql, 1849 rollup_sql, 1850 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1851 sep=self.GROUPINGS_SEP, 1852 ) 1853 1854 if expression.args.get("expressions") and groupings: 1855 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1856 1857 return f"{group_by}{groupings}" 1858 1859 def having_sql(self, expression: exp.Having) -> str: 1860 this = self.indent(self.sql(expression, "this")) 1861 return f"{self.seg('HAVING')}{self.sep()}{this}" 1862 1863 def connect_sql(self, expression: exp.Connect) -> str: 1864 start = self.sql(expression, "start") 1865 start = self.seg(f"START WITH {start}") if start else "" 1866 nocycle = " NOCYCLE" if expression.args.get("nocycle") else "" 1867 connect = self.sql(expression, "connect") 1868 connect = self.seg(f"CONNECT BY{nocycle} {connect}") 1869 return start + connect 1870 1871 def prior_sql(self, expression: exp.Prior) -> str: 1872 return f"PRIOR {self.sql(expression, 'this')}" 1873 1874 def join_sql(self, expression: exp.Join) -> str: 1875 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1876 side = None 1877 else: 1878 side = expression.side 1879 1880 op_sql = " ".join( 1881 op 1882 for op in ( 1883 expression.method, 1884 "GLOBAL" if expression.args.get("global") else None, 1885 side, 1886 expression.kind, 1887 expression.hint if self.JOIN_HINTS else None, 1888 ) 1889 if op 1890 ) 1891 match_cond = self.sql(expression, "match_condition") 1892 match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else "" 1893 on_sql = self.sql(expression, "on") 1894 using = expression.args.get("using") 1895 1896 if not on_sql and using: 1897 on_sql = csv(*(self.sql(column) for column in using)) 1898 1899 this = expression.this 1900 this_sql = self.sql(this) 1901 1902 if on_sql: 1903 on_sql = self.indent(on_sql, skip_first=True) 1904 space = self.seg(" " * self.pad) if self.pretty else " " 1905 if using: 1906 on_sql = f"{space}USING ({on_sql})" 1907 else: 1908 on_sql = f"{space}ON {on_sql}" 1909 elif not op_sql: 1910 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 1911 return f" {this_sql}" 1912 1913 return f", {this_sql}" 1914 1915 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1916 return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}" 1917 1918 def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str: 1919 args = self.expressions(expression, flat=True) 1920 args = f"({args})" if len(args.split(",")) > 1 else args 1921 return f"{args} {arrow_sep} {self.sql(expression, 'this')}" 1922 1923 def lateral_op(self, expression: exp.Lateral) -> str: 1924 cross_apply = expression.args.get("cross_apply") 1925 1926 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 1927 if cross_apply is True: 1928 op = "INNER JOIN " 1929 elif cross_apply is False: 1930 op = "LEFT JOIN " 1931 else: 1932 op = "" 1933 1934 return f"{op}LATERAL" 1935 1936 def lateral_sql(self, expression: exp.Lateral) -> str: 1937 this = self.sql(expression, "this") 1938 1939 if expression.args.get("view"): 1940 alias = expression.args["alias"] 1941 columns = self.expressions(alias, key="columns", flat=True) 1942 table = f" {alias.name}" if alias.name else "" 1943 columns = f" AS {columns}" if columns else "" 1944 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1945 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1946 1947 alias = self.sql(expression, "alias") 1948 alias = f" AS {alias}" if alias else "" 1949 return f"{self.lateral_op(expression)} {this}{alias}" 1950 1951 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1952 this = self.sql(expression, "this") 1953 1954 args = [ 1955 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1956 for e in (expression.args.get(k) for k in ("offset", "expression")) 1957 if e 1958 ] 1959 1960 args_sql = ", ".join(self.sql(e) for e in args) 1961 args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql 1962 expressions = self.expressions(expression, flat=True) 1963 expressions = f" BY {expressions}" if expressions else "" 1964 1965 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}" 1966 1967 def offset_sql(self, expression: exp.Offset) -> str: 1968 this = self.sql(expression, "this") 1969 value = expression.expression 1970 value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value 1971 expressions = self.expressions(expression, flat=True) 1972 expressions = f" BY {expressions}" if expressions else "" 1973 return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}" 1974 1975 def setitem_sql(self, expression: exp.SetItem) -> str: 1976 kind = self.sql(expression, "kind") 1977 kind = f"{kind} " if kind else "" 1978 this = self.sql(expression, "this") 1979 expressions = self.expressions(expression) 1980 collate = self.sql(expression, "collate") 1981 collate = f" COLLATE {collate}" if collate else "" 1982 global_ = "GLOBAL " if expression.args.get("global") else "" 1983 return f"{global_}{kind}{this}{expressions}{collate}" 1984 1985 def set_sql(self, expression: exp.Set) -> str: 1986 expressions = ( 1987 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 1988 ) 1989 tag = " TAG" if expression.args.get("tag") else "" 1990 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}" 1991 1992 def pragma_sql(self, expression: exp.Pragma) -> str: 1993 return f"PRAGMA {self.sql(expression, 'this')}" 1994 1995 def lock_sql(self, expression: exp.Lock) -> str: 1996 if not self.LOCKING_READS_SUPPORTED: 1997 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 1998 return "" 1999 2000 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 2001 expressions = self.expressions(expression, flat=True) 2002 expressions = f" OF {expressions}" if expressions else "" 2003 wait = expression.args.get("wait") 2004 2005 if wait is not None: 2006 if isinstance(wait, exp.Literal): 2007 wait = f" WAIT {self.sql(wait)}" 2008 else: 2009 wait = " NOWAIT" if wait else " SKIP LOCKED" 2010 2011 return f"{lock_type}{expressions}{wait or ''}" 2012 2013 def literal_sql(self, expression: exp.Literal) -> str: 2014 text = expression.this or "" 2015 if expression.is_string: 2016 text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}" 2017 return text 2018 2019 def escape_str(self, text: str, escape_backslash: bool = True) -> str: 2020 if self.dialect.ESCAPED_SEQUENCES: 2021 to_escaped = self.dialect.ESCAPED_SEQUENCES 2022 text = "".join( 2023 to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text 2024 ) 2025 2026 return self._replace_line_breaks(text).replace( 2027 self.dialect.QUOTE_END, self._escaped_quote_end 2028 ) 2029 2030 def loaddata_sql(self, expression: exp.LoadData) -> str: 2031 local = " LOCAL" if expression.args.get("local") else "" 2032 inpath = f" INPATH {self.sql(expression, 'inpath')}" 2033 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 2034 this = f" INTO TABLE {self.sql(expression, 'this')}" 2035 partition = self.sql(expression, "partition") 2036 partition = f" {partition}" if partition else "" 2037 input_format = self.sql(expression, "input_format") 2038 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 2039 serde = self.sql(expression, "serde") 2040 serde = f" SERDE {serde}" if serde else "" 2041 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}" 2042 2043 def null_sql(self, *_) -> str: 2044 return "NULL" 2045 2046 def boolean_sql(self, expression: exp.Boolean) -> str: 2047 return "TRUE" if expression.this else "FALSE" 2048 2049 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 2050 this = self.sql(expression, "this") 2051 this = f"{this} " if this else this 2052 siblings = "SIBLINGS " if expression.args.get("siblings") else "" 2053 order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore 2054 interpolated_values = [ 2055 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 2056 for named_expression in expression.args.get("interpolate") or [] 2057 ] 2058 interpolate = ( 2059 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 2060 ) 2061 return f"{order}{interpolate}" 2062 2063 def withfill_sql(self, expression: exp.WithFill) -> str: 2064 from_sql = self.sql(expression, "from") 2065 from_sql = f" FROM {from_sql}" if from_sql else "" 2066 to_sql = self.sql(expression, "to") 2067 to_sql = f" TO {to_sql}" if to_sql else "" 2068 step_sql = self.sql(expression, "step") 2069 step_sql = f" STEP {step_sql}" if step_sql else "" 2070 return f"WITH FILL{from_sql}{to_sql}{step_sql}" 2071 2072 def cluster_sql(self, expression: exp.Cluster) -> str: 2073 return self.op_expressions("CLUSTER BY", expression) 2074 2075 def distribute_sql(self, expression: exp.Distribute) -> str: 2076 return self.op_expressions("DISTRIBUTE BY", expression) 2077 2078 def sort_sql(self, expression: exp.Sort) -> str: 2079 return self.op_expressions("SORT BY", expression) 2080 2081 def ordered_sql(self, expression: exp.Ordered) -> str: 2082 desc = expression.args.get("desc") 2083 asc = not desc 2084 2085 nulls_first = expression.args.get("nulls_first") 2086 nulls_last = not nulls_first 2087 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 2088 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 2089 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 2090 2091 this = self.sql(expression, "this") 2092 2093 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 2094 nulls_sort_change = "" 2095 if nulls_first and ( 2096 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 2097 ): 2098 nulls_sort_change = " NULLS FIRST" 2099 elif ( 2100 nulls_last 2101 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 2102 and not nulls_are_last 2103 ): 2104 nulls_sort_change = " NULLS LAST" 2105 2106 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 2107 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 2108 window = expression.find_ancestor(exp.Window, exp.Select) 2109 if isinstance(window, exp.Window) and window.args.get("spec"): 2110 self.unsupported( 2111 f"'{nulls_sort_change.strip()}' translation not supported in window functions" 2112 ) 2113 nulls_sort_change = "" 2114 elif self.NULL_ORDERING_SUPPORTED is None: 2115 if expression.this.is_int: 2116 self.unsupported( 2117 f"'{nulls_sort_change.strip()}' translation not supported with positional ordering" 2118 ) 2119 elif not isinstance(expression.this, exp.Rand): 2120 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 2121 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 2122 nulls_sort_change = "" 2123 2124 with_fill = self.sql(expression, "with_fill") 2125 with_fill = f" {with_fill}" if with_fill else "" 2126 2127 return f"{this}{sort_order}{nulls_sort_change}{with_fill}" 2128 2129 def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str: 2130 window_frame = self.sql(expression, "window_frame") 2131 window_frame = f"{window_frame} " if window_frame else "" 2132 2133 this = self.sql(expression, "this") 2134 2135 return f"{window_frame}{this}" 2136 2137 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 2138 partition = self.partition_by_sql(expression) 2139 order = self.sql(expression, "order") 2140 measures = self.expressions(expression, key="measures") 2141 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 2142 rows = self.sql(expression, "rows") 2143 rows = self.seg(rows) if rows else "" 2144 after = self.sql(expression, "after") 2145 after = self.seg(after) if after else "" 2146 pattern = self.sql(expression, "pattern") 2147 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 2148 definition_sqls = [ 2149 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 2150 for definition in expression.args.get("define", []) 2151 ] 2152 definitions = self.expressions(sqls=definition_sqls) 2153 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 2154 body = "".join( 2155 ( 2156 partition, 2157 order, 2158 measures, 2159 rows, 2160 after, 2161 pattern, 2162 define, 2163 ) 2164 ) 2165 alias = self.sql(expression, "alias") 2166 alias = f" {alias}" if alias else "" 2167 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}" 2168 2169 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 2170 limit = expression.args.get("limit") 2171 2172 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 2173 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 2174 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 2175 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 2176 2177 options = self.expressions(expression, key="options") 2178 if options: 2179 options = f" OPTION{self.wrap(options)}" 2180 2181 return csv( 2182 *sqls, 2183 *[self.sql(join) for join in expression.args.get("joins") or []], 2184 self.sql(expression, "connect"), 2185 self.sql(expression, "match"), 2186 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 2187 self.sql(expression, "prewhere"), 2188 self.sql(expression, "where"), 2189 self.sql(expression, "group"), 2190 self.sql(expression, "having"), 2191 *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()], 2192 self.sql(expression, "order"), 2193 *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit), 2194 *self.after_limit_modifiers(expression), 2195 options, 2196 sep="", 2197 ) 2198 2199 def queryoption_sql(self, expression: exp.QueryOption) -> str: 2200 return "" 2201 2202 def offset_limit_modifiers( 2203 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 2204 ) -> t.List[str]: 2205 return [ 2206 self.sql(expression, "offset") if fetch else self.sql(limit), 2207 self.sql(limit) if fetch else self.sql(expression, "offset"), 2208 ] 2209 2210 def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]: 2211 locks = self.expressions(expression, key="locks", sep=" ") 2212 locks = f" {locks}" if locks else "" 2213 return [locks, self.sql(expression, "sample")] 2214 2215 def select_sql(self, expression: exp.Select) -> str: 2216 into = expression.args.get("into") 2217 if not self.SUPPORTS_SELECT_INTO and into: 2218 into.pop() 2219 2220 hint = self.sql(expression, "hint") 2221 distinct = self.sql(expression, "distinct") 2222 distinct = f" {distinct}" if distinct else "" 2223 kind = self.sql(expression, "kind") 2224 2225 limit = expression.args.get("limit") 2226 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP: 2227 top = self.limit_sql(limit, top=True) 2228 limit.pop() 2229 else: 2230 top = "" 2231 2232 expressions = self.expressions(expression) 2233 2234 if kind: 2235 if kind in self.SELECT_KINDS: 2236 kind = f" AS {kind}" 2237 else: 2238 if kind == "STRUCT": 2239 expressions = self.expressions( 2240 sqls=[ 2241 self.sql( 2242 exp.Struct( 2243 expressions=[ 2244 exp.PropertyEQ(this=e.args.get("alias"), expression=e.this) 2245 if isinstance(e, exp.Alias) 2246 else e 2247 for e in expression.expressions 2248 ] 2249 ) 2250 ) 2251 ] 2252 ) 2253 kind = "" 2254 2255 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2256 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2257 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2258 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2259 sql = self.query_modifiers( 2260 expression, 2261 f"SELECT{top_distinct}{kind}{expressions}", 2262 self.sql(expression, "into", comment=False), 2263 self.sql(expression, "from", comment=False), 2264 ) 2265 2266 sql = self.prepend_ctes(expression, sql) 2267 2268 if not self.SUPPORTS_SELECT_INTO and into: 2269 if into.args.get("temporary"): 2270 table_kind = " TEMPORARY" 2271 elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"): 2272 table_kind = " UNLOGGED" 2273 else: 2274 table_kind = "" 2275 sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}" 2276 2277 return sql 2278 2279 def schema_sql(self, expression: exp.Schema) -> str: 2280 this = self.sql(expression, "this") 2281 sql = self.schema_columns_sql(expression) 2282 return f"{this} {sql}" if this and sql else this or sql 2283 2284 def schema_columns_sql(self, expression: exp.Schema) -> str: 2285 if expression.expressions: 2286 return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}" 2287 return "" 2288 2289 def star_sql(self, expression: exp.Star) -> str: 2290 except_ = self.expressions(expression, key="except", flat=True) 2291 except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else "" 2292 replace = self.expressions(expression, key="replace", flat=True) 2293 replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else "" 2294 return f"*{except_}{replace}" 2295 2296 def parameter_sql(self, expression: exp.Parameter) -> str: 2297 this = self.sql(expression, "this") 2298 return f"{self.PARAMETER_TOKEN}{this}" 2299 2300 def sessionparameter_sql(self, expression: exp.SessionParameter) -> str: 2301 this = self.sql(expression, "this") 2302 kind = expression.text("kind") 2303 if kind: 2304 kind = f"{kind}." 2305 return f"@@{kind}{this}" 2306 2307 def placeholder_sql(self, expression: exp.Placeholder) -> str: 2308 return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?" 2309 2310 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2311 alias = self.sql(expression, "alias") 2312 alias = f"{sep}{alias}" if alias else "" 2313 2314 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2315 pivots = f" {pivots}" if pivots else "" 2316 2317 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2318 return self.prepend_ctes(expression, sql) 2319 2320 def qualify_sql(self, expression: exp.Qualify) -> str: 2321 this = self.indent(self.sql(expression, "this")) 2322 return f"{self.seg('QUALIFY')}{self.sep()}{this}" 2323 2324 def set_operations(self, expression: exp.Union) -> str: 2325 if not self.OUTER_UNION_MODIFIERS: 2326 limit = expression.args.get("limit") 2327 order = expression.args.get("order") 2328 2329 if limit or order: 2330 select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False) 2331 2332 if limit: 2333 select = select.limit(limit.pop(), copy=False) 2334 if order: 2335 select = select.order_by(order.pop(), copy=False) 2336 return self.sql(select) 2337 2338 sqls: t.List[str] = [] 2339 stack: t.List[t.Union[str, exp.Expression]] = [expression] 2340 2341 while stack: 2342 node = stack.pop() 2343 2344 if isinstance(node, exp.Union): 2345 stack.append(node.expression) 2346 stack.append( 2347 self.maybe_comment( 2348 getattr(self, f"{node.key}_op")(node), 2349 comments=node.comments, 2350 separated=True, 2351 ) 2352 ) 2353 stack.append(node.this) 2354 else: 2355 sqls.append(self.sql(node)) 2356 2357 this = self.sep().join(sqls) 2358 this = self.query_modifiers(expression, this) 2359 return self.prepend_ctes(expression, this) 2360 2361 def union_sql(self, expression: exp.Union) -> str: 2362 return self.set_operations(expression) 2363 2364 def union_op(self, expression: exp.Union) -> str: 2365 kind = " DISTINCT" if self.EXPLICIT_UNION else "" 2366 kind = kind if expression.args.get("distinct") else " ALL" 2367 by_name = " BY NAME" if expression.args.get("by_name") else "" 2368 return f"UNION{kind}{by_name}" 2369 2370 def unnest_sql(self, expression: exp.Unnest) -> str: 2371 args = self.expressions(expression, flat=True) 2372 2373 alias = expression.args.get("alias") 2374 offset = expression.args.get("offset") 2375 2376 if self.UNNEST_WITH_ORDINALITY: 2377 if alias and isinstance(offset, exp.Expression): 2378 alias.append("columns", offset) 2379 2380 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2381 columns = alias.columns 2382 alias = self.sql(columns[0]) if columns else "" 2383 else: 2384 alias = self.sql(alias) 2385 2386 alias = f" AS {alias}" if alias else alias 2387 if self.UNNEST_WITH_ORDINALITY: 2388 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2389 else: 2390 if isinstance(offset, exp.Expression): 2391 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2392 elif offset: 2393 suffix = f"{alias} WITH OFFSET" 2394 else: 2395 suffix = alias 2396 2397 return f"UNNEST({args}){suffix}" 2398 2399 def prewhere_sql(self, expression: exp.PreWhere) -> str: 2400 return "" 2401 2402 def where_sql(self, expression: exp.Where) -> str: 2403 this = self.indent(self.sql(expression, "this")) 2404 return f"{self.seg('WHERE')}{self.sep()}{this}" 2405 2406 def window_sql(self, expression: exp.Window) -> str: 2407 this = self.sql(expression, "this") 2408 partition = self.partition_by_sql(expression) 2409 order = expression.args.get("order") 2410 order = self.order_sql(order, flat=True) if order else "" 2411 spec = self.sql(expression, "spec") 2412 alias = self.sql(expression, "alias") 2413 over = self.sql(expression, "over") or "OVER" 2414 2415 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2416 2417 first = expression.args.get("first") 2418 if first is None: 2419 first = "" 2420 else: 2421 first = "FIRST" if first else "LAST" 2422 2423 if not partition and not order and not spec and alias: 2424 return f"{this} {alias}" 2425 2426 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2427 return f"{this} ({args})" 2428 2429 def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str: 2430 partition = self.expressions(expression, key="partition_by", flat=True) 2431 return f"PARTITION BY {partition}" if partition else "" 2432 2433 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2434 kind = self.sql(expression, "kind") 2435 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2436 end = ( 2437 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2438 or "CURRENT ROW" 2439 ) 2440 return f"{kind} BETWEEN {start} AND {end}" 2441 2442 def withingroup_sql(self, expression: exp.WithinGroup) -> str: 2443 this = self.sql(expression, "this") 2444 expression_sql = self.sql(expression, "expression")[1:] # order has a leading space 2445 return f"{this} WITHIN GROUP ({expression_sql})" 2446 2447 def between_sql(self, expression: exp.Between) -> str: 2448 this = self.sql(expression, "this") 2449 low = self.sql(expression, "low") 2450 high = self.sql(expression, "high") 2451 return f"{this} BETWEEN {low} AND {high}" 2452 2453 def bracket_offset_expressions(self, expression: exp.Bracket) -> t.List[exp.Expression]: 2454 return apply_index_offset( 2455 expression.this, 2456 expression.expressions, 2457 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2458 ) 2459 2460 def bracket_sql(self, expression: exp.Bracket) -> str: 2461 expressions = self.bracket_offset_expressions(expression) 2462 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2463 return f"{self.sql(expression, 'this')}[{expressions_sql}]" 2464 2465 def all_sql(self, expression: exp.All) -> str: 2466 return f"ALL {self.wrap(expression)}" 2467 2468 def any_sql(self, expression: exp.Any) -> str: 2469 this = self.sql(expression, "this") 2470 if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)): 2471 if isinstance(expression.this, exp.UNWRAPPED_QUERIES): 2472 this = self.wrap(this) 2473 return f"ANY{this}" 2474 return f"ANY {this}" 2475 2476 def exists_sql(self, expression: exp.Exists) -> str: 2477 return f"EXISTS{self.wrap(expression)}" 2478 2479 def case_sql(self, expression: exp.Case) -> str: 2480 this = self.sql(expression, "this") 2481 statements = [f"CASE {this}" if this else "CASE"] 2482 2483 for e in expression.args["ifs"]: 2484 statements.append(f"WHEN {self.sql(e, 'this')}") 2485 statements.append(f"THEN {self.sql(e, 'true')}") 2486 2487 default = self.sql(expression, "default") 2488 2489 if default: 2490 statements.append(f"ELSE {default}") 2491 2492 statements.append("END") 2493 2494 if self.pretty and self.too_wide(statements): 2495 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2496 2497 return " ".join(statements) 2498 2499 def constraint_sql(self, expression: exp.Constraint) -> str: 2500 this = self.sql(expression, "this") 2501 expressions = self.expressions(expression, flat=True) 2502 return f"CONSTRAINT {this} {expressions}" 2503 2504 def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str: 2505 order = expression.args.get("order") 2506 order = f" OVER ({self.order_sql(order, flat=True)})" if order else "" 2507 return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}" 2508 2509 def extract_sql(self, expression: exp.Extract) -> str: 2510 this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name 2511 expression_sql = self.sql(expression, "expression") 2512 return f"EXTRACT({this} FROM {expression_sql})" 2513 2514 def trim_sql(self, expression: exp.Trim) -> str: 2515 trim_type = self.sql(expression, "position") 2516 2517 if trim_type == "LEADING": 2518 return self.func("LTRIM", expression.this) 2519 elif trim_type == "TRAILING": 2520 return self.func("RTRIM", expression.this) 2521 else: 2522 return self.func("TRIM", expression.this, expression.expression) 2523 2524 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2525 args = expression.expressions 2526 if isinstance(expression, exp.ConcatWs): 2527 args = args[1:] # Skip the delimiter 2528 2529 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2530 args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args] 2531 2532 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2533 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2534 2535 return args 2536 2537 def concat_sql(self, expression: exp.Concat) -> str: 2538 expressions = self.convert_concat_args(expression) 2539 2540 # Some dialects don't allow a single-argument CONCAT call 2541 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2542 return self.sql(expressions[0]) 2543 2544 return self.func("CONCAT", *expressions) 2545 2546 def concatws_sql(self, expression: exp.ConcatWs) -> str: 2547 return self.func( 2548 "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression) 2549 ) 2550 2551 def check_sql(self, expression: exp.Check) -> str: 2552 this = self.sql(expression, key="this") 2553 return f"CHECK ({this})" 2554 2555 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2556 expressions = self.expressions(expression, flat=True) 2557 reference = self.sql(expression, "reference") 2558 reference = f" {reference}" if reference else "" 2559 delete = self.sql(expression, "delete") 2560 delete = f" ON DELETE {delete}" if delete else "" 2561 update = self.sql(expression, "update") 2562 update = f" ON UPDATE {update}" if update else "" 2563 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}" 2564 2565 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2566 expressions = self.expressions(expression, flat=True) 2567 options = self.expressions(expression, key="options", flat=True, sep=" ") 2568 options = f" {options}" if options else "" 2569 return f"PRIMARY KEY ({expressions}){options}" 2570 2571 def if_sql(self, expression: exp.If) -> str: 2572 return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false"))) 2573 2574 def matchagainst_sql(self, expression: exp.MatchAgainst) -> str: 2575 modifier = expression.args.get("modifier") 2576 modifier = f" {modifier}" if modifier else "" 2577 return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})" 2578 2579 def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str: 2580 return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}" 2581 2582 def jsonpath_sql(self, expression: exp.JSONPath) -> str: 2583 path = self.expressions(expression, sep="", flat=True).lstrip(".") 2584 return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}" 2585 2586 def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str: 2587 if isinstance(expression, exp.JSONPathPart): 2588 transform = self.TRANSFORMS.get(expression.__class__) 2589 if not callable(transform): 2590 self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}") 2591 return "" 2592 2593 return transform(self, expression) 2594 2595 if isinstance(expression, int): 2596 return str(expression) 2597 2598 if self.JSON_PATH_SINGLE_QUOTE_ESCAPE: 2599 escaped = expression.replace("'", "\\'") 2600 escaped = f"\\'{expression}\\'" 2601 else: 2602 escaped = expression.replace('"', '\\"') 2603 escaped = f'"{escaped}"' 2604 2605 return escaped 2606 2607 def formatjson_sql(self, expression: exp.FormatJson) -> str: 2608 return f"{self.sql(expression, 'this')} FORMAT JSON" 2609 2610 def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str: 2611 null_handling = expression.args.get("null_handling") 2612 null_handling = f" {null_handling}" if null_handling else "" 2613 2614 unique_keys = expression.args.get("unique_keys") 2615 if unique_keys is not None: 2616 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2617 else: 2618 unique_keys = "" 2619 2620 return_type = self.sql(expression, "return_type") 2621 return_type = f" RETURNING {return_type}" if return_type else "" 2622 encoding = self.sql(expression, "encoding") 2623 encoding = f" ENCODING {encoding}" if encoding else "" 2624 2625 return self.func( 2626 "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG", 2627 *expression.expressions, 2628 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2629 ) 2630 2631 def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str: 2632 return self.jsonobject_sql(expression) 2633 2634 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2635 null_handling = expression.args.get("null_handling") 2636 null_handling = f" {null_handling}" if null_handling else "" 2637 return_type = self.sql(expression, "return_type") 2638 return_type = f" RETURNING {return_type}" if return_type else "" 2639 strict = " STRICT" if expression.args.get("strict") else "" 2640 return self.func( 2641 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2642 ) 2643 2644 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2645 this = self.sql(expression, "this") 2646 order = self.sql(expression, "order") 2647 null_handling = expression.args.get("null_handling") 2648 null_handling = f" {null_handling}" if null_handling else "" 2649 return_type = self.sql(expression, "return_type") 2650 return_type = f" RETURNING {return_type}" if return_type else "" 2651 strict = " STRICT" if expression.args.get("strict") else "" 2652 return self.func( 2653 "JSON_ARRAYAGG", 2654 this, 2655 suffix=f"{order}{null_handling}{return_type}{strict})", 2656 ) 2657 2658 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2659 path = self.sql(expression, "path") 2660 path = f" PATH {path}" if path else "" 2661 nested_schema = self.sql(expression, "nested_schema") 2662 2663 if nested_schema: 2664 return f"NESTED{path} {nested_schema}" 2665 2666 this = self.sql(expression, "this") 2667 kind = self.sql(expression, "kind") 2668 kind = f" {kind}" if kind else "" 2669 return f"{this}{kind}{path}" 2670 2671 def jsonschema_sql(self, expression: exp.JSONSchema) -> str: 2672 return self.func("COLUMNS", *expression.expressions) 2673 2674 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2675 this = self.sql(expression, "this") 2676 path = self.sql(expression, "path") 2677 path = f", {path}" if path else "" 2678 error_handling = expression.args.get("error_handling") 2679 error_handling = f" {error_handling}" if error_handling else "" 2680 empty_handling = expression.args.get("empty_handling") 2681 empty_handling = f" {empty_handling}" if empty_handling else "" 2682 schema = self.sql(expression, "schema") 2683 return self.func( 2684 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2685 ) 2686 2687 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2688 this = self.sql(expression, "this") 2689 kind = self.sql(expression, "kind") 2690 path = self.sql(expression, "path") 2691 path = f" {path}" if path else "" 2692 as_json = " AS JSON" if expression.args.get("as_json") else "" 2693 return f"{this} {kind}{path}{as_json}" 2694 2695 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2696 this = self.sql(expression, "this") 2697 path = self.sql(expression, "path") 2698 path = f", {path}" if path else "" 2699 expressions = self.expressions(expression) 2700 with_ = ( 2701 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2702 if expressions 2703 else "" 2704 ) 2705 return f"OPENJSON({this}{path}){with_}" 2706 2707 def in_sql(self, expression: exp.In) -> str: 2708 query = expression.args.get("query") 2709 unnest = expression.args.get("unnest") 2710 field = expression.args.get("field") 2711 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2712 2713 if query: 2714 in_sql = self.sql(query) 2715 elif unnest: 2716 in_sql = self.in_unnest_op(unnest) 2717 elif field: 2718 in_sql = self.sql(field) 2719 else: 2720 in_sql = f"({self.expressions(expression, flat=True)})" 2721 2722 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}" 2723 2724 def in_unnest_op(self, unnest: exp.Unnest) -> str: 2725 return f"(SELECT {self.sql(unnest)})" 2726 2727 def interval_sql(self, expression: exp.Interval) -> str: 2728 unit = self.sql(expression, "unit") 2729 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2730 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2731 unit = f" {unit}" if unit else "" 2732 2733 if self.SINGLE_STRING_INTERVAL: 2734 this = expression.this.name if expression.this else "" 2735 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2736 2737 this = self.sql(expression, "this") 2738 if this: 2739 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2740 this = f" {this}" if unwrapped else f" ({this})" 2741 2742 return f"INTERVAL{this}{unit}" 2743 2744 def return_sql(self, expression: exp.Return) -> str: 2745 return f"RETURN {self.sql(expression, 'this')}" 2746 2747 def reference_sql(self, expression: exp.Reference) -> str: 2748 this = self.sql(expression, "this") 2749 expressions = self.expressions(expression, flat=True) 2750 expressions = f"({expressions})" if expressions else "" 2751 options = self.expressions(expression, key="options", flat=True, sep=" ") 2752 options = f" {options}" if options else "" 2753 return f"REFERENCES {this}{expressions}{options}" 2754 2755 def anonymous_sql(self, expression: exp.Anonymous) -> str: 2756 return self.func(self.sql(expression, "this"), *expression.expressions) 2757 2758 def paren_sql(self, expression: exp.Paren) -> str: 2759 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2760 return f"({sql}{self.seg(')', sep='')}" 2761 2762 def neg_sql(self, expression: exp.Neg) -> str: 2763 # This makes sure we don't convert "- - 5" to "--5", which is a comment 2764 this_sql = self.sql(expression, "this") 2765 sep = " " if this_sql[0] == "-" else "" 2766 return f"-{sep}{this_sql}" 2767 2768 def not_sql(self, expression: exp.Not) -> str: 2769 return f"NOT {self.sql(expression, 'this')}" 2770 2771 def alias_sql(self, expression: exp.Alias) -> str: 2772 alias = self.sql(expression, "alias") 2773 alias = f" AS {alias}" if alias else "" 2774 return f"{self.sql(expression, 'this')}{alias}" 2775 2776 def pivotalias_sql(self, expression: exp.PivotAlias) -> str: 2777 alias = expression.args["alias"] 2778 identifier_alias = isinstance(alias, exp.Identifier) 2779 2780 if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2781 alias.replace(exp.Literal.string(alias.output_name)) 2782 elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2783 alias.replace(exp.to_identifier(alias.output_name)) 2784 2785 return self.alias_sql(expression) 2786 2787 def aliases_sql(self, expression: exp.Aliases) -> str: 2788 return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})" 2789 2790 def atindex_sql(self, expression: exp.AtTimeZone) -> str: 2791 this = self.sql(expression, "this") 2792 index = self.sql(expression, "expression") 2793 return f"{this} AT {index}" 2794 2795 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 2796 this = self.sql(expression, "this") 2797 zone = self.sql(expression, "zone") 2798 return f"{this} AT TIME ZONE {zone}" 2799 2800 def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str: 2801 this = self.sql(expression, "this") 2802 zone = self.sql(expression, "zone") 2803 return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'" 2804 2805 def add_sql(self, expression: exp.Add) -> str: 2806 return self.binary(expression, "+") 2807 2808 def and_sql( 2809 self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None 2810 ) -> str: 2811 return self.connector_sql(expression, "AND", stack) 2812 2813 def or_sql( 2814 self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None 2815 ) -> str: 2816 return self.connector_sql(expression, "OR", stack) 2817 2818 def xor_sql( 2819 self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None 2820 ) -> str: 2821 return self.connector_sql(expression, "XOR", stack) 2822 2823 def connector_sql( 2824 self, 2825 expression: exp.Connector, 2826 op: str, 2827 stack: t.Optional[t.List[str | exp.Expression]] = None, 2828 ) -> str: 2829 if stack is not None: 2830 if expression.expressions: 2831 stack.append(self.expressions(expression, sep=f" {op} ")) 2832 else: 2833 stack.append(expression.right) 2834 if expression.comments: 2835 for comment in expression.comments: 2836 op += f" /*{self.pad_comment(comment)}*/" 2837 stack.extend((op, expression.left)) 2838 return op 2839 2840 stack = [expression] 2841 sqls: t.List[str] = [] 2842 ops = set() 2843 2844 while stack: 2845 node = stack.pop() 2846 if isinstance(node, exp.Connector): 2847 ops.add(getattr(self, f"{node.key}_sql")(node, stack)) 2848 else: 2849 sql = self.sql(node) 2850 if sqls and sqls[-1] in ops: 2851 sqls[-1] += f" {sql}" 2852 else: 2853 sqls.append(sql) 2854 2855 sep = "\n" if self.pretty and self.too_wide(sqls) else " " 2856 return sep.join(sqls) 2857 2858 def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str: 2859 return self.binary(expression, "&") 2860 2861 def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str: 2862 return self.binary(expression, "<<") 2863 2864 def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str: 2865 return f"~{self.sql(expression, 'this')}" 2866 2867 def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str: 2868 return self.binary(expression, "|") 2869 2870 def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str: 2871 return self.binary(expression, ">>") 2872 2873 def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str: 2874 return self.binary(expression, "^") 2875 2876 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2877 format_sql = self.sql(expression, "format") 2878 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2879 to_sql = self.sql(expression, "to") 2880 to_sql = f" {to_sql}" if to_sql else "" 2881 action = self.sql(expression, "action") 2882 action = f" {action}" if action else "" 2883 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql}{action})" 2884 2885 def currentdate_sql(self, expression: exp.CurrentDate) -> str: 2886 zone = self.sql(expression, "this") 2887 return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE" 2888 2889 def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str: 2890 return self.func("CURRENT_TIMESTAMP", expression.this) 2891 2892 def collate_sql(self, expression: exp.Collate) -> str: 2893 if self.COLLATE_IS_FUNC: 2894 return self.function_fallback_sql(expression) 2895 return self.binary(expression, "COLLATE") 2896 2897 def command_sql(self, expression: exp.Command) -> str: 2898 return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}" 2899 2900 def comment_sql(self, expression: exp.Comment) -> str: 2901 this = self.sql(expression, "this") 2902 kind = expression.args["kind"] 2903 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 2904 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2905 expression_sql = self.sql(expression, "expression") 2906 return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}" 2907 2908 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2909 this = self.sql(expression, "this") 2910 delete = " DELETE" if expression.args.get("delete") else "" 2911 recompress = self.sql(expression, "recompress") 2912 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2913 to_disk = self.sql(expression, "to_disk") 2914 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2915 to_volume = self.sql(expression, "to_volume") 2916 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2917 return f"{this}{delete}{recompress}{to_disk}{to_volume}" 2918 2919 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2920 where = self.sql(expression, "where") 2921 group = self.sql(expression, "group") 2922 aggregates = self.expressions(expression, key="aggregates") 2923 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2924 2925 if not (where or group or aggregates) and len(expression.expressions) == 1: 2926 return f"TTL {self.expressions(expression, flat=True)}" 2927 2928 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}" 2929 2930 def transaction_sql(self, expression: exp.Transaction) -> str: 2931 return "BEGIN" 2932 2933 def commit_sql(self, expression: exp.Commit) -> str: 2934 chain = expression.args.get("chain") 2935 if chain is not None: 2936 chain = " AND CHAIN" if chain else " AND NO CHAIN" 2937 2938 return f"COMMIT{chain or ''}" 2939 2940 def rollback_sql(self, expression: exp.Rollback) -> str: 2941 savepoint = expression.args.get("savepoint") 2942 savepoint = f" TO {savepoint}" if savepoint else "" 2943 return f"ROLLBACK{savepoint}" 2944 2945 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2946 this = self.sql(expression, "this") 2947 2948 dtype = self.sql(expression, "dtype") 2949 if dtype: 2950 collate = self.sql(expression, "collate") 2951 collate = f" COLLATE {collate}" if collate else "" 2952 using = self.sql(expression, "using") 2953 using = f" USING {using}" if using else "" 2954 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2955 2956 default = self.sql(expression, "default") 2957 if default: 2958 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2959 2960 comment = self.sql(expression, "comment") 2961 if comment: 2962 return f"ALTER COLUMN {this} COMMENT {comment}" 2963 2964 if not expression.args.get("drop"): 2965 self.unsupported("Unsupported ALTER COLUMN syntax") 2966 2967 return f"ALTER COLUMN {this} DROP DEFAULT" 2968 2969 def renametable_sql(self, expression: exp.RenameTable) -> str: 2970 if not self.RENAME_TABLE_WITH_DB: 2971 # Remove db from tables 2972 expression = expression.transform( 2973 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 2974 ).assert_is(exp.RenameTable) 2975 this = self.sql(expression, "this") 2976 return f"RENAME TO {this}" 2977 2978 def renamecolumn_sql(self, expression: exp.RenameColumn) -> str: 2979 exists = " IF EXISTS" if expression.args.get("exists") else "" 2980 old_column = self.sql(expression, "this") 2981 new_column = self.sql(expression, "to") 2982 return f"RENAME COLUMN{exists} {old_column} TO {new_column}" 2983 2984 def altertable_sql(self, expression: exp.AlterTable) -> str: 2985 actions = expression.args["actions"] 2986 2987 if isinstance(actions[0], exp.ColumnDef): 2988 actions = self.add_column_sql(expression) 2989 elif isinstance(actions[0], exp.Schema): 2990 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 2991 elif isinstance(actions[0], exp.Delete): 2992 actions = self.expressions(expression, key="actions", flat=True) 2993 else: 2994 actions = self.expressions(expression, key="actions", flat=True) 2995 2996 exists = " IF EXISTS" if expression.args.get("exists") else "" 2997 only = " ONLY" if expression.args.get("only") else "" 2998 options = self.expressions(expression, key="options") 2999 options = f", {options}" if options else "" 3000 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}" 3001 3002 def add_column_sql(self, expression: exp.AlterTable) -> str: 3003 if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD: 3004 return self.expressions( 3005 expression, 3006 key="actions", 3007 prefix="ADD COLUMN ", 3008 ) 3009 return f"ADD {self.expressions(expression, key='actions', flat=True)}" 3010 3011 def droppartition_sql(self, expression: exp.DropPartition) -> str: 3012 expressions = self.expressions(expression) 3013 exists = " IF EXISTS " if expression.args.get("exists") else " " 3014 return f"DROP{exists}{expressions}" 3015 3016 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 3017 return f"ADD {self.expressions(expression)}" 3018 3019 def distinct_sql(self, expression: exp.Distinct) -> str: 3020 this = self.expressions(expression, flat=True) 3021 3022 if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1: 3023 case = exp.case() 3024 for arg in expression.expressions: 3025 case = case.when(arg.is_(exp.null()), exp.null()) 3026 this = self.sql(case.else_(f"({this})")) 3027 3028 this = f" {this}" if this else "" 3029 3030 on = self.sql(expression, "on") 3031 on = f" ON {on}" if on else "" 3032 return f"DISTINCT{this}{on}" 3033 3034 def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: 3035 return self._embed_ignore_nulls(expression, "IGNORE NULLS") 3036 3037 def respectnulls_sql(self, expression: exp.RespectNulls) -> str: 3038 return self._embed_ignore_nulls(expression, "RESPECT NULLS") 3039 3040 def havingmax_sql(self, expression: exp.HavingMax) -> str: 3041 this_sql = self.sql(expression, "this") 3042 expression_sql = self.sql(expression, "expression") 3043 kind = "MAX" if expression.args.get("max") else "MIN" 3044 return f"{this_sql} HAVING {kind} {expression_sql}" 3045 3046 def intdiv_sql(self, expression: exp.IntDiv) -> str: 3047 return self.sql( 3048 exp.Cast( 3049 this=exp.Div(this=expression.this, expression=expression.expression), 3050 to=exp.DataType(this=exp.DataType.Type.INT), 3051 ) 3052 ) 3053 3054 def dpipe_sql(self, expression: exp.DPipe) -> str: 3055 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 3056 return self.func( 3057 "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten()) 3058 ) 3059 return self.binary(expression, "||") 3060 3061 def div_sql(self, expression: exp.Div) -> str: 3062 l, r = expression.left, expression.right 3063 3064 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 3065 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 3066 3067 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 3068 if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES): 3069 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 3070 3071 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 3072 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 3073 return self.sql( 3074 exp.cast( 3075 l / r, 3076 to=exp.DataType.Type.BIGINT, 3077 ) 3078 ) 3079 3080 return self.binary(expression, "/") 3081 3082 def overlaps_sql(self, expression: exp.Overlaps) -> str: 3083 return self.binary(expression, "OVERLAPS") 3084 3085 def distance_sql(self, expression: exp.Distance) -> str: 3086 return self.binary(expression, "<->") 3087 3088 def dot_sql(self, expression: exp.Dot) -> str: 3089 return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}" 3090 3091 def eq_sql(self, expression: exp.EQ) -> str: 3092 return self.binary(expression, "=") 3093 3094 def propertyeq_sql(self, expression: exp.PropertyEQ) -> str: 3095 return self.binary(expression, ":=") 3096 3097 def escape_sql(self, expression: exp.Escape) -> str: 3098 return self.binary(expression, "ESCAPE") 3099 3100 def glob_sql(self, expression: exp.Glob) -> str: 3101 return self.binary(expression, "GLOB") 3102 3103 def gt_sql(self, expression: exp.GT) -> str: 3104 return self.binary(expression, ">") 3105 3106 def gte_sql(self, expression: exp.GTE) -> str: 3107 return self.binary(expression, ">=") 3108 3109 def ilike_sql(self, expression: exp.ILike) -> str: 3110 return self.binary(expression, "ILIKE") 3111 3112 def ilikeany_sql(self, expression: exp.ILikeAny) -> str: 3113 return self.binary(expression, "ILIKE ANY") 3114 3115 def is_sql(self, expression: exp.Is) -> str: 3116 if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean): 3117 return self.sql( 3118 expression.this if expression.expression.this else exp.not_(expression.this) 3119 ) 3120 return self.binary(expression, "IS") 3121 3122 def like_sql(self, expression: exp.Like) -> str: 3123 return self.binary(expression, "LIKE") 3124 3125 def likeany_sql(self, expression: exp.LikeAny) -> str: 3126 return self.binary(expression, "LIKE ANY") 3127 3128 def similarto_sql(self, expression: exp.SimilarTo) -> str: 3129 return self.binary(expression, "SIMILAR TO") 3130 3131 def lt_sql(self, expression: exp.LT) -> str: 3132 return self.binary(expression, "<") 3133 3134 def lte_sql(self, expression: exp.LTE) -> str: 3135 return self.binary(expression, "<=") 3136 3137 def mod_sql(self, expression: exp.Mod) -> str: 3138 return self.binary(expression, "%") 3139 3140 def mul_sql(self, expression: exp.Mul) -> str: 3141 return self.binary(expression, "*") 3142 3143 def neq_sql(self, expression: exp.NEQ) -> str: 3144 return self.binary(expression, "<>") 3145 3146 def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str: 3147 return self.binary(expression, "IS NOT DISTINCT FROM") 3148 3149 def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str: 3150 return self.binary(expression, "IS DISTINCT FROM") 3151 3152 def slice_sql(self, expression: exp.Slice) -> str: 3153 return self.binary(expression, ":") 3154 3155 def sub_sql(self, expression: exp.Sub) -> str: 3156 return self.binary(expression, "-") 3157 3158 def trycast_sql(self, expression: exp.TryCast) -> str: 3159 return self.cast_sql(expression, safe_prefix="TRY_") 3160 3161 def log_sql(self, expression: exp.Log) -> str: 3162 this = expression.this 3163 expr = expression.expression 3164 3165 if self.dialect.LOG_BASE_FIRST is False: 3166 this, expr = expr, this 3167 elif self.dialect.LOG_BASE_FIRST is None and expr: 3168 if this.name in ("2", "10"): 3169 return self.func(f"LOG{this.name}", expr) 3170 3171 self.unsupported(f"Unsupported logarithm with base {self.sql(this)}") 3172 3173 return self.func("LOG", this, expr) 3174 3175 def use_sql(self, expression: exp.Use) -> str: 3176 kind = self.sql(expression, "kind") 3177 kind = f" {kind}" if kind else "" 3178 this = self.sql(expression, "this") 3179 this = f" {this}" if this else "" 3180 return f"USE{kind}{this}" 3181 3182 def binary(self, expression: exp.Binary, op: str) -> str: 3183 op = self.maybe_comment(op, comments=expression.comments) 3184 return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}" 3185 3186 def function_fallback_sql(self, expression: exp.Func) -> str: 3187 args = [] 3188 3189 for key in expression.arg_types: 3190 arg_value = expression.args.get(key) 3191 3192 if isinstance(arg_value, list): 3193 for value in arg_value: 3194 args.append(value) 3195 elif arg_value is not None: 3196 args.append(arg_value) 3197 3198 if self.normalize_functions: 3199 name = expression.sql_name() 3200 else: 3201 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 3202 3203 return self.func(name, *args) 3204 3205 def func( 3206 self, 3207 name: str, 3208 *args: t.Optional[exp.Expression | str], 3209 prefix: str = "(", 3210 suffix: str = ")", 3211 ) -> str: 3212 return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}" 3213 3214 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 3215 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 3216 if self.pretty and self.too_wide(arg_sqls): 3217 return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 3218 return ", ".join(arg_sqls) 3219 3220 def too_wide(self, args: t.Iterable) -> bool: 3221 return sum(len(arg) for arg in args) > self.max_text_width 3222 3223 def format_time( 3224 self, 3225 expression: exp.Expression, 3226 inverse_time_mapping: t.Optional[t.Dict[str, str]] = None, 3227 inverse_time_trie: t.Optional[t.Dict] = None, 3228 ) -> t.Optional[str]: 3229 return format_time( 3230 self.sql(expression, "format"), 3231 inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING, 3232 inverse_time_trie or self.dialect.INVERSE_TIME_TRIE, 3233 ) 3234 3235 def expressions( 3236 self, 3237 expression: t.Optional[exp.Expression] = None, 3238 key: t.Optional[str] = None, 3239 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 3240 flat: bool = False, 3241 indent: bool = True, 3242 skip_first: bool = False, 3243 skip_last: bool = False, 3244 sep: str = ", ", 3245 prefix: str = "", 3246 dynamic: bool = False, 3247 new_line: bool = False, 3248 ) -> str: 3249 expressions = expression.args.get(key or "expressions") if expression else sqls 3250 3251 if not expressions: 3252 return "" 3253 3254 if flat: 3255 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 3256 3257 num_sqls = len(expressions) 3258 3259 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 3260 if self.pretty and not self.leading_comma: 3261 stripped_sep = sep.strip() 3262 3263 result_sqls = [] 3264 for i, e in enumerate(expressions): 3265 sql = self.sql(e, comment=False) 3266 if not sql: 3267 continue 3268 3269 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 3270 3271 if self.pretty: 3272 if self.leading_comma: 3273 result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}") 3274 else: 3275 result_sqls.append( 3276 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 3277 ) 3278 else: 3279 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 3280 3281 if self.pretty and (not dynamic or self.too_wide(result_sqls)): 3282 if new_line: 3283 result_sqls.insert(0, "") 3284 result_sqls.append("") 3285 result_sql = "\n".join(result_sqls) 3286 else: 3287 result_sql = "".join(result_sqls) 3288 return ( 3289 self.indent(result_sql, skip_first=skip_first, skip_last=skip_last) 3290 if indent 3291 else result_sql 3292 ) 3293 3294 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 3295 flat = flat or isinstance(expression.parent, exp.Properties) 3296 expressions_sql = self.expressions(expression, flat=flat) 3297 if flat: 3298 return f"{op} {expressions_sql}" 3299 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}" 3300 3301 def naked_property(self, expression: exp.Property) -> str: 3302 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 3303 if not property_name: 3304 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 3305 return f"{property_name} {self.sql(expression, 'this')}" 3306 3307 def tag_sql(self, expression: exp.Tag) -> str: 3308 return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}" 3309 3310 def token_sql(self, token_type: TokenType) -> str: 3311 return self.TOKEN_MAPPING.get(token_type, token_type.name) 3312 3313 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 3314 this = self.sql(expression, "this") 3315 expressions = self.no_identify(self.expressions, expression) 3316 expressions = ( 3317 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 3318 ) 3319 return f"{this}{expressions}" 3320 3321 def joinhint_sql(self, expression: exp.JoinHint) -> str: 3322 this = self.sql(expression, "this") 3323 expressions = self.expressions(expression, flat=True) 3324 return f"{this}({expressions})" 3325 3326 def kwarg_sql(self, expression: exp.Kwarg) -> str: 3327 return self.binary(expression, "=>") 3328 3329 def when_sql(self, expression: exp.When) -> str: 3330 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 3331 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 3332 condition = self.sql(expression, "condition") 3333 condition = f" AND {condition}" if condition else "" 3334 3335 then_expression = expression.args.get("then") 3336 if isinstance(then_expression, exp.Insert): 3337 then = f"INSERT {self.sql(then_expression, 'this')}" 3338 if "expression" in then_expression.args: 3339 then += f" VALUES {self.sql(then_expression, 'expression')}" 3340 elif isinstance(then_expression, exp.Update): 3341 if isinstance(then_expression.args.get("expressions"), exp.Star): 3342 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 3343 else: 3344 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 3345 else: 3346 then = self.sql(then_expression) 3347 return f"WHEN {matched}{source}{condition} THEN {then}" 3348 3349 def merge_sql(self, expression: exp.Merge) -> str: 3350 table = expression.this 3351 table_alias = "" 3352 3353 hints = table.args.get("hints") 3354 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 3355 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 3356 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 3357 3358 this = self.sql(table) 3359 using = f"USING {self.sql(expression, 'using')}" 3360 on = f"ON {self.sql(expression, 'on')}" 3361 expressions = self.expressions(expression, sep=" ") 3362 3363 return self.prepend_ctes( 3364 expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}" 3365 ) 3366 3367 def tochar_sql(self, expression: exp.ToChar) -> str: 3368 if expression.args.get("format"): 3369 self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function") 3370 3371 return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT)) 3372 3373 def tonumber_sql(self, expression: exp.ToNumber) -> str: 3374 if not self.SUPPORTS_TO_NUMBER: 3375 self.unsupported("Unsupported TO_NUMBER function") 3376 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3377 3378 fmt = expression.args.get("format") 3379 if not fmt: 3380 self.unsupported("Conversion format is required for TO_NUMBER") 3381 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3382 3383 return self.func("TO_NUMBER", expression.this, fmt) 3384 3385 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 3386 this = self.sql(expression, "this") 3387 kind = self.sql(expression, "kind") 3388 settings_sql = self.expressions(expression, key="settings", sep=" ") 3389 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 3390 return f"{this}({kind}{args})" 3391 3392 def dictrange_sql(self, expression: exp.DictRange) -> str: 3393 this = self.sql(expression, "this") 3394 max = self.sql(expression, "max") 3395 min = self.sql(expression, "min") 3396 return f"{this}(MIN {min} MAX {max})" 3397 3398 def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str: 3399 return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}" 3400 3401 def oncluster_sql(self, expression: exp.OnCluster) -> str: 3402 return "" 3403 3404 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3405 expressions = self.expressions(expression, key="expressions", flat=True) 3406 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3407 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3408 buckets = self.sql(expression, "buckets") 3409 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS" 3410 3411 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3412 this = self.sql(expression, "this") 3413 having = self.sql(expression, "having") 3414 3415 if having: 3416 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3417 3418 return self.func("ANY_VALUE", this) 3419 3420 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3421 transform = self.func("TRANSFORM", *expression.expressions) 3422 row_format_before = self.sql(expression, "row_format_before") 3423 row_format_before = f" {row_format_before}" if row_format_before else "" 3424 record_writer = self.sql(expression, "record_writer") 3425 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3426 using = f" USING {self.sql(expression, 'command_script')}" 3427 schema = self.sql(expression, "schema") 3428 schema = f" AS {schema}" if schema else "" 3429 row_format_after = self.sql(expression, "row_format_after") 3430 row_format_after = f" {row_format_after}" if row_format_after else "" 3431 record_reader = self.sql(expression, "record_reader") 3432 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3433 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}" 3434 3435 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3436 key_block_size = self.sql(expression, "key_block_size") 3437 if key_block_size: 3438 return f"KEY_BLOCK_SIZE = {key_block_size}" 3439 3440 using = self.sql(expression, "using") 3441 if using: 3442 return f"USING {using}" 3443 3444 parser = self.sql(expression, "parser") 3445 if parser: 3446 return f"WITH PARSER {parser}" 3447 3448 comment = self.sql(expression, "comment") 3449 if comment: 3450 return f"COMMENT {comment}" 3451 3452 visible = expression.args.get("visible") 3453 if visible is not None: 3454 return "VISIBLE" if visible else "INVISIBLE" 3455 3456 engine_attr = self.sql(expression, "engine_attr") 3457 if engine_attr: 3458 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3459 3460 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3461 if secondary_engine_attr: 3462 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3463 3464 self.unsupported("Unsupported index constraint option.") 3465 return "" 3466 3467 def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str: 3468 enforced = " ENFORCED" if expression.args.get("enforced") else "" 3469 return f"CHECK ({self.sql(expression, 'this')}){enforced}" 3470 3471 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3472 kind = self.sql(expression, "kind") 3473 kind = f"{kind} INDEX" if kind else "INDEX" 3474 this = self.sql(expression, "this") 3475 this = f" {this}" if this else "" 3476 index_type = self.sql(expression, "index_type") 3477 index_type = f" USING {index_type}" if index_type else "" 3478 expressions = self.expressions(expression, flat=True) 3479 expressions = f" ({expressions})" if expressions else "" 3480 options = self.expressions(expression, key="options", sep=" ") 3481 options = f" {options}" if options else "" 3482 return f"{kind}{this}{index_type}{expressions}{options}" 3483 3484 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3485 if self.NVL2_SUPPORTED: 3486 return self.function_fallback_sql(expression) 3487 3488 case = exp.Case().when( 3489 expression.this.is_(exp.null()).not_(copy=False), 3490 expression.args["true"], 3491 copy=False, 3492 ) 3493 else_cond = expression.args.get("false") 3494 if else_cond: 3495 case.else_(else_cond, copy=False) 3496 3497 return self.sql(case) 3498 3499 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3500 this = self.sql(expression, "this") 3501 expr = self.sql(expression, "expression") 3502 iterator = self.sql(expression, "iterator") 3503 condition = self.sql(expression, "condition") 3504 condition = f" IF {condition}" if condition else "" 3505 return f"{this} FOR {expr} IN {iterator}{condition}" 3506 3507 def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str: 3508 return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})" 3509 3510 def opclass_sql(self, expression: exp.Opclass) -> str: 3511 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 3512 3513 def predict_sql(self, expression: exp.Predict) -> str: 3514 model = self.sql(expression, "this") 3515 model = f"MODEL {model}" 3516 table = self.sql(expression, "expression") 3517 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3518 parameters = self.sql(expression, "params_struct") 3519 return self.func("PREDICT", model, table, parameters or None) 3520 3521 def forin_sql(self, expression: exp.ForIn) -> str: 3522 this = self.sql(expression, "this") 3523 expression_sql = self.sql(expression, "expression") 3524 return f"FOR {this} DO {expression_sql}" 3525 3526 def refresh_sql(self, expression: exp.Refresh) -> str: 3527 this = self.sql(expression, "this") 3528 table = "" if isinstance(expression.this, exp.Literal) else "TABLE " 3529 return f"REFRESH {table}{this}" 3530 3531 def operator_sql(self, expression: exp.Operator) -> str: 3532 return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})") 3533 3534 def toarray_sql(self, expression: exp.ToArray) -> str: 3535 arg = expression.this 3536 if not arg.type: 3537 from sqlglot.optimizer.annotate_types import annotate_types 3538 3539 arg = annotate_types(arg) 3540 3541 if arg.is_type(exp.DataType.Type.ARRAY): 3542 return self.sql(arg) 3543 3544 cond_for_null = arg.is_(exp.null()) 3545 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False))) 3546 3547 def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str: 3548 this = expression.this 3549 if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME): 3550 return self.sql(this) 3551 3552 return self.sql(exp.cast(this, exp.DataType.Type.TIME)) 3553 3554 def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str: 3555 this = expression.this 3556 if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP): 3557 return self.sql(this) 3558 3559 return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP)) 3560 3561 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3562 this = expression.this 3563 time_format = self.format_time(expression) 3564 3565 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3566 return self.sql( 3567 exp.cast( 3568 exp.StrToTime(this=this, format=expression.args["format"]), 3569 exp.DataType.Type.DATE, 3570 ) 3571 ) 3572 3573 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3574 return self.sql(this) 3575 3576 return self.sql(exp.cast(this, exp.DataType.Type.DATE)) 3577 3578 def unixdate_sql(self, expression: exp.UnixDate) -> str: 3579 return self.sql( 3580 exp.func( 3581 "DATEDIFF", 3582 expression.this, 3583 exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE), 3584 "day", 3585 ) 3586 ) 3587 3588 def lastday_sql(self, expression: exp.LastDay) -> str: 3589 if self.LAST_DAY_SUPPORTS_DATE_PART: 3590 return self.function_fallback_sql(expression) 3591 3592 unit = expression.text("unit") 3593 if unit and unit != "MONTH": 3594 self.unsupported("Date parts are not supported in LAST_DAY.") 3595 3596 return self.func("LAST_DAY", expression.this) 3597 3598 def dateadd_sql(self, expression: exp.DateAdd) -> str: 3599 from sqlglot.dialects.dialect import unit_to_str 3600 3601 return self.func( 3602 "DATE_ADD", expression.this, expression.expression, unit_to_str(expression) 3603 ) 3604 3605 def arrayany_sql(self, expression: exp.ArrayAny) -> str: 3606 if self.CAN_IMPLEMENT_ARRAY_ANY: 3607 filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression) 3608 filtered_not_empty = exp.ArraySize(this=filtered).neq(0) 3609 original_is_empty = exp.ArraySize(this=expression.this).eq(0) 3610 return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty))) 3611 3612 from sqlglot.dialects import Dialect 3613 3614 # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect 3615 if self.dialect.__class__ != Dialect: 3616 self.unsupported("ARRAY_ANY is unsupported") 3617 3618 return self.function_fallback_sql(expression) 3619 3620 def generateseries_sql(self, expression: exp.GenerateSeries) -> str: 3621 expression.set("is_end_exclusive", None) 3622 return self.function_fallback_sql(expression) 3623 3624 def struct_sql(self, expression: exp.Struct) -> str: 3625 expression.set( 3626 "expressions", 3627 [ 3628 exp.alias_(e.expression, e.name if e.this.is_string else e.this) 3629 if isinstance(e, exp.PropertyEQ) 3630 else e 3631 for e in expression.expressions 3632 ], 3633 ) 3634 3635 return self.function_fallback_sql(expression) 3636 3637 def partitionrange_sql(self, expression: exp.PartitionRange) -> str: 3638 low = self.sql(expression, "this") 3639 high = self.sql(expression, "expression") 3640 3641 return f"{low} TO {high}" 3642 3643 def truncatetable_sql(self, expression: exp.TruncateTable) -> str: 3644 target = "DATABASE" if expression.args.get("is_database") else "TABLE" 3645 tables = f" {self.expressions(expression)}" 3646 3647 exists = " IF EXISTS" if expression.args.get("exists") else "" 3648 3649 on_cluster = self.sql(expression, "cluster") 3650 on_cluster = f" {on_cluster}" if on_cluster else "" 3651 3652 identity = self.sql(expression, "identity") 3653 identity = f" {identity} IDENTITY" if identity else "" 3654 3655 option = self.sql(expression, "option") 3656 option = f" {option}" if option else "" 3657 3658 partition = self.sql(expression, "partition") 3659 partition = f" {partition}" if partition else "" 3660 3661 return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}" 3662 3663 # This transpiles T-SQL's CONVERT function 3664 # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16 3665 def convert_sql(self, expression: exp.Convert) -> str: 3666 to = expression.this 3667 value = expression.expression 3668 style = expression.args.get("style") 3669 safe = expression.args.get("safe") 3670 strict = expression.args.get("strict") 3671 3672 if not to or not value: 3673 return "" 3674 3675 # Retrieve length of datatype and override to default if not specified 3676 if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3677 to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False) 3678 3679 transformed: t.Optional[exp.Expression] = None 3680 cast = exp.Cast if strict else exp.TryCast 3681 3682 # Check whether a conversion with format (T-SQL calls this 'style') is applicable 3683 if isinstance(style, exp.Literal) and style.is_int: 3684 from sqlglot.dialects.tsql import TSQL 3685 3686 style_value = style.name 3687 converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value) 3688 if not converted_style: 3689 self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}") 3690 3691 fmt = exp.Literal.string(converted_style) 3692 3693 if to.this == exp.DataType.Type.DATE: 3694 transformed = exp.StrToDate(this=value, format=fmt) 3695 elif to.this == exp.DataType.Type.DATETIME: 3696 transformed = exp.StrToTime(this=value, format=fmt) 3697 elif to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3698 transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe) 3699 elif to.this == exp.DataType.Type.TEXT: 3700 transformed = exp.TimeToStr(this=value, format=fmt) 3701 3702 if not transformed: 3703 transformed = cast(this=value, to=to, safe=safe) 3704 3705 return self.sql(transformed) 3706 3707 def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str: 3708 this = expression.this 3709 if isinstance(this, exp.JSONPathWildcard): 3710 this = self.json_path_part(this) 3711 return f".{this}" if this else "" 3712 3713 if exp.SAFE_IDENTIFIER_RE.match(this): 3714 return f".{this}" 3715 3716 this = self.json_path_part(this) 3717 return f"[{this}]" if self.JSON_PATH_BRACKETED_KEY_SUPPORTED else f".{this}" 3718 3719 def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str: 3720 this = self.json_path_part(expression.this) 3721 return f"[{this}]" if this else "" 3722 3723 def _simplify_unless_literal(self, expression: E) -> E: 3724 if not isinstance(expression, exp.Literal): 3725 from sqlglot.optimizer.simplify import simplify 3726 3727 expression = simplify(expression, dialect=self.dialect) 3728 3729 return expression 3730 3731 def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str: 3732 if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"): 3733 # The first modifier here will be the one closest to the AggFunc's arg 3734 mods = sorted( 3735 expression.find_all(exp.HavingMax, exp.Order, exp.Limit), 3736 key=lambda x: 0 3737 if isinstance(x, exp.HavingMax) 3738 else (1 if isinstance(x, exp.Order) else 2), 3739 ) 3740 3741 if mods: 3742 mod = mods[0] 3743 this = expression.__class__(this=mod.this.copy()) 3744 this.meta["inline"] = True 3745 mod.this.replace(this) 3746 return self.sql(expression.this) 3747 3748 agg_func = expression.find(exp.AggFunc) 3749 3750 if agg_func: 3751 return self.sql(agg_func)[:-1] + f" {text})" 3752 3753 return f"{self.sql(expression, 'this')} {text}" 3754 3755 def _replace_line_breaks(self, string: str) -> str: 3756 """We don't want to extra indent line breaks so we temporarily replace them with sentinels.""" 3757 if self.pretty: 3758 return string.replace("\n", self.SENTINEL_LINE_BREAK) 3759 return string
logger =
<Logger sqlglot (WARNING)>
ESCAPED_UNICODE_RE =
re.compile('\\\\(\\d+)')
class
Generator:
37class Generator(metaclass=_Generator): 38 """ 39 Generator converts a given syntax tree to the corresponding SQL string. 40 41 Args: 42 pretty: Whether to format the produced SQL string. 43 Default: False. 44 identify: Determines when an identifier should be quoted. Possible values are: 45 False (default): Never quote, except in cases where it's mandatory by the dialect. 46 True or 'always': Always quote. 47 'safe': Only quote identifiers that are case insensitive. 48 normalize: Whether to normalize identifiers to lowercase. 49 Default: False. 50 pad: The pad size in a formatted string. For example, this affects the indentation of 51 a projection in a query, relative to its nesting level. 52 Default: 2. 53 indent: The indentation size in a formatted string. For example, this affects the 54 indentation of subqueries and filters under a `WHERE` clause. 55 Default: 2. 56 normalize_functions: How to normalize function names. Possible values are: 57 "upper" or True (default): Convert names to uppercase. 58 "lower": Convert names to lowercase. 59 False: Disables function name normalization. 60 unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. 61 Default ErrorLevel.WARN. 62 max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. 63 This is only relevant if unsupported_level is ErrorLevel.RAISE. 64 Default: 3 65 leading_comma: Whether the comma is leading or trailing in select expressions. 66 This is only relevant when generating in pretty mode. 67 Default: False 68 max_text_width: The max number of characters in a segment before creating new lines in pretty mode. 69 The default is on the smaller end because the length only represents a segment and not the true 70 line length. 71 Default: 80 72 comments: Whether to preserve comments in the output SQL code. 73 Default: True 74 """ 75 76 TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = { 77 **JSON_PATH_PART_TRANSFORMS, 78 exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}", 79 exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}", 80 exp.CaseSpecificColumnConstraint: lambda _, 81 e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC", 82 exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}", 83 exp.CharacterSetProperty: lambda self, 84 e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}", 85 exp.ClusteredColumnConstraint: lambda self, 86 e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})", 87 exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}", 88 exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}", 89 exp.CopyGrantsProperty: lambda *_: "COPY GRANTS", 90 exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}", 91 exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}", 92 exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}", 93 exp.EphemeralColumnConstraint: lambda self, 94 e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}", 95 exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}", 96 exp.ExecuteAsProperty: lambda self, e: self.naked_property(e), 97 exp.ExternalProperty: lambda *_: "EXTERNAL", 98 exp.GlobalProperty: lambda *_: "GLOBAL", 99 exp.HeapProperty: lambda *_: "HEAP", 100 exp.IcebergProperty: lambda *_: "ICEBERG", 101 exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})", 102 exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}", 103 exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}", 104 exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}", 105 exp.JSONExtract: lambda self, e: self.func( 106 "JSON_EXTRACT", e.this, e.expression, *e.expressions 107 ), 108 exp.JSONExtractScalar: lambda self, e: self.func( 109 "JSON_EXTRACT_SCALAR", e.this, e.expression, *e.expressions 110 ), 111 exp.LanguageProperty: lambda self, e: self.naked_property(e), 112 exp.LocationProperty: lambda self, e: self.naked_property(e), 113 exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG", 114 exp.MaterializedProperty: lambda *_: "MATERIALIZED", 115 exp.NonClusteredColumnConstraint: lambda self, 116 e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})", 117 exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX", 118 exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION", 119 exp.OnCommitProperty: lambda _, 120 e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS", 121 exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}", 122 exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}", 123 exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}", 124 exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}", 125 exp.RemoteWithConnectionModelProperty: lambda self, 126 e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}", 127 exp.ReturnsProperty: lambda self, e: self.naked_property(e), 128 exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}", 129 exp.SetConfigProperty: lambda self, e: self.sql(e, "this"), 130 exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET", 131 exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}", 132 exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}", 133 exp.SqlReadWriteProperty: lambda _, e: e.name, 134 exp.SqlSecurityProperty: lambda _, 135 e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}", 136 exp.StabilityProperty: lambda _, e: e.name, 137 exp.TemporaryProperty: lambda *_: "TEMPORARY", 138 exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}", 139 exp.Timestamp: lambda self, e: self.func("TIMESTAMP", e.this, e.expression), 140 exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}", 141 exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}", 142 exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions), 143 exp.TransientProperty: lambda *_: "TRANSIENT", 144 exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE", 145 exp.UnloggedProperty: lambda *_: "UNLOGGED", 146 exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]), 147 exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}", 148 exp.VolatileProperty: lambda *_: "VOLATILE", 149 exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}", 150 exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}", 151 } 152 153 # Whether null ordering is supported in order by 154 # True: Full Support, None: No support, False: No support in window specifications 155 NULL_ORDERING_SUPPORTED: t.Optional[bool] = True 156 157 # Whether ignore nulls is inside the agg or outside. 158 # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER 159 IGNORE_NULLS_IN_FUNC = False 160 161 # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported 162 LOCKING_READS_SUPPORTED = False 163 164 # Always do union distinct or union all 165 EXPLICIT_UNION = False 166 167 # Wrap derived values in parens, usually standard but spark doesn't support it 168 WRAP_DERIVED_VALUES = True 169 170 # Whether create function uses an AS before the RETURN 171 CREATE_FUNCTION_RETURN_AS = True 172 173 # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed 174 MATCHED_BY_SOURCE = True 175 176 # Whether the INTERVAL expression works only with values like '1 day' 177 SINGLE_STRING_INTERVAL = False 178 179 # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs 180 INTERVAL_ALLOWS_PLURAL_FORM = True 181 182 # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH") 183 LIMIT_FETCH = "ALL" 184 185 # Whether limit and fetch allows expresions or just limits 186 LIMIT_ONLY_LITERALS = False 187 188 # Whether a table is allowed to be renamed with a db 189 RENAME_TABLE_WITH_DB = True 190 191 # The separator for grouping sets and rollups 192 GROUPINGS_SEP = "," 193 194 # The string used for creating an index on a table 195 INDEX_ON = "ON" 196 197 # Whether join hints should be generated 198 JOIN_HINTS = True 199 200 # Whether table hints should be generated 201 TABLE_HINTS = True 202 203 # Whether query hints should be generated 204 QUERY_HINTS = True 205 206 # What kind of separator to use for query hints 207 QUERY_HINT_SEP = ", " 208 209 # Whether comparing against booleans (e.g. x IS TRUE) is supported 210 IS_BOOL_ALLOWED = True 211 212 # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement 213 DUPLICATE_KEY_UPDATE_WITH_SET = True 214 215 # Whether to generate the limit as TOP <value> instead of LIMIT <value> 216 LIMIT_IS_TOP = False 217 218 # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ... 219 RETURNING_END = True 220 221 # Whether to generate the (+) suffix for columns used in old-style join conditions 222 COLUMN_JOIN_MARKS_SUPPORTED = False 223 224 # Whether to generate an unquoted value for EXTRACT's date part argument 225 EXTRACT_ALLOWS_QUOTES = True 226 227 # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax 228 TZ_TO_WITH_TIME_ZONE = False 229 230 # Whether the NVL2 function is supported 231 NVL2_SUPPORTED = True 232 233 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax 234 SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE") 235 236 # Whether VALUES statements can be used as derived tables. 237 # MySQL 5 and Redshift do not allow this, so when False, it will convert 238 # SELECT * VALUES into SELECT UNION 239 VALUES_AS_TABLE = True 240 241 # Whether the word COLUMN is included when adding a column with ALTER TABLE 242 ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True 243 244 # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery) 245 UNNEST_WITH_ORDINALITY = True 246 247 # Whether FILTER (WHERE cond) can be used for conditional aggregation 248 AGGREGATE_FILTER_SUPPORTED = True 249 250 # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds 251 SEMI_ANTI_JOIN_WITH_SIDE = True 252 253 # Whether to include the type of a computed column in the CREATE DDL 254 COMPUTED_COLUMN_WITH_TYPE = True 255 256 # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY 257 SUPPORTS_TABLE_COPY = True 258 259 # Whether parentheses are required around the table sample's expression 260 TABLESAMPLE_REQUIRES_PARENS = True 261 262 # Whether a table sample clause's size needs to be followed by the ROWS keyword 263 TABLESAMPLE_SIZE_IS_ROWS = True 264 265 # The keyword(s) to use when generating a sample clause 266 TABLESAMPLE_KEYWORDS = "TABLESAMPLE" 267 268 # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI 269 TABLESAMPLE_WITH_METHOD = True 270 271 # The keyword to use when specifying the seed of a sample clause 272 TABLESAMPLE_SEED_KEYWORD = "SEED" 273 274 # Whether COLLATE is a function instead of a binary operator 275 COLLATE_IS_FUNC = False 276 277 # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle) 278 DATA_TYPE_SPECIFIERS_ALLOWED = False 279 280 # Whether conditions require booleans WHERE x = 0 vs WHERE x 281 ENSURE_BOOLS = False 282 283 # Whether the "RECURSIVE" keyword is required when defining recursive CTEs 284 CTE_RECURSIVE_KEYWORD_REQUIRED = True 285 286 # Whether CONCAT requires >1 arguments 287 SUPPORTS_SINGLE_ARG_CONCAT = True 288 289 # Whether LAST_DAY function supports a date part argument 290 LAST_DAY_SUPPORTS_DATE_PART = True 291 292 # Whether named columns are allowed in table aliases 293 SUPPORTS_TABLE_ALIAS_COLUMNS = True 294 295 # Whether UNPIVOT aliases are Identifiers (False means they're Literals) 296 UNPIVOT_ALIASES_ARE_IDENTIFIERS = True 297 298 # What delimiter to use for separating JSON key/value pairs 299 JSON_KEY_VALUE_PAIR_SEP = ":" 300 301 # INSERT OVERWRITE TABLE x override 302 INSERT_OVERWRITE = " OVERWRITE TABLE" 303 304 # Whether the SELECT .. INTO syntax is used instead of CTAS 305 SUPPORTS_SELECT_INTO = False 306 307 # Whether UNLOGGED tables can be created 308 SUPPORTS_UNLOGGED_TABLES = False 309 310 # Whether the CREATE TABLE LIKE statement is supported 311 SUPPORTS_CREATE_TABLE_LIKE = True 312 313 # Whether the LikeProperty needs to be specified inside of the schema clause 314 LIKE_PROPERTY_INSIDE_SCHEMA = False 315 316 # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be 317 # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args 318 MULTI_ARG_DISTINCT = True 319 320 # Whether the JSON extraction operators expect a value of type JSON 321 JSON_TYPE_REQUIRED_FOR_EXTRACTION = False 322 323 # Whether bracketed keys like ["foo"] are supported in JSON paths 324 JSON_PATH_BRACKETED_KEY_SUPPORTED = True 325 326 # Whether to escape keys using single quotes in JSON paths 327 JSON_PATH_SINGLE_QUOTE_ESCAPE = False 328 329 # The JSONPathPart expressions supported by this dialect 330 SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy() 331 332 # Whether any(f(x) for x in array) can be implemented by this dialect 333 CAN_IMPLEMENT_ARRAY_ANY = False 334 335 # Whether the function TO_NUMBER is supported 336 SUPPORTS_TO_NUMBER = True 337 338 # Whether or not union modifiers apply to the outer union or select. 339 # SELECT * FROM x UNION SELECT * FROM y LIMIT 1 340 # True means limit 1 happens after the union, False means it it happens on y. 341 OUTER_UNION_MODIFIERS = True 342 343 TYPE_MAPPING = { 344 exp.DataType.Type.NCHAR: "CHAR", 345 exp.DataType.Type.NVARCHAR: "VARCHAR", 346 exp.DataType.Type.MEDIUMTEXT: "TEXT", 347 exp.DataType.Type.LONGTEXT: "TEXT", 348 exp.DataType.Type.TINYTEXT: "TEXT", 349 exp.DataType.Type.MEDIUMBLOB: "BLOB", 350 exp.DataType.Type.LONGBLOB: "BLOB", 351 exp.DataType.Type.TINYBLOB: "BLOB", 352 exp.DataType.Type.INET: "INET", 353 exp.DataType.Type.ROWVERSION: "VARBINARY", 354 } 355 356 STAR_MAPPING = { 357 "except": "EXCEPT", 358 "replace": "REPLACE", 359 } 360 361 TIME_PART_SINGULARS = { 362 "MICROSECONDS": "MICROSECOND", 363 "SECONDS": "SECOND", 364 "MINUTES": "MINUTE", 365 "HOURS": "HOUR", 366 "DAYS": "DAY", 367 "WEEKS": "WEEK", 368 "MONTHS": "MONTH", 369 "QUARTERS": "QUARTER", 370 "YEARS": "YEAR", 371 } 372 373 AFTER_HAVING_MODIFIER_TRANSFORMS = { 374 "cluster": lambda self, e: self.sql(e, "cluster"), 375 "distribute": lambda self, e: self.sql(e, "distribute"), 376 "qualify": lambda self, e: self.sql(e, "qualify"), 377 "sort": lambda self, e: self.sql(e, "sort"), 378 "windows": lambda self, e: ( 379 self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True) 380 if e.args.get("windows") 381 else "" 382 ), 383 } 384 385 TOKEN_MAPPING: t.Dict[TokenType, str] = {} 386 387 STRUCT_DELIMITER = ("<", ">") 388 389 PARAMETER_TOKEN = "@" 390 NAMED_PLACEHOLDER_TOKEN = ":" 391 392 PROPERTIES_LOCATION = { 393 exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE, 394 exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA, 395 exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA, 396 exp.BackupProperty: exp.Properties.Location.POST_SCHEMA, 397 exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME, 398 exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA, 399 exp.ChecksumProperty: exp.Properties.Location.POST_NAME, 400 exp.CollateProperty: exp.Properties.Location.POST_SCHEMA, 401 exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA, 402 exp.Cluster: exp.Properties.Location.POST_SCHEMA, 403 exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA, 404 exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME, 405 exp.DefinerProperty: exp.Properties.Location.POST_CREATE, 406 exp.DictRange: exp.Properties.Location.POST_SCHEMA, 407 exp.DictProperty: exp.Properties.Location.POST_SCHEMA, 408 exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA, 409 exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA, 410 exp.EngineProperty: exp.Properties.Location.POST_SCHEMA, 411 exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA, 412 exp.ExternalProperty: exp.Properties.Location.POST_CREATE, 413 exp.FallbackProperty: exp.Properties.Location.POST_NAME, 414 exp.FileFormatProperty: exp.Properties.Location.POST_WITH, 415 exp.FreespaceProperty: exp.Properties.Location.POST_NAME, 416 exp.GlobalProperty: exp.Properties.Location.POST_CREATE, 417 exp.HeapProperty: exp.Properties.Location.POST_WITH, 418 exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA, 419 exp.IcebergProperty: exp.Properties.Location.POST_CREATE, 420 exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA, 421 exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME, 422 exp.JournalProperty: exp.Properties.Location.POST_NAME, 423 exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA, 424 exp.LikeProperty: exp.Properties.Location.POST_SCHEMA, 425 exp.LocationProperty: exp.Properties.Location.POST_SCHEMA, 426 exp.LockProperty: exp.Properties.Location.POST_SCHEMA, 427 exp.LockingProperty: exp.Properties.Location.POST_ALIAS, 428 exp.LogProperty: exp.Properties.Location.POST_NAME, 429 exp.MaterializedProperty: exp.Properties.Location.POST_CREATE, 430 exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME, 431 exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION, 432 exp.OnProperty: exp.Properties.Location.POST_SCHEMA, 433 exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION, 434 exp.Order: exp.Properties.Location.POST_SCHEMA, 435 exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA, 436 exp.PartitionedByProperty: exp.Properties.Location.POST_WITH, 437 exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA, 438 exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA, 439 exp.Property: exp.Properties.Location.POST_WITH, 440 exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA, 441 exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA, 442 exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA, 443 exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA, 444 exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA, 445 exp.SampleProperty: exp.Properties.Location.POST_SCHEMA, 446 exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA, 447 exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA, 448 exp.Set: exp.Properties.Location.POST_SCHEMA, 449 exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA, 450 exp.SetProperty: exp.Properties.Location.POST_CREATE, 451 exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA, 452 exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION, 453 exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION, 454 exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA, 455 exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA, 456 exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE, 457 exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA, 458 exp.TemporaryProperty: exp.Properties.Location.POST_CREATE, 459 exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA, 460 exp.TransientProperty: exp.Properties.Location.POST_CREATE, 461 exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA, 462 exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA, 463 exp.UnloggedProperty: exp.Properties.Location.POST_CREATE, 464 exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA, 465 exp.VolatileProperty: exp.Properties.Location.POST_CREATE, 466 exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION, 467 exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME, 468 exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA, 469 } 470 471 # Keywords that can't be used as unquoted identifier names 472 RESERVED_KEYWORDS: t.Set[str] = set() 473 474 # Expressions whose comments are separated from them for better formatting 475 WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 476 exp.Create, 477 exp.Delete, 478 exp.Drop, 479 exp.From, 480 exp.Insert, 481 exp.Join, 482 exp.Select, 483 exp.Union, 484 exp.Update, 485 exp.Where, 486 exp.With, 487 ) 488 489 # Expressions that should not have their comments generated in maybe_comment 490 EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 491 exp.Binary, 492 exp.Union, 493 ) 494 495 # Expressions that can remain unwrapped when appearing in the context of an INTERVAL 496 UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = ( 497 exp.Column, 498 exp.Literal, 499 exp.Neg, 500 exp.Paren, 501 ) 502 503 PARAMETERIZABLE_TEXT_TYPES = { 504 exp.DataType.Type.NVARCHAR, 505 exp.DataType.Type.VARCHAR, 506 exp.DataType.Type.CHAR, 507 exp.DataType.Type.NCHAR, 508 } 509 510 # Expressions that need to have all CTEs under them bubbled up to them 511 EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set() 512 513 SENTINEL_LINE_BREAK = "__SQLGLOT__LB__" 514 515 __slots__ = ( 516 "pretty", 517 "identify", 518 "normalize", 519 "pad", 520 "_indent", 521 "normalize_functions", 522 "unsupported_level", 523 "max_unsupported", 524 "leading_comma", 525 "max_text_width", 526 "comments", 527 "dialect", 528 "unsupported_messages", 529 "_escaped_quote_end", 530 "_escaped_identifier_end", 531 ) 532 533 def __init__( 534 self, 535 pretty: t.Optional[bool] = None, 536 identify: str | bool = False, 537 normalize: bool = False, 538 pad: int = 2, 539 indent: int = 2, 540 normalize_functions: t.Optional[str | bool] = None, 541 unsupported_level: ErrorLevel = ErrorLevel.WARN, 542 max_unsupported: int = 3, 543 leading_comma: bool = False, 544 max_text_width: int = 80, 545 comments: bool = True, 546 dialect: DialectType = None, 547 ): 548 import sqlglot 549 from sqlglot.dialects import Dialect 550 551 self.pretty = pretty if pretty is not None else sqlglot.pretty 552 self.identify = identify 553 self.normalize = normalize 554 self.pad = pad 555 self._indent = indent 556 self.unsupported_level = unsupported_level 557 self.max_unsupported = max_unsupported 558 self.leading_comma = leading_comma 559 self.max_text_width = max_text_width 560 self.comments = comments 561 self.dialect = Dialect.get_or_raise(dialect) 562 563 # This is both a Dialect property and a Generator argument, so we prioritize the latter 564 self.normalize_functions = ( 565 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 566 ) 567 568 self.unsupported_messages: t.List[str] = [] 569 self._escaped_quote_end: str = ( 570 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 571 ) 572 self._escaped_identifier_end: str = ( 573 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 574 ) 575 576 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 577 """ 578 Generates the SQL string corresponding to the given syntax tree. 579 580 Args: 581 expression: The syntax tree. 582 copy: Whether to copy the expression. The generator performs mutations so 583 it is safer to copy. 584 585 Returns: 586 The SQL string corresponding to `expression`. 587 """ 588 if copy: 589 expression = expression.copy() 590 591 expression = self.preprocess(expression) 592 593 self.unsupported_messages = [] 594 sql = self.sql(expression).strip() 595 596 if self.pretty: 597 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 598 599 if self.unsupported_level == ErrorLevel.IGNORE: 600 return sql 601 602 if self.unsupported_level == ErrorLevel.WARN: 603 for msg in self.unsupported_messages: 604 logger.warning(msg) 605 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 606 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 607 608 return sql 609 610 def preprocess(self, expression: exp.Expression) -> exp.Expression: 611 """Apply generic preprocessing transformations to a given expression.""" 612 if ( 613 not expression.parent 614 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 615 and any(node.parent is not expression for node in expression.find_all(exp.With)) 616 ): 617 from sqlglot.transforms import move_ctes_to_top_level 618 619 expression = move_ctes_to_top_level(expression) 620 621 if self.ENSURE_BOOLS: 622 from sqlglot.transforms import ensure_bools 623 624 expression = ensure_bools(expression) 625 626 return expression 627 628 def unsupported(self, message: str) -> None: 629 if self.unsupported_level == ErrorLevel.IMMEDIATE: 630 raise UnsupportedError(message) 631 self.unsupported_messages.append(message) 632 633 def sep(self, sep: str = " ") -> str: 634 return f"{sep.strip()}\n" if self.pretty else sep 635 636 def seg(self, sql: str, sep: str = " ") -> str: 637 return f"{self.sep(sep)}{sql}" 638 639 def pad_comment(self, comment: str) -> str: 640 comment = " " + comment if comment[0].strip() else comment 641 comment = comment + " " if comment[-1].strip() else comment 642 return comment 643 644 def maybe_comment( 645 self, 646 sql: str, 647 expression: t.Optional[exp.Expression] = None, 648 comments: t.Optional[t.List[str]] = None, 649 separated: bool = False, 650 ) -> str: 651 comments = ( 652 ((expression and expression.comments) if comments is None else comments) # type: ignore 653 if self.comments 654 else None 655 ) 656 657 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 658 return sql 659 660 comments_sql = " ".join( 661 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 662 ) 663 664 if not comments_sql: 665 return sql 666 667 comments_sql = self._replace_line_breaks(comments_sql) 668 669 if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS): 670 return ( 671 f"{self.sep()}{comments_sql}{sql}" 672 if not sql or sql[0].isspace() 673 else f"{comments_sql}{self.sep()}{sql}" 674 ) 675 676 return f"{sql} {comments_sql}" 677 678 def wrap(self, expression: exp.Expression | str) -> str: 679 this_sql = self.indent( 680 ( 681 self.sql(expression) 682 if isinstance(expression, exp.UNWRAPPED_QUERIES) 683 else self.sql(expression, "this") 684 ), 685 level=1, 686 pad=0, 687 ) 688 return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}" 689 690 def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str: 691 original = self.identify 692 self.identify = False 693 result = func(*args, **kwargs) 694 self.identify = original 695 return result 696 697 def normalize_func(self, name: str) -> str: 698 if self.normalize_functions == "upper" or self.normalize_functions is True: 699 return name.upper() 700 if self.normalize_functions == "lower": 701 return name.lower() 702 return name 703 704 def indent( 705 self, 706 sql: str, 707 level: int = 0, 708 pad: t.Optional[int] = None, 709 skip_first: bool = False, 710 skip_last: bool = False, 711 ) -> str: 712 if not self.pretty: 713 return sql 714 715 pad = self.pad if pad is None else pad 716 lines = sql.split("\n") 717 718 return "\n".join( 719 ( 720 line 721 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 722 else f"{' ' * (level * self._indent + pad)}{line}" 723 ) 724 for i, line in enumerate(lines) 725 ) 726 727 def sql( 728 self, 729 expression: t.Optional[str | exp.Expression], 730 key: t.Optional[str] = None, 731 comment: bool = True, 732 ) -> str: 733 if not expression: 734 return "" 735 736 if isinstance(expression, str): 737 return expression 738 739 if key: 740 value = expression.args.get(key) 741 if value: 742 return self.sql(value) 743 return "" 744 745 transform = self.TRANSFORMS.get(expression.__class__) 746 747 if callable(transform): 748 sql = transform(self, expression) 749 elif isinstance(expression, exp.Expression): 750 exp_handler_name = f"{expression.key}_sql" 751 752 if hasattr(self, exp_handler_name): 753 sql = getattr(self, exp_handler_name)(expression) 754 elif isinstance(expression, exp.Func): 755 sql = self.function_fallback_sql(expression) 756 elif isinstance(expression, exp.Property): 757 sql = self.property_sql(expression) 758 else: 759 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 760 else: 761 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 762 763 return self.maybe_comment(sql, expression) if self.comments and comment else sql 764 765 def uncache_sql(self, expression: exp.Uncache) -> str: 766 table = self.sql(expression, "this") 767 exists_sql = " IF EXISTS" if expression.args.get("exists") else "" 768 return f"UNCACHE TABLE{exists_sql} {table}" 769 770 def cache_sql(self, expression: exp.Cache) -> str: 771 lazy = " LAZY" if expression.args.get("lazy") else "" 772 table = self.sql(expression, "this") 773 options = expression.args.get("options") 774 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 775 sql = self.sql(expression, "expression") 776 sql = f" AS{self.sep()}{sql}" if sql else "" 777 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 778 return self.prepend_ctes(expression, sql) 779 780 def characterset_sql(self, expression: exp.CharacterSet) -> str: 781 if isinstance(expression.parent, exp.Cast): 782 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 783 default = "DEFAULT " if expression.args.get("default") else "" 784 return f"{default}CHARACTER SET={self.sql(expression, 'this')}" 785 786 def column_parts(self, expression: exp.Column) -> str: 787 return ".".join( 788 self.sql(part) 789 for part in ( 790 expression.args.get("catalog"), 791 expression.args.get("db"), 792 expression.args.get("table"), 793 expression.args.get("this"), 794 ) 795 if part 796 ) 797 798 def column_sql(self, expression: exp.Column) -> str: 799 join_mark = " (+)" if expression.args.get("join_mark") else "" 800 801 if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED: 802 join_mark = "" 803 self.unsupported("Outer join syntax using the (+) operator is not supported.") 804 805 return f"{self.column_parts(expression)}{join_mark}" 806 807 def columnposition_sql(self, expression: exp.ColumnPosition) -> str: 808 this = self.sql(expression, "this") 809 this = f" {this}" if this else "" 810 position = self.sql(expression, "position") 811 return f"{position}{this}" 812 813 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 814 column = self.sql(expression, "this") 815 kind = self.sql(expression, "kind") 816 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 817 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 818 kind = f"{sep}{kind}" if kind else "" 819 constraints = f" {constraints}" if constraints else "" 820 position = self.sql(expression, "position") 821 position = f" {position}" if position else "" 822 823 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 824 kind = "" 825 826 return f"{exists}{column}{kind}{constraints}{position}" 827 828 def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str: 829 this = self.sql(expression, "this") 830 kind_sql = self.sql(expression, "kind").strip() 831 return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql 832 833 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 834 this = self.sql(expression, "this") 835 if expression.args.get("not_null"): 836 persisted = " PERSISTED NOT NULL" 837 elif expression.args.get("persisted"): 838 persisted = " PERSISTED" 839 else: 840 persisted = "" 841 return f"AS {this}{persisted}" 842 843 def autoincrementcolumnconstraint_sql(self, _) -> str: 844 return self.token_sql(TokenType.AUTO_INCREMENT) 845 846 def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str: 847 if isinstance(expression.this, list): 848 this = self.wrap(self.expressions(expression, key="this", flat=True)) 849 else: 850 this = self.sql(expression, "this") 851 852 return f"COMPRESS {this}" 853 854 def generatedasidentitycolumnconstraint_sql( 855 self, expression: exp.GeneratedAsIdentityColumnConstraint 856 ) -> str: 857 this = "" 858 if expression.this is not None: 859 on_null = " ON NULL" if expression.args.get("on_null") else "" 860 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 861 862 start = expression.args.get("start") 863 start = f"START WITH {start}" if start else "" 864 increment = expression.args.get("increment") 865 increment = f" INCREMENT BY {increment}" if increment else "" 866 minvalue = expression.args.get("minvalue") 867 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 868 maxvalue = expression.args.get("maxvalue") 869 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 870 cycle = expression.args.get("cycle") 871 cycle_sql = "" 872 873 if cycle is not None: 874 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 875 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 876 877 sequence_opts = "" 878 if start or increment or cycle_sql: 879 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 880 sequence_opts = f" ({sequence_opts.strip()})" 881 882 expr = self.sql(expression, "expression") 883 expr = f"({expr})" if expr else "IDENTITY" 884 885 return f"GENERATED{this} AS {expr}{sequence_opts}" 886 887 def generatedasrowcolumnconstraint_sql( 888 self, expression: exp.GeneratedAsRowColumnConstraint 889 ) -> str: 890 start = "START" if expression.args.get("start") else "END" 891 hidden = " HIDDEN" if expression.args.get("hidden") else "" 892 return f"GENERATED ALWAYS AS ROW {start}{hidden}" 893 894 def periodforsystemtimeconstraint_sql( 895 self, expression: exp.PeriodForSystemTimeConstraint 896 ) -> str: 897 return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})" 898 899 def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str: 900 return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL" 901 902 def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str: 903 return f"AS {self.sql(expression, 'this')}" 904 905 def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str: 906 desc = expression.args.get("desc") 907 if desc is not None: 908 return f"PRIMARY KEY{' DESC' if desc else ' ASC'}" 909 return "PRIMARY KEY" 910 911 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 912 this = self.sql(expression, "this") 913 this = f" {this}" if this else "" 914 index_type = expression.args.get("index_type") 915 index_type = f" USING {index_type}" if index_type else "" 916 on_conflict = self.sql(expression, "on_conflict") 917 on_conflict = f" {on_conflict}" if on_conflict else "" 918 return f"UNIQUE{this}{index_type}{on_conflict}" 919 920 def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str: 921 return self.sql(expression, "this") 922 923 def create_sql(self, expression: exp.Create) -> str: 924 kind = self.sql(expression, "kind") 925 properties = expression.args.get("properties") 926 properties_locs = self.locate_properties(properties) if properties else defaultdict() 927 928 this = self.createable_sql(expression, properties_locs) 929 930 properties_sql = "" 931 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 932 exp.Properties.Location.POST_WITH 933 ): 934 properties_sql = self.sql( 935 exp.Properties( 936 expressions=[ 937 *properties_locs[exp.Properties.Location.POST_SCHEMA], 938 *properties_locs[exp.Properties.Location.POST_WITH], 939 ] 940 ) 941 ) 942 943 begin = " BEGIN" if expression.args.get("begin") else "" 944 end = " END" if expression.args.get("end") else "" 945 946 expression_sql = self.sql(expression, "expression") 947 if expression_sql: 948 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 949 950 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 951 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 952 postalias_props_sql = self.properties( 953 exp.Properties( 954 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 955 ), 956 wrapped=False, 957 ) 958 expression_sql = f" AS {postalias_props_sql}{expression_sql}" 959 else: 960 expression_sql = f" AS{expression_sql}" 961 962 postindex_props_sql = "" 963 if properties_locs.get(exp.Properties.Location.POST_INDEX): 964 postindex_props_sql = self.properties( 965 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 966 wrapped=False, 967 prefix=" ", 968 ) 969 970 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 971 indexes = f" {indexes}" if indexes else "" 972 index_sql = indexes + postindex_props_sql 973 974 replace = " OR REPLACE" if expression.args.get("replace") else "" 975 unique = " UNIQUE" if expression.args.get("unique") else "" 976 977 postcreate_props_sql = "" 978 if properties_locs.get(exp.Properties.Location.POST_CREATE): 979 postcreate_props_sql = self.properties( 980 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 981 sep=" ", 982 prefix=" ", 983 wrapped=False, 984 ) 985 986 modifiers = "".join((replace, unique, postcreate_props_sql)) 987 988 postexpression_props_sql = "" 989 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 990 postexpression_props_sql = self.properties( 991 exp.Properties( 992 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 993 ), 994 sep=" ", 995 prefix=" ", 996 wrapped=False, 997 ) 998 999 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 1000 no_schema_binding = ( 1001 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 1002 ) 1003 1004 clone = self.sql(expression, "clone") 1005 clone = f" {clone}" if clone else "" 1006 1007 expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 1008 return self.prepend_ctes(expression, expression_sql) 1009 1010 def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str: 1011 start = self.sql(expression, "start") 1012 start = f"START WITH {start}" if start else "" 1013 increment = self.sql(expression, "increment") 1014 increment = f" INCREMENT BY {increment}" if increment else "" 1015 minvalue = self.sql(expression, "minvalue") 1016 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 1017 maxvalue = self.sql(expression, "maxvalue") 1018 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 1019 owned = self.sql(expression, "owned") 1020 owned = f" OWNED BY {owned}" if owned else "" 1021 1022 cache = expression.args.get("cache") 1023 if cache is None: 1024 cache_str = "" 1025 elif cache is True: 1026 cache_str = " CACHE" 1027 else: 1028 cache_str = f" CACHE {cache}" 1029 1030 options = self.expressions(expression, key="options", flat=True, sep=" ") 1031 options = f" {options}" if options else "" 1032 1033 return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip() 1034 1035 def clone_sql(self, expression: exp.Clone) -> str: 1036 this = self.sql(expression, "this") 1037 shallow = "SHALLOW " if expression.args.get("shallow") else "" 1038 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 1039 return f"{shallow}{keyword} {this}" 1040 1041 def describe_sql(self, expression: exp.Describe) -> str: 1042 style = expression.args.get("style") 1043 style = f" {style}" if style else "" 1044 return f"DESCRIBE{style} {self.sql(expression, 'this')}" 1045 1046 def heredoc_sql(self, expression: exp.Heredoc) -> str: 1047 tag = self.sql(expression, "tag") 1048 return f"${tag}${self.sql(expression, 'this')}${tag}$" 1049 1050 def prepend_ctes(self, expression: exp.Expression, sql: str) -> str: 1051 with_ = self.sql(expression, "with") 1052 if with_: 1053 sql = f"{with_}{self.sep()}{sql}" 1054 return sql 1055 1056 def with_sql(self, expression: exp.With) -> str: 1057 sql = self.expressions(expression, flat=True) 1058 recursive = ( 1059 "RECURSIVE " 1060 if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive") 1061 else "" 1062 ) 1063 1064 return f"WITH {recursive}{sql}" 1065 1066 def cte_sql(self, expression: exp.CTE) -> str: 1067 alias = self.sql(expression, "alias") 1068 1069 materialized = expression.args.get("materialized") 1070 if materialized is False: 1071 materialized = "NOT MATERIALIZED " 1072 elif materialized: 1073 materialized = "MATERIALIZED " 1074 1075 return f"{alias} AS {materialized or ''}{self.wrap(expression)}" 1076 1077 def tablealias_sql(self, expression: exp.TableAlias) -> str: 1078 alias = self.sql(expression, "this") 1079 columns = self.expressions(expression, key="columns", flat=True) 1080 columns = f"({columns})" if columns else "" 1081 1082 if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS: 1083 columns = "" 1084 self.unsupported("Named columns are not supported in table alias.") 1085 1086 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 1087 alias = "_t" 1088 1089 return f"{alias}{columns}" 1090 1091 def bitstring_sql(self, expression: exp.BitString) -> str: 1092 this = self.sql(expression, "this") 1093 if self.dialect.BIT_START: 1094 return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}" 1095 return f"{int(this, 2)}" 1096 1097 def hexstring_sql(self, expression: exp.HexString) -> str: 1098 this = self.sql(expression, "this") 1099 if self.dialect.HEX_START: 1100 return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}" 1101 return f"{int(this, 16)}" 1102 1103 def bytestring_sql(self, expression: exp.ByteString) -> str: 1104 this = self.sql(expression, "this") 1105 if self.dialect.BYTE_START: 1106 return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}" 1107 return this 1108 1109 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 1110 this = self.sql(expression, "this") 1111 escape = expression.args.get("escape") 1112 1113 if self.dialect.UNICODE_START: 1114 escape = f" UESCAPE {self.sql(escape)}" if escape else "" 1115 return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}" 1116 1117 if escape: 1118 pattern = re.compile(rf"{escape.name}(\d+)") 1119 else: 1120 pattern = ESCAPED_UNICODE_RE 1121 1122 this = pattern.sub(r"\\u\1", this) 1123 return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}" 1124 1125 def rawstring_sql(self, expression: exp.RawString) -> str: 1126 string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False) 1127 return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}" 1128 1129 def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str: 1130 this = self.sql(expression, "this") 1131 specifier = self.sql(expression, "expression") 1132 specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else "" 1133 return f"{this}{specifier}" 1134 1135 def datatype_sql(self, expression: exp.DataType) -> str: 1136 type_value = expression.this 1137 1138 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 1139 type_sql = self.sql(expression, "kind") 1140 else: 1141 type_sql = ( 1142 self.TYPE_MAPPING.get(type_value, type_value.value) 1143 if isinstance(type_value, exp.DataType.Type) 1144 else type_value 1145 ) 1146 1147 nested = "" 1148 interior = self.expressions(expression, flat=True) 1149 values = "" 1150 1151 if interior: 1152 if expression.args.get("nested"): 1153 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 1154 if expression.args.get("values") is not None: 1155 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 1156 values = self.expressions(expression, key="values", flat=True) 1157 values = f"{delimiters[0]}{values}{delimiters[1]}" 1158 elif type_value == exp.DataType.Type.INTERVAL: 1159 nested = f" {interior}" 1160 else: 1161 nested = f"({interior})" 1162 1163 type_sql = f"{type_sql}{nested}{values}" 1164 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 1165 exp.DataType.Type.TIMETZ, 1166 exp.DataType.Type.TIMESTAMPTZ, 1167 ): 1168 type_sql = f"{type_sql} WITH TIME ZONE" 1169 1170 return type_sql 1171 1172 def directory_sql(self, expression: exp.Directory) -> str: 1173 local = "LOCAL " if expression.args.get("local") else "" 1174 row_format = self.sql(expression, "row_format") 1175 row_format = f" {row_format}" if row_format else "" 1176 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}" 1177 1178 def delete_sql(self, expression: exp.Delete) -> str: 1179 this = self.sql(expression, "this") 1180 this = f" FROM {this}" if this else "" 1181 using = self.sql(expression, "using") 1182 using = f" USING {using}" if using else "" 1183 where = self.sql(expression, "where") 1184 returning = self.sql(expression, "returning") 1185 limit = self.sql(expression, "limit") 1186 tables = self.expressions(expression, key="tables") 1187 tables = f" {tables}" if tables else "" 1188 if self.RETURNING_END: 1189 expression_sql = f"{this}{using}{where}{returning}{limit}" 1190 else: 1191 expression_sql = f"{returning}{this}{using}{where}{limit}" 1192 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}") 1193 1194 def drop_sql(self, expression: exp.Drop) -> str: 1195 this = self.sql(expression, "this") 1196 expressions = self.expressions(expression, flat=True) 1197 expressions = f" ({expressions})" if expressions else "" 1198 kind = expression.args["kind"] 1199 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1200 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1201 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1202 cascade = " CASCADE" if expression.args.get("cascade") else "" 1203 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1204 purge = " PURGE" if expression.args.get("purge") else "" 1205 return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{expressions}{cascade}{constraints}{purge}" 1206 1207 def except_sql(self, expression: exp.Except) -> str: 1208 return self.set_operations(expression) 1209 1210 def except_op(self, expression: exp.Except) -> str: 1211 return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}" 1212 1213 def fetch_sql(self, expression: exp.Fetch) -> str: 1214 direction = expression.args.get("direction") 1215 direction = f" {direction}" if direction else "" 1216 count = expression.args.get("count") 1217 count = f" {count}" if count else "" 1218 if expression.args.get("percent"): 1219 count = f"{count} PERCENT" 1220 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1221 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}" 1222 1223 def filter_sql(self, expression: exp.Filter) -> str: 1224 if self.AGGREGATE_FILTER_SUPPORTED: 1225 this = self.sql(expression, "this") 1226 where = self.sql(expression, "expression").strip() 1227 return f"{this} FILTER({where})" 1228 1229 agg = expression.this 1230 agg_arg = agg.this 1231 cond = expression.expression.this 1232 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1233 return self.sql(agg) 1234 1235 def hint_sql(self, expression: exp.Hint) -> str: 1236 if not self.QUERY_HINTS: 1237 self.unsupported("Hints are not supported") 1238 return "" 1239 1240 return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */" 1241 1242 def indexparameters_sql(self, expression: exp.IndexParameters) -> str: 1243 using = self.sql(expression, "using") 1244 using = f" USING {using}" if using else "" 1245 columns = self.expressions(expression, key="columns", flat=True) 1246 columns = f"({columns})" if columns else "" 1247 partition_by = self.expressions(expression, key="partition_by", flat=True) 1248 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1249 where = self.sql(expression, "where") 1250 include = self.expressions(expression, key="include", flat=True) 1251 if include: 1252 include = f" INCLUDE ({include})" 1253 with_storage = self.expressions(expression, key="with_storage", flat=True) 1254 with_storage = f" WITH ({with_storage})" if with_storage else "" 1255 tablespace = self.sql(expression, "tablespace") 1256 tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else "" 1257 1258 return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}" 1259 1260 def index_sql(self, expression: exp.Index) -> str: 1261 unique = "UNIQUE " if expression.args.get("unique") else "" 1262 primary = "PRIMARY " if expression.args.get("primary") else "" 1263 amp = "AMP " if expression.args.get("amp") else "" 1264 name = self.sql(expression, "this") 1265 name = f"{name} " if name else "" 1266 table = self.sql(expression, "table") 1267 table = f"{self.INDEX_ON} {table}" if table else "" 1268 1269 index = "INDEX " if not table else "" 1270 1271 params = self.sql(expression, "params") 1272 return f"{unique}{primary}{amp}{index}{name}{table}{params}" 1273 1274 def identifier_sql(self, expression: exp.Identifier) -> str: 1275 text = expression.name 1276 lower = text.lower() 1277 text = lower if self.normalize and not expression.quoted else text 1278 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1279 if ( 1280 expression.quoted 1281 or self.dialect.can_identify(text, self.identify) 1282 or lower in self.RESERVED_KEYWORDS 1283 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1284 ): 1285 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1286 return text 1287 1288 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1289 input_format = self.sql(expression, "input_format") 1290 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1291 output_format = self.sql(expression, "output_format") 1292 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1293 return self.sep().join((input_format, output_format)) 1294 1295 def national_sql(self, expression: exp.National, prefix: str = "N") -> str: 1296 string = self.sql(exp.Literal.string(expression.name)) 1297 return f"{prefix}{string}" 1298 1299 def partition_sql(self, expression: exp.Partition) -> str: 1300 return f"PARTITION({self.expressions(expression, flat=True)})" 1301 1302 def properties_sql(self, expression: exp.Properties) -> str: 1303 root_properties = [] 1304 with_properties = [] 1305 1306 for p in expression.expressions: 1307 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1308 if p_loc == exp.Properties.Location.POST_WITH: 1309 with_properties.append(p) 1310 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1311 root_properties.append(p) 1312 1313 return self.root_properties( 1314 exp.Properties(expressions=root_properties) 1315 ) + self.with_properties(exp.Properties(expressions=with_properties)) 1316 1317 def root_properties(self, properties: exp.Properties) -> str: 1318 if properties.expressions: 1319 return self.sep() + self.expressions(properties, indent=False, sep=" ") 1320 return "" 1321 1322 def properties( 1323 self, 1324 properties: exp.Properties, 1325 prefix: str = "", 1326 sep: str = ", ", 1327 suffix: str = "", 1328 wrapped: bool = True, 1329 ) -> str: 1330 if properties.expressions: 1331 expressions = self.expressions(properties, sep=sep, indent=False) 1332 if expressions: 1333 expressions = self.wrap(expressions) if wrapped else expressions 1334 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1335 return "" 1336 1337 def with_properties(self, properties: exp.Properties) -> str: 1338 return self.properties(properties, prefix=self.seg("WITH")) 1339 1340 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1341 properties_locs = defaultdict(list) 1342 for p in properties.expressions: 1343 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1344 if p_loc != exp.Properties.Location.UNSUPPORTED: 1345 properties_locs[p_loc].append(p) 1346 else: 1347 self.unsupported(f"Unsupported property {p.key}") 1348 1349 return properties_locs 1350 1351 def property_name(self, expression: exp.Property, string_key: bool = False) -> str: 1352 if isinstance(expression.this, exp.Dot): 1353 return self.sql(expression, "this") 1354 return f"'{expression.name}'" if string_key else expression.name 1355 1356 def property_sql(self, expression: exp.Property) -> str: 1357 property_cls = expression.__class__ 1358 if property_cls == exp.Property: 1359 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1360 1361 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1362 if not property_name: 1363 self.unsupported(f"Unsupported property {expression.key}") 1364 1365 return f"{property_name}={self.sql(expression, 'this')}" 1366 1367 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1368 if self.SUPPORTS_CREATE_TABLE_LIKE: 1369 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1370 options = f" {options}" if options else "" 1371 1372 like = f"LIKE {self.sql(expression, 'this')}{options}" 1373 if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema): 1374 like = f"({like})" 1375 1376 return like 1377 1378 if expression.expressions: 1379 self.unsupported("Transpilation of LIKE property options is unsupported") 1380 1381 select = exp.select("*").from_(expression.this).limit(0) 1382 return f"AS {self.sql(select)}" 1383 1384 def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str: 1385 no = "NO " if expression.args.get("no") else "" 1386 protection = " PROTECTION" if expression.args.get("protection") else "" 1387 return f"{no}FALLBACK{protection}" 1388 1389 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1390 no = "NO " if expression.args.get("no") else "" 1391 local = expression.args.get("local") 1392 local = f"{local} " if local else "" 1393 dual = "DUAL " if expression.args.get("dual") else "" 1394 before = "BEFORE " if expression.args.get("before") else "" 1395 after = "AFTER " if expression.args.get("after") else "" 1396 return f"{no}{local}{dual}{before}{after}JOURNAL" 1397 1398 def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str: 1399 freespace = self.sql(expression, "this") 1400 percent = " PERCENT" if expression.args.get("percent") else "" 1401 return f"FREESPACE={freespace}{percent}" 1402 1403 def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str: 1404 if expression.args.get("default"): 1405 property = "DEFAULT" 1406 elif expression.args.get("on"): 1407 property = "ON" 1408 else: 1409 property = "OFF" 1410 return f"CHECKSUM={property}" 1411 1412 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1413 if expression.args.get("no"): 1414 return "NO MERGEBLOCKRATIO" 1415 if expression.args.get("default"): 1416 return "DEFAULT MERGEBLOCKRATIO" 1417 1418 percent = " PERCENT" if expression.args.get("percent") else "" 1419 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}" 1420 1421 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1422 default = expression.args.get("default") 1423 minimum = expression.args.get("minimum") 1424 maximum = expression.args.get("maximum") 1425 if default or minimum or maximum: 1426 if default: 1427 prop = "DEFAULT" 1428 elif minimum: 1429 prop = "MINIMUM" 1430 else: 1431 prop = "MAXIMUM" 1432 return f"{prop} DATABLOCKSIZE" 1433 units = expression.args.get("units") 1434 units = f" {units}" if units else "" 1435 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}" 1436 1437 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1438 autotemp = expression.args.get("autotemp") 1439 always = expression.args.get("always") 1440 default = expression.args.get("default") 1441 manual = expression.args.get("manual") 1442 never = expression.args.get("never") 1443 1444 if autotemp is not None: 1445 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1446 elif always: 1447 prop = "ALWAYS" 1448 elif default: 1449 prop = "DEFAULT" 1450 elif manual: 1451 prop = "MANUAL" 1452 elif never: 1453 prop = "NEVER" 1454 return f"BLOCKCOMPRESSION={prop}" 1455 1456 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1457 no = expression.args.get("no") 1458 no = " NO" if no else "" 1459 concurrent = expression.args.get("concurrent") 1460 concurrent = " CONCURRENT" if concurrent else "" 1461 target = self.sql(expression, "target") 1462 target = f" {target}" if target else "" 1463 return f"WITH{no}{concurrent} ISOLATED LOADING{target}" 1464 1465 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1466 if isinstance(expression.this, list): 1467 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1468 if expression.this: 1469 modulus = self.sql(expression, "this") 1470 remainder = self.sql(expression, "expression") 1471 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1472 1473 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1474 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1475 return f"FROM ({from_expressions}) TO ({to_expressions})" 1476 1477 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1478 this = self.sql(expression, "this") 1479 1480 for_values_or_default = expression.expression 1481 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1482 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1483 else: 1484 for_values_or_default = " DEFAULT" 1485 1486 return f"PARTITION OF {this}{for_values_or_default}" 1487 1488 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1489 kind = expression.args.get("kind") 1490 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1491 for_or_in = expression.args.get("for_or_in") 1492 for_or_in = f" {for_or_in}" if for_or_in else "" 1493 lock_type = expression.args.get("lock_type") 1494 override = " OVERRIDE" if expression.args.get("override") else "" 1495 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}" 1496 1497 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1498 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1499 statistics = expression.args.get("statistics") 1500 statistics_sql = "" 1501 if statistics is not None: 1502 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1503 return f"{data_sql}{statistics_sql}" 1504 1505 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1506 sql = "WITH(SYSTEM_VERSIONING=ON" 1507 1508 if expression.this: 1509 history_table = self.sql(expression, "this") 1510 sql = f"{sql}(HISTORY_TABLE={history_table}" 1511 1512 if expression.expression: 1513 data_consistency_check = self.sql(expression, "expression") 1514 sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}" 1515 1516 sql = f"{sql})" 1517 1518 return f"{sql})" 1519 1520 def insert_sql(self, expression: exp.Insert) -> str: 1521 hint = self.sql(expression, "hint") 1522 overwrite = expression.args.get("overwrite") 1523 1524 if isinstance(expression.this, exp.Directory): 1525 this = " OVERWRITE" if overwrite else " INTO" 1526 else: 1527 this = self.INSERT_OVERWRITE if overwrite else " INTO" 1528 1529 stored = self.sql(expression, "stored") 1530 stored = f" {stored}" if stored else "" 1531 alternative = expression.args.get("alternative") 1532 alternative = f" OR {alternative}" if alternative else "" 1533 ignore = " IGNORE" if expression.args.get("ignore") else "" 1534 is_function = expression.args.get("is_function") 1535 if is_function: 1536 this = f"{this} FUNCTION" 1537 this = f"{this} {self.sql(expression, 'this')}" 1538 1539 exists = " IF EXISTS" if expression.args.get("exists") else "" 1540 where = self.sql(expression, "where") 1541 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1542 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1543 on_conflict = self.sql(expression, "conflict") 1544 on_conflict = f" {on_conflict}" if on_conflict else "" 1545 by_name = " BY NAME" if expression.args.get("by_name") else "" 1546 returning = self.sql(expression, "returning") 1547 1548 if self.RETURNING_END: 1549 expression_sql = f"{expression_sql}{on_conflict}{returning}" 1550 else: 1551 expression_sql = f"{returning}{expression_sql}{on_conflict}" 1552 1553 sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{where}{expression_sql}" 1554 return self.prepend_ctes(expression, sql) 1555 1556 def intersect_sql(self, expression: exp.Intersect) -> str: 1557 return self.set_operations(expression) 1558 1559 def intersect_op(self, expression: exp.Intersect) -> str: 1560 return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}" 1561 1562 def introducer_sql(self, expression: exp.Introducer) -> str: 1563 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 1564 1565 def kill_sql(self, expression: exp.Kill) -> str: 1566 kind = self.sql(expression, "kind") 1567 kind = f" {kind}" if kind else "" 1568 this = self.sql(expression, "this") 1569 this = f" {this}" if this else "" 1570 return f"KILL{kind}{this}" 1571 1572 def pseudotype_sql(self, expression: exp.PseudoType) -> str: 1573 return expression.name 1574 1575 def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str: 1576 return expression.name 1577 1578 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1579 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1580 1581 constraint = self.sql(expression, "constraint") 1582 constraint = f" ON CONSTRAINT {constraint}" if constraint else "" 1583 1584 conflict_keys = self.expressions(expression, key="conflict_keys", flat=True) 1585 conflict_keys = f"({conflict_keys}) " if conflict_keys else " " 1586 action = self.sql(expression, "action") 1587 1588 expressions = self.expressions(expression, flat=True) 1589 if expressions: 1590 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1591 expressions = f" {set_keyword}{expressions}" 1592 1593 return f"{conflict}{constraint}{conflict_keys}{action}{expressions}" 1594 1595 def returning_sql(self, expression: exp.Returning) -> str: 1596 return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}" 1597 1598 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1599 fields = self.sql(expression, "fields") 1600 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1601 escaped = self.sql(expression, "escaped") 1602 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1603 items = self.sql(expression, "collection_items") 1604 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1605 keys = self.sql(expression, "map_keys") 1606 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1607 lines = self.sql(expression, "lines") 1608 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1609 null = self.sql(expression, "null") 1610 null = f" NULL DEFINED AS {null}" if null else "" 1611 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}" 1612 1613 def withtablehint_sql(self, expression: exp.WithTableHint) -> str: 1614 return f"WITH ({self.expressions(expression, flat=True)})" 1615 1616 def indextablehint_sql(self, expression: exp.IndexTableHint) -> str: 1617 this = f"{self.sql(expression, 'this')} INDEX" 1618 target = self.sql(expression, "target") 1619 target = f" FOR {target}" if target else "" 1620 return f"{this}{target} ({self.expressions(expression, flat=True)})" 1621 1622 def historicaldata_sql(self, expression: exp.HistoricalData) -> str: 1623 this = self.sql(expression, "this") 1624 kind = self.sql(expression, "kind") 1625 expr = self.sql(expression, "expression") 1626 return f"{this} ({kind} => {expr})" 1627 1628 def table_parts(self, expression: exp.Table) -> str: 1629 return ".".join( 1630 self.sql(part) 1631 for part in ( 1632 expression.args.get("catalog"), 1633 expression.args.get("db"), 1634 expression.args.get("this"), 1635 ) 1636 if part is not None 1637 ) 1638 1639 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1640 table = self.table_parts(expression) 1641 only = "ONLY " if expression.args.get("only") else "" 1642 partition = self.sql(expression, "partition") 1643 partition = f" {partition}" if partition else "" 1644 version = self.sql(expression, "version") 1645 version = f" {version}" if version else "" 1646 alias = self.sql(expression, "alias") 1647 alias = f"{sep}{alias}" if alias else "" 1648 hints = self.expressions(expression, key="hints", sep=" ") 1649 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1650 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 1651 pivots = f" {pivots}" if pivots else "" 1652 joins = self.indent( 1653 self.expressions(expression, key="joins", sep="", flat=True), skip_first=True 1654 ) 1655 laterals = self.expressions(expression, key="laterals", sep="") 1656 1657 file_format = self.sql(expression, "format") 1658 if file_format: 1659 pattern = self.sql(expression, "pattern") 1660 pattern = f", PATTERN => {pattern}" if pattern else "" 1661 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1662 1663 ordinality = expression.args.get("ordinality") or "" 1664 if ordinality: 1665 ordinality = f" WITH ORDINALITY{alias}" 1666 alias = "" 1667 1668 when = self.sql(expression, "when") 1669 if when: 1670 table = f"{table} {when}" 1671 1672 return f"{only}{table}{partition}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}" 1673 1674 def tablesample_sql( 1675 self, 1676 expression: exp.TableSample, 1677 sep: str = " AS ", 1678 tablesample_keyword: t.Optional[str] = None, 1679 ) -> str: 1680 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1681 table = expression.this.copy() 1682 table.set("alias", None) 1683 this = self.sql(table) 1684 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1685 else: 1686 this = self.sql(expression, "this") 1687 alias = "" 1688 1689 method = self.sql(expression, "method") 1690 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1691 numerator = self.sql(expression, "bucket_numerator") 1692 denominator = self.sql(expression, "bucket_denominator") 1693 field = self.sql(expression, "bucket_field") 1694 field = f" ON {field}" if field else "" 1695 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1696 seed = self.sql(expression, "seed") 1697 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1698 1699 size = self.sql(expression, "size") 1700 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1701 size = f"{size} ROWS" 1702 1703 percent = self.sql(expression, "percent") 1704 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1705 percent = f"{percent} PERCENT" 1706 1707 expr = f"{bucket}{percent}{size}" 1708 if self.TABLESAMPLE_REQUIRES_PARENS: 1709 expr = f"({expr})" 1710 1711 return ( 1712 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1713 ) 1714 1715 def pivot_sql(self, expression: exp.Pivot) -> str: 1716 expressions = self.expressions(expression, flat=True) 1717 1718 if expression.this: 1719 this = self.sql(expression, "this") 1720 if not expressions: 1721 return f"UNPIVOT {this}" 1722 1723 on = f"{self.seg('ON')} {expressions}" 1724 using = self.expressions(expression, key="using", flat=True) 1725 using = f"{self.seg('USING')} {using}" if using else "" 1726 group = self.sql(expression, "group") 1727 return f"PIVOT {this}{on}{using}{group}" 1728 1729 alias = self.sql(expression, "alias") 1730 alias = f" AS {alias}" if alias else "" 1731 direction = "UNPIVOT" if expression.unpivot else "PIVOT" 1732 field = self.sql(expression, "field") 1733 include_nulls = expression.args.get("include_nulls") 1734 if include_nulls is not None: 1735 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1736 else: 1737 nulls = "" 1738 return f"{direction}{nulls}({expressions} FOR {field}){alias}" 1739 1740 def version_sql(self, expression: exp.Version) -> str: 1741 this = f"FOR {expression.name}" 1742 kind = expression.text("kind") 1743 expr = self.sql(expression, "expression") 1744 return f"{this} {kind} {expr}" 1745 1746 def tuple_sql(self, expression: exp.Tuple) -> str: 1747 return f"({self.expressions(expression, flat=True)})" 1748 1749 def update_sql(self, expression: exp.Update) -> str: 1750 this = self.sql(expression, "this") 1751 set_sql = self.expressions(expression, flat=True) 1752 from_sql = self.sql(expression, "from") 1753 where_sql = self.sql(expression, "where") 1754 returning = self.sql(expression, "returning") 1755 order = self.sql(expression, "order") 1756 limit = self.sql(expression, "limit") 1757 if self.RETURNING_END: 1758 expression_sql = f"{from_sql}{where_sql}{returning}" 1759 else: 1760 expression_sql = f"{returning}{from_sql}{where_sql}" 1761 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1762 return self.prepend_ctes(expression, sql) 1763 1764 def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str: 1765 values_as_table = values_as_table and self.VALUES_AS_TABLE 1766 1767 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1768 if values_as_table or not expression.find_ancestor(exp.From, exp.Join): 1769 args = self.expressions(expression) 1770 alias = self.sql(expression, "alias") 1771 values = f"VALUES{self.seg('')}{args}" 1772 values = ( 1773 f"({values})" 1774 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1775 else values 1776 ) 1777 return f"{values} AS {alias}" if alias else values 1778 1779 # Converts `VALUES...` expression into a series of select unions. 1780 alias_node = expression.args.get("alias") 1781 column_names = alias_node and alias_node.columns 1782 1783 selects: t.List[exp.Query] = [] 1784 1785 for i, tup in enumerate(expression.expressions): 1786 row = tup.expressions 1787 1788 if i == 0 and column_names: 1789 row = [ 1790 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1791 ] 1792 1793 selects.append(exp.Select(expressions=row)) 1794 1795 if self.pretty: 1796 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1797 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1798 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1799 query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1800 return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False)) 1801 1802 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1803 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1804 return f"({unions}){alias}" 1805 1806 def var_sql(self, expression: exp.Var) -> str: 1807 return self.sql(expression, "this") 1808 1809 def into_sql(self, expression: exp.Into) -> str: 1810 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1811 unlogged = " UNLOGGED" if expression.args.get("unlogged") else "" 1812 return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}" 1813 1814 def from_sql(self, expression: exp.From) -> str: 1815 return f"{self.seg('FROM')} {self.sql(expression, 'this')}" 1816 1817 def group_sql(self, expression: exp.Group) -> str: 1818 group_by_all = expression.args.get("all") 1819 if group_by_all is True: 1820 modifier = " ALL" 1821 elif group_by_all is False: 1822 modifier = " DISTINCT" 1823 else: 1824 modifier = "" 1825 1826 group_by = self.op_expressions(f"GROUP BY{modifier}", expression) 1827 1828 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1829 grouping_sets = ( 1830 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1831 ) 1832 1833 cube = expression.args.get("cube", []) 1834 if seq_get(cube, 0) is True: 1835 return f"{group_by}{self.seg('WITH CUBE')}" 1836 else: 1837 cube_sql = self.expressions(expression, key="cube", indent=False) 1838 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1839 1840 rollup = expression.args.get("rollup", []) 1841 if seq_get(rollup, 0) is True: 1842 return f"{group_by}{self.seg('WITH ROLLUP')}" 1843 else: 1844 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1845 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1846 1847 groupings = csv( 1848 grouping_sets, 1849 cube_sql, 1850 rollup_sql, 1851 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1852 sep=self.GROUPINGS_SEP, 1853 ) 1854 1855 if expression.args.get("expressions") and groupings: 1856 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1857 1858 return f"{group_by}{groupings}" 1859 1860 def having_sql(self, expression: exp.Having) -> str: 1861 this = self.indent(self.sql(expression, "this")) 1862 return f"{self.seg('HAVING')}{self.sep()}{this}" 1863 1864 def connect_sql(self, expression: exp.Connect) -> str: 1865 start = self.sql(expression, "start") 1866 start = self.seg(f"START WITH {start}") if start else "" 1867 nocycle = " NOCYCLE" if expression.args.get("nocycle") else "" 1868 connect = self.sql(expression, "connect") 1869 connect = self.seg(f"CONNECT BY{nocycle} {connect}") 1870 return start + connect 1871 1872 def prior_sql(self, expression: exp.Prior) -> str: 1873 return f"PRIOR {self.sql(expression, 'this')}" 1874 1875 def join_sql(self, expression: exp.Join) -> str: 1876 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1877 side = None 1878 else: 1879 side = expression.side 1880 1881 op_sql = " ".join( 1882 op 1883 for op in ( 1884 expression.method, 1885 "GLOBAL" if expression.args.get("global") else None, 1886 side, 1887 expression.kind, 1888 expression.hint if self.JOIN_HINTS else None, 1889 ) 1890 if op 1891 ) 1892 match_cond = self.sql(expression, "match_condition") 1893 match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else "" 1894 on_sql = self.sql(expression, "on") 1895 using = expression.args.get("using") 1896 1897 if not on_sql and using: 1898 on_sql = csv(*(self.sql(column) for column in using)) 1899 1900 this = expression.this 1901 this_sql = self.sql(this) 1902 1903 if on_sql: 1904 on_sql = self.indent(on_sql, skip_first=True) 1905 space = self.seg(" " * self.pad) if self.pretty else " " 1906 if using: 1907 on_sql = f"{space}USING ({on_sql})" 1908 else: 1909 on_sql = f"{space}ON {on_sql}" 1910 elif not op_sql: 1911 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 1912 return f" {this_sql}" 1913 1914 return f", {this_sql}" 1915 1916 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1917 return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}" 1918 1919 def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str: 1920 args = self.expressions(expression, flat=True) 1921 args = f"({args})" if len(args.split(",")) > 1 else args 1922 return f"{args} {arrow_sep} {self.sql(expression, 'this')}" 1923 1924 def lateral_op(self, expression: exp.Lateral) -> str: 1925 cross_apply = expression.args.get("cross_apply") 1926 1927 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 1928 if cross_apply is True: 1929 op = "INNER JOIN " 1930 elif cross_apply is False: 1931 op = "LEFT JOIN " 1932 else: 1933 op = "" 1934 1935 return f"{op}LATERAL" 1936 1937 def lateral_sql(self, expression: exp.Lateral) -> str: 1938 this = self.sql(expression, "this") 1939 1940 if expression.args.get("view"): 1941 alias = expression.args["alias"] 1942 columns = self.expressions(alias, key="columns", flat=True) 1943 table = f" {alias.name}" if alias.name else "" 1944 columns = f" AS {columns}" if columns else "" 1945 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1946 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1947 1948 alias = self.sql(expression, "alias") 1949 alias = f" AS {alias}" if alias else "" 1950 return f"{self.lateral_op(expression)} {this}{alias}" 1951 1952 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1953 this = self.sql(expression, "this") 1954 1955 args = [ 1956 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1957 for e in (expression.args.get(k) for k in ("offset", "expression")) 1958 if e 1959 ] 1960 1961 args_sql = ", ".join(self.sql(e) for e in args) 1962 args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql 1963 expressions = self.expressions(expression, flat=True) 1964 expressions = f" BY {expressions}" if expressions else "" 1965 1966 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}" 1967 1968 def offset_sql(self, expression: exp.Offset) -> str: 1969 this = self.sql(expression, "this") 1970 value = expression.expression 1971 value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value 1972 expressions = self.expressions(expression, flat=True) 1973 expressions = f" BY {expressions}" if expressions else "" 1974 return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}" 1975 1976 def setitem_sql(self, expression: exp.SetItem) -> str: 1977 kind = self.sql(expression, "kind") 1978 kind = f"{kind} " if kind else "" 1979 this = self.sql(expression, "this") 1980 expressions = self.expressions(expression) 1981 collate = self.sql(expression, "collate") 1982 collate = f" COLLATE {collate}" if collate else "" 1983 global_ = "GLOBAL " if expression.args.get("global") else "" 1984 return f"{global_}{kind}{this}{expressions}{collate}" 1985 1986 def set_sql(self, expression: exp.Set) -> str: 1987 expressions = ( 1988 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 1989 ) 1990 tag = " TAG" if expression.args.get("tag") else "" 1991 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}" 1992 1993 def pragma_sql(self, expression: exp.Pragma) -> str: 1994 return f"PRAGMA {self.sql(expression, 'this')}" 1995 1996 def lock_sql(self, expression: exp.Lock) -> str: 1997 if not self.LOCKING_READS_SUPPORTED: 1998 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 1999 return "" 2000 2001 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 2002 expressions = self.expressions(expression, flat=True) 2003 expressions = f" OF {expressions}" if expressions else "" 2004 wait = expression.args.get("wait") 2005 2006 if wait is not None: 2007 if isinstance(wait, exp.Literal): 2008 wait = f" WAIT {self.sql(wait)}" 2009 else: 2010 wait = " NOWAIT" if wait else " SKIP LOCKED" 2011 2012 return f"{lock_type}{expressions}{wait or ''}" 2013 2014 def literal_sql(self, expression: exp.Literal) -> str: 2015 text = expression.this or "" 2016 if expression.is_string: 2017 text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}" 2018 return text 2019 2020 def escape_str(self, text: str, escape_backslash: bool = True) -> str: 2021 if self.dialect.ESCAPED_SEQUENCES: 2022 to_escaped = self.dialect.ESCAPED_SEQUENCES 2023 text = "".join( 2024 to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text 2025 ) 2026 2027 return self._replace_line_breaks(text).replace( 2028 self.dialect.QUOTE_END, self._escaped_quote_end 2029 ) 2030 2031 def loaddata_sql(self, expression: exp.LoadData) -> str: 2032 local = " LOCAL" if expression.args.get("local") else "" 2033 inpath = f" INPATH {self.sql(expression, 'inpath')}" 2034 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 2035 this = f" INTO TABLE {self.sql(expression, 'this')}" 2036 partition = self.sql(expression, "partition") 2037 partition = f" {partition}" if partition else "" 2038 input_format = self.sql(expression, "input_format") 2039 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 2040 serde = self.sql(expression, "serde") 2041 serde = f" SERDE {serde}" if serde else "" 2042 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}" 2043 2044 def null_sql(self, *_) -> str: 2045 return "NULL" 2046 2047 def boolean_sql(self, expression: exp.Boolean) -> str: 2048 return "TRUE" if expression.this else "FALSE" 2049 2050 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 2051 this = self.sql(expression, "this") 2052 this = f"{this} " if this else this 2053 siblings = "SIBLINGS " if expression.args.get("siblings") else "" 2054 order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore 2055 interpolated_values = [ 2056 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 2057 for named_expression in expression.args.get("interpolate") or [] 2058 ] 2059 interpolate = ( 2060 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 2061 ) 2062 return f"{order}{interpolate}" 2063 2064 def withfill_sql(self, expression: exp.WithFill) -> str: 2065 from_sql = self.sql(expression, "from") 2066 from_sql = f" FROM {from_sql}" if from_sql else "" 2067 to_sql = self.sql(expression, "to") 2068 to_sql = f" TO {to_sql}" if to_sql else "" 2069 step_sql = self.sql(expression, "step") 2070 step_sql = f" STEP {step_sql}" if step_sql else "" 2071 return f"WITH FILL{from_sql}{to_sql}{step_sql}" 2072 2073 def cluster_sql(self, expression: exp.Cluster) -> str: 2074 return self.op_expressions("CLUSTER BY", expression) 2075 2076 def distribute_sql(self, expression: exp.Distribute) -> str: 2077 return self.op_expressions("DISTRIBUTE BY", expression) 2078 2079 def sort_sql(self, expression: exp.Sort) -> str: 2080 return self.op_expressions("SORT BY", expression) 2081 2082 def ordered_sql(self, expression: exp.Ordered) -> str: 2083 desc = expression.args.get("desc") 2084 asc = not desc 2085 2086 nulls_first = expression.args.get("nulls_first") 2087 nulls_last = not nulls_first 2088 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 2089 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 2090 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 2091 2092 this = self.sql(expression, "this") 2093 2094 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 2095 nulls_sort_change = "" 2096 if nulls_first and ( 2097 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 2098 ): 2099 nulls_sort_change = " NULLS FIRST" 2100 elif ( 2101 nulls_last 2102 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 2103 and not nulls_are_last 2104 ): 2105 nulls_sort_change = " NULLS LAST" 2106 2107 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 2108 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 2109 window = expression.find_ancestor(exp.Window, exp.Select) 2110 if isinstance(window, exp.Window) and window.args.get("spec"): 2111 self.unsupported( 2112 f"'{nulls_sort_change.strip()}' translation not supported in window functions" 2113 ) 2114 nulls_sort_change = "" 2115 elif self.NULL_ORDERING_SUPPORTED is None: 2116 if expression.this.is_int: 2117 self.unsupported( 2118 f"'{nulls_sort_change.strip()}' translation not supported with positional ordering" 2119 ) 2120 elif not isinstance(expression.this, exp.Rand): 2121 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 2122 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 2123 nulls_sort_change = "" 2124 2125 with_fill = self.sql(expression, "with_fill") 2126 with_fill = f" {with_fill}" if with_fill else "" 2127 2128 return f"{this}{sort_order}{nulls_sort_change}{with_fill}" 2129 2130 def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str: 2131 window_frame = self.sql(expression, "window_frame") 2132 window_frame = f"{window_frame} " if window_frame else "" 2133 2134 this = self.sql(expression, "this") 2135 2136 return f"{window_frame}{this}" 2137 2138 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 2139 partition = self.partition_by_sql(expression) 2140 order = self.sql(expression, "order") 2141 measures = self.expressions(expression, key="measures") 2142 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 2143 rows = self.sql(expression, "rows") 2144 rows = self.seg(rows) if rows else "" 2145 after = self.sql(expression, "after") 2146 after = self.seg(after) if after else "" 2147 pattern = self.sql(expression, "pattern") 2148 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 2149 definition_sqls = [ 2150 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 2151 for definition in expression.args.get("define", []) 2152 ] 2153 definitions = self.expressions(sqls=definition_sqls) 2154 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 2155 body = "".join( 2156 ( 2157 partition, 2158 order, 2159 measures, 2160 rows, 2161 after, 2162 pattern, 2163 define, 2164 ) 2165 ) 2166 alias = self.sql(expression, "alias") 2167 alias = f" {alias}" if alias else "" 2168 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}" 2169 2170 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 2171 limit = expression.args.get("limit") 2172 2173 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 2174 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 2175 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 2176 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 2177 2178 options = self.expressions(expression, key="options") 2179 if options: 2180 options = f" OPTION{self.wrap(options)}" 2181 2182 return csv( 2183 *sqls, 2184 *[self.sql(join) for join in expression.args.get("joins") or []], 2185 self.sql(expression, "connect"), 2186 self.sql(expression, "match"), 2187 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 2188 self.sql(expression, "prewhere"), 2189 self.sql(expression, "where"), 2190 self.sql(expression, "group"), 2191 self.sql(expression, "having"), 2192 *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()], 2193 self.sql(expression, "order"), 2194 *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit), 2195 *self.after_limit_modifiers(expression), 2196 options, 2197 sep="", 2198 ) 2199 2200 def queryoption_sql(self, expression: exp.QueryOption) -> str: 2201 return "" 2202 2203 def offset_limit_modifiers( 2204 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 2205 ) -> t.List[str]: 2206 return [ 2207 self.sql(expression, "offset") if fetch else self.sql(limit), 2208 self.sql(limit) if fetch else self.sql(expression, "offset"), 2209 ] 2210 2211 def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]: 2212 locks = self.expressions(expression, key="locks", sep=" ") 2213 locks = f" {locks}" if locks else "" 2214 return [locks, self.sql(expression, "sample")] 2215 2216 def select_sql(self, expression: exp.Select) -> str: 2217 into = expression.args.get("into") 2218 if not self.SUPPORTS_SELECT_INTO and into: 2219 into.pop() 2220 2221 hint = self.sql(expression, "hint") 2222 distinct = self.sql(expression, "distinct") 2223 distinct = f" {distinct}" if distinct else "" 2224 kind = self.sql(expression, "kind") 2225 2226 limit = expression.args.get("limit") 2227 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP: 2228 top = self.limit_sql(limit, top=True) 2229 limit.pop() 2230 else: 2231 top = "" 2232 2233 expressions = self.expressions(expression) 2234 2235 if kind: 2236 if kind in self.SELECT_KINDS: 2237 kind = f" AS {kind}" 2238 else: 2239 if kind == "STRUCT": 2240 expressions = self.expressions( 2241 sqls=[ 2242 self.sql( 2243 exp.Struct( 2244 expressions=[ 2245 exp.PropertyEQ(this=e.args.get("alias"), expression=e.this) 2246 if isinstance(e, exp.Alias) 2247 else e 2248 for e in expression.expressions 2249 ] 2250 ) 2251 ) 2252 ] 2253 ) 2254 kind = "" 2255 2256 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2257 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2258 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2259 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2260 sql = self.query_modifiers( 2261 expression, 2262 f"SELECT{top_distinct}{kind}{expressions}", 2263 self.sql(expression, "into", comment=False), 2264 self.sql(expression, "from", comment=False), 2265 ) 2266 2267 sql = self.prepend_ctes(expression, sql) 2268 2269 if not self.SUPPORTS_SELECT_INTO and into: 2270 if into.args.get("temporary"): 2271 table_kind = " TEMPORARY" 2272 elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"): 2273 table_kind = " UNLOGGED" 2274 else: 2275 table_kind = "" 2276 sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}" 2277 2278 return sql 2279 2280 def schema_sql(self, expression: exp.Schema) -> str: 2281 this = self.sql(expression, "this") 2282 sql = self.schema_columns_sql(expression) 2283 return f"{this} {sql}" if this and sql else this or sql 2284 2285 def schema_columns_sql(self, expression: exp.Schema) -> str: 2286 if expression.expressions: 2287 return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}" 2288 return "" 2289 2290 def star_sql(self, expression: exp.Star) -> str: 2291 except_ = self.expressions(expression, key="except", flat=True) 2292 except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else "" 2293 replace = self.expressions(expression, key="replace", flat=True) 2294 replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else "" 2295 return f"*{except_}{replace}" 2296 2297 def parameter_sql(self, expression: exp.Parameter) -> str: 2298 this = self.sql(expression, "this") 2299 return f"{self.PARAMETER_TOKEN}{this}" 2300 2301 def sessionparameter_sql(self, expression: exp.SessionParameter) -> str: 2302 this = self.sql(expression, "this") 2303 kind = expression.text("kind") 2304 if kind: 2305 kind = f"{kind}." 2306 return f"@@{kind}{this}" 2307 2308 def placeholder_sql(self, expression: exp.Placeholder) -> str: 2309 return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?" 2310 2311 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2312 alias = self.sql(expression, "alias") 2313 alias = f"{sep}{alias}" if alias else "" 2314 2315 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2316 pivots = f" {pivots}" if pivots else "" 2317 2318 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2319 return self.prepend_ctes(expression, sql) 2320 2321 def qualify_sql(self, expression: exp.Qualify) -> str: 2322 this = self.indent(self.sql(expression, "this")) 2323 return f"{self.seg('QUALIFY')}{self.sep()}{this}" 2324 2325 def set_operations(self, expression: exp.Union) -> str: 2326 if not self.OUTER_UNION_MODIFIERS: 2327 limit = expression.args.get("limit") 2328 order = expression.args.get("order") 2329 2330 if limit or order: 2331 select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False) 2332 2333 if limit: 2334 select = select.limit(limit.pop(), copy=False) 2335 if order: 2336 select = select.order_by(order.pop(), copy=False) 2337 return self.sql(select) 2338 2339 sqls: t.List[str] = [] 2340 stack: t.List[t.Union[str, exp.Expression]] = [expression] 2341 2342 while stack: 2343 node = stack.pop() 2344 2345 if isinstance(node, exp.Union): 2346 stack.append(node.expression) 2347 stack.append( 2348 self.maybe_comment( 2349 getattr(self, f"{node.key}_op")(node), 2350 comments=node.comments, 2351 separated=True, 2352 ) 2353 ) 2354 stack.append(node.this) 2355 else: 2356 sqls.append(self.sql(node)) 2357 2358 this = self.sep().join(sqls) 2359 this = self.query_modifiers(expression, this) 2360 return self.prepend_ctes(expression, this) 2361 2362 def union_sql(self, expression: exp.Union) -> str: 2363 return self.set_operations(expression) 2364 2365 def union_op(self, expression: exp.Union) -> str: 2366 kind = " DISTINCT" if self.EXPLICIT_UNION else "" 2367 kind = kind if expression.args.get("distinct") else " ALL" 2368 by_name = " BY NAME" if expression.args.get("by_name") else "" 2369 return f"UNION{kind}{by_name}" 2370 2371 def unnest_sql(self, expression: exp.Unnest) -> str: 2372 args = self.expressions(expression, flat=True) 2373 2374 alias = expression.args.get("alias") 2375 offset = expression.args.get("offset") 2376 2377 if self.UNNEST_WITH_ORDINALITY: 2378 if alias and isinstance(offset, exp.Expression): 2379 alias.append("columns", offset) 2380 2381 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2382 columns = alias.columns 2383 alias = self.sql(columns[0]) if columns else "" 2384 else: 2385 alias = self.sql(alias) 2386 2387 alias = f" AS {alias}" if alias else alias 2388 if self.UNNEST_WITH_ORDINALITY: 2389 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2390 else: 2391 if isinstance(offset, exp.Expression): 2392 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2393 elif offset: 2394 suffix = f"{alias} WITH OFFSET" 2395 else: 2396 suffix = alias 2397 2398 return f"UNNEST({args}){suffix}" 2399 2400 def prewhere_sql(self, expression: exp.PreWhere) -> str: 2401 return "" 2402 2403 def where_sql(self, expression: exp.Where) -> str: 2404 this = self.indent(self.sql(expression, "this")) 2405 return f"{self.seg('WHERE')}{self.sep()}{this}" 2406 2407 def window_sql(self, expression: exp.Window) -> str: 2408 this = self.sql(expression, "this") 2409 partition = self.partition_by_sql(expression) 2410 order = expression.args.get("order") 2411 order = self.order_sql(order, flat=True) if order else "" 2412 spec = self.sql(expression, "spec") 2413 alias = self.sql(expression, "alias") 2414 over = self.sql(expression, "over") or "OVER" 2415 2416 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2417 2418 first = expression.args.get("first") 2419 if first is None: 2420 first = "" 2421 else: 2422 first = "FIRST" if first else "LAST" 2423 2424 if not partition and not order and not spec and alias: 2425 return f"{this} {alias}" 2426 2427 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2428 return f"{this} ({args})" 2429 2430 def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str: 2431 partition = self.expressions(expression, key="partition_by", flat=True) 2432 return f"PARTITION BY {partition}" if partition else "" 2433 2434 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2435 kind = self.sql(expression, "kind") 2436 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2437 end = ( 2438 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2439 or "CURRENT ROW" 2440 ) 2441 return f"{kind} BETWEEN {start} AND {end}" 2442 2443 def withingroup_sql(self, expression: exp.WithinGroup) -> str: 2444 this = self.sql(expression, "this") 2445 expression_sql = self.sql(expression, "expression")[1:] # order has a leading space 2446 return f"{this} WITHIN GROUP ({expression_sql})" 2447 2448 def between_sql(self, expression: exp.Between) -> str: 2449 this = self.sql(expression, "this") 2450 low = self.sql(expression, "low") 2451 high = self.sql(expression, "high") 2452 return f"{this} BETWEEN {low} AND {high}" 2453 2454 def bracket_offset_expressions(self, expression: exp.Bracket) -> t.List[exp.Expression]: 2455 return apply_index_offset( 2456 expression.this, 2457 expression.expressions, 2458 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2459 ) 2460 2461 def bracket_sql(self, expression: exp.Bracket) -> str: 2462 expressions = self.bracket_offset_expressions(expression) 2463 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2464 return f"{self.sql(expression, 'this')}[{expressions_sql}]" 2465 2466 def all_sql(self, expression: exp.All) -> str: 2467 return f"ALL {self.wrap(expression)}" 2468 2469 def any_sql(self, expression: exp.Any) -> str: 2470 this = self.sql(expression, "this") 2471 if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)): 2472 if isinstance(expression.this, exp.UNWRAPPED_QUERIES): 2473 this = self.wrap(this) 2474 return f"ANY{this}" 2475 return f"ANY {this}" 2476 2477 def exists_sql(self, expression: exp.Exists) -> str: 2478 return f"EXISTS{self.wrap(expression)}" 2479 2480 def case_sql(self, expression: exp.Case) -> str: 2481 this = self.sql(expression, "this") 2482 statements = [f"CASE {this}" if this else "CASE"] 2483 2484 for e in expression.args["ifs"]: 2485 statements.append(f"WHEN {self.sql(e, 'this')}") 2486 statements.append(f"THEN {self.sql(e, 'true')}") 2487 2488 default = self.sql(expression, "default") 2489 2490 if default: 2491 statements.append(f"ELSE {default}") 2492 2493 statements.append("END") 2494 2495 if self.pretty and self.too_wide(statements): 2496 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2497 2498 return " ".join(statements) 2499 2500 def constraint_sql(self, expression: exp.Constraint) -> str: 2501 this = self.sql(expression, "this") 2502 expressions = self.expressions(expression, flat=True) 2503 return f"CONSTRAINT {this} {expressions}" 2504 2505 def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str: 2506 order = expression.args.get("order") 2507 order = f" OVER ({self.order_sql(order, flat=True)})" if order else "" 2508 return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}" 2509 2510 def extract_sql(self, expression: exp.Extract) -> str: 2511 this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name 2512 expression_sql = self.sql(expression, "expression") 2513 return f"EXTRACT({this} FROM {expression_sql})" 2514 2515 def trim_sql(self, expression: exp.Trim) -> str: 2516 trim_type = self.sql(expression, "position") 2517 2518 if trim_type == "LEADING": 2519 return self.func("LTRIM", expression.this) 2520 elif trim_type == "TRAILING": 2521 return self.func("RTRIM", expression.this) 2522 else: 2523 return self.func("TRIM", expression.this, expression.expression) 2524 2525 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2526 args = expression.expressions 2527 if isinstance(expression, exp.ConcatWs): 2528 args = args[1:] # Skip the delimiter 2529 2530 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2531 args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args] 2532 2533 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2534 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2535 2536 return args 2537 2538 def concat_sql(self, expression: exp.Concat) -> str: 2539 expressions = self.convert_concat_args(expression) 2540 2541 # Some dialects don't allow a single-argument CONCAT call 2542 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2543 return self.sql(expressions[0]) 2544 2545 return self.func("CONCAT", *expressions) 2546 2547 def concatws_sql(self, expression: exp.ConcatWs) -> str: 2548 return self.func( 2549 "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression) 2550 ) 2551 2552 def check_sql(self, expression: exp.Check) -> str: 2553 this = self.sql(expression, key="this") 2554 return f"CHECK ({this})" 2555 2556 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2557 expressions = self.expressions(expression, flat=True) 2558 reference = self.sql(expression, "reference") 2559 reference = f" {reference}" if reference else "" 2560 delete = self.sql(expression, "delete") 2561 delete = f" ON DELETE {delete}" if delete else "" 2562 update = self.sql(expression, "update") 2563 update = f" ON UPDATE {update}" if update else "" 2564 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}" 2565 2566 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2567 expressions = self.expressions(expression, flat=True) 2568 options = self.expressions(expression, key="options", flat=True, sep=" ") 2569 options = f" {options}" if options else "" 2570 return f"PRIMARY KEY ({expressions}){options}" 2571 2572 def if_sql(self, expression: exp.If) -> str: 2573 return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false"))) 2574 2575 def matchagainst_sql(self, expression: exp.MatchAgainst) -> str: 2576 modifier = expression.args.get("modifier") 2577 modifier = f" {modifier}" if modifier else "" 2578 return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})" 2579 2580 def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str: 2581 return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}" 2582 2583 def jsonpath_sql(self, expression: exp.JSONPath) -> str: 2584 path = self.expressions(expression, sep="", flat=True).lstrip(".") 2585 return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}" 2586 2587 def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str: 2588 if isinstance(expression, exp.JSONPathPart): 2589 transform = self.TRANSFORMS.get(expression.__class__) 2590 if not callable(transform): 2591 self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}") 2592 return "" 2593 2594 return transform(self, expression) 2595 2596 if isinstance(expression, int): 2597 return str(expression) 2598 2599 if self.JSON_PATH_SINGLE_QUOTE_ESCAPE: 2600 escaped = expression.replace("'", "\\'") 2601 escaped = f"\\'{expression}\\'" 2602 else: 2603 escaped = expression.replace('"', '\\"') 2604 escaped = f'"{escaped}"' 2605 2606 return escaped 2607 2608 def formatjson_sql(self, expression: exp.FormatJson) -> str: 2609 return f"{self.sql(expression, 'this')} FORMAT JSON" 2610 2611 def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str: 2612 null_handling = expression.args.get("null_handling") 2613 null_handling = f" {null_handling}" if null_handling else "" 2614 2615 unique_keys = expression.args.get("unique_keys") 2616 if unique_keys is not None: 2617 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2618 else: 2619 unique_keys = "" 2620 2621 return_type = self.sql(expression, "return_type") 2622 return_type = f" RETURNING {return_type}" if return_type else "" 2623 encoding = self.sql(expression, "encoding") 2624 encoding = f" ENCODING {encoding}" if encoding else "" 2625 2626 return self.func( 2627 "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG", 2628 *expression.expressions, 2629 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2630 ) 2631 2632 def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str: 2633 return self.jsonobject_sql(expression) 2634 2635 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2636 null_handling = expression.args.get("null_handling") 2637 null_handling = f" {null_handling}" if null_handling else "" 2638 return_type = self.sql(expression, "return_type") 2639 return_type = f" RETURNING {return_type}" if return_type else "" 2640 strict = " STRICT" if expression.args.get("strict") else "" 2641 return self.func( 2642 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2643 ) 2644 2645 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2646 this = self.sql(expression, "this") 2647 order = self.sql(expression, "order") 2648 null_handling = expression.args.get("null_handling") 2649 null_handling = f" {null_handling}" if null_handling else "" 2650 return_type = self.sql(expression, "return_type") 2651 return_type = f" RETURNING {return_type}" if return_type else "" 2652 strict = " STRICT" if expression.args.get("strict") else "" 2653 return self.func( 2654 "JSON_ARRAYAGG", 2655 this, 2656 suffix=f"{order}{null_handling}{return_type}{strict})", 2657 ) 2658 2659 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2660 path = self.sql(expression, "path") 2661 path = f" PATH {path}" if path else "" 2662 nested_schema = self.sql(expression, "nested_schema") 2663 2664 if nested_schema: 2665 return f"NESTED{path} {nested_schema}" 2666 2667 this = self.sql(expression, "this") 2668 kind = self.sql(expression, "kind") 2669 kind = f" {kind}" if kind else "" 2670 return f"{this}{kind}{path}" 2671 2672 def jsonschema_sql(self, expression: exp.JSONSchema) -> str: 2673 return self.func("COLUMNS", *expression.expressions) 2674 2675 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2676 this = self.sql(expression, "this") 2677 path = self.sql(expression, "path") 2678 path = f", {path}" if path else "" 2679 error_handling = expression.args.get("error_handling") 2680 error_handling = f" {error_handling}" if error_handling else "" 2681 empty_handling = expression.args.get("empty_handling") 2682 empty_handling = f" {empty_handling}" if empty_handling else "" 2683 schema = self.sql(expression, "schema") 2684 return self.func( 2685 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2686 ) 2687 2688 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2689 this = self.sql(expression, "this") 2690 kind = self.sql(expression, "kind") 2691 path = self.sql(expression, "path") 2692 path = f" {path}" if path else "" 2693 as_json = " AS JSON" if expression.args.get("as_json") else "" 2694 return f"{this} {kind}{path}{as_json}" 2695 2696 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2697 this = self.sql(expression, "this") 2698 path = self.sql(expression, "path") 2699 path = f", {path}" if path else "" 2700 expressions = self.expressions(expression) 2701 with_ = ( 2702 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2703 if expressions 2704 else "" 2705 ) 2706 return f"OPENJSON({this}{path}){with_}" 2707 2708 def in_sql(self, expression: exp.In) -> str: 2709 query = expression.args.get("query") 2710 unnest = expression.args.get("unnest") 2711 field = expression.args.get("field") 2712 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2713 2714 if query: 2715 in_sql = self.sql(query) 2716 elif unnest: 2717 in_sql = self.in_unnest_op(unnest) 2718 elif field: 2719 in_sql = self.sql(field) 2720 else: 2721 in_sql = f"({self.expressions(expression, flat=True)})" 2722 2723 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}" 2724 2725 def in_unnest_op(self, unnest: exp.Unnest) -> str: 2726 return f"(SELECT {self.sql(unnest)})" 2727 2728 def interval_sql(self, expression: exp.Interval) -> str: 2729 unit = self.sql(expression, "unit") 2730 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2731 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2732 unit = f" {unit}" if unit else "" 2733 2734 if self.SINGLE_STRING_INTERVAL: 2735 this = expression.this.name if expression.this else "" 2736 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2737 2738 this = self.sql(expression, "this") 2739 if this: 2740 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2741 this = f" {this}" if unwrapped else f" ({this})" 2742 2743 return f"INTERVAL{this}{unit}" 2744 2745 def return_sql(self, expression: exp.Return) -> str: 2746 return f"RETURN {self.sql(expression, 'this')}" 2747 2748 def reference_sql(self, expression: exp.Reference) -> str: 2749 this = self.sql(expression, "this") 2750 expressions = self.expressions(expression, flat=True) 2751 expressions = f"({expressions})" if expressions else "" 2752 options = self.expressions(expression, key="options", flat=True, sep=" ") 2753 options = f" {options}" if options else "" 2754 return f"REFERENCES {this}{expressions}{options}" 2755 2756 def anonymous_sql(self, expression: exp.Anonymous) -> str: 2757 return self.func(self.sql(expression, "this"), *expression.expressions) 2758 2759 def paren_sql(self, expression: exp.Paren) -> str: 2760 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2761 return f"({sql}{self.seg(')', sep='')}" 2762 2763 def neg_sql(self, expression: exp.Neg) -> str: 2764 # This makes sure we don't convert "- - 5" to "--5", which is a comment 2765 this_sql = self.sql(expression, "this") 2766 sep = " " if this_sql[0] == "-" else "" 2767 return f"-{sep}{this_sql}" 2768 2769 def not_sql(self, expression: exp.Not) -> str: 2770 return f"NOT {self.sql(expression, 'this')}" 2771 2772 def alias_sql(self, expression: exp.Alias) -> str: 2773 alias = self.sql(expression, "alias") 2774 alias = f" AS {alias}" if alias else "" 2775 return f"{self.sql(expression, 'this')}{alias}" 2776 2777 def pivotalias_sql(self, expression: exp.PivotAlias) -> str: 2778 alias = expression.args["alias"] 2779 identifier_alias = isinstance(alias, exp.Identifier) 2780 2781 if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2782 alias.replace(exp.Literal.string(alias.output_name)) 2783 elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2784 alias.replace(exp.to_identifier(alias.output_name)) 2785 2786 return self.alias_sql(expression) 2787 2788 def aliases_sql(self, expression: exp.Aliases) -> str: 2789 return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})" 2790 2791 def atindex_sql(self, expression: exp.AtTimeZone) -> str: 2792 this = self.sql(expression, "this") 2793 index = self.sql(expression, "expression") 2794 return f"{this} AT {index}" 2795 2796 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 2797 this = self.sql(expression, "this") 2798 zone = self.sql(expression, "zone") 2799 return f"{this} AT TIME ZONE {zone}" 2800 2801 def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str: 2802 this = self.sql(expression, "this") 2803 zone = self.sql(expression, "zone") 2804 return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'" 2805 2806 def add_sql(self, expression: exp.Add) -> str: 2807 return self.binary(expression, "+") 2808 2809 def and_sql( 2810 self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None 2811 ) -> str: 2812 return self.connector_sql(expression, "AND", stack) 2813 2814 def or_sql( 2815 self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None 2816 ) -> str: 2817 return self.connector_sql(expression, "OR", stack) 2818 2819 def xor_sql( 2820 self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None 2821 ) -> str: 2822 return self.connector_sql(expression, "XOR", stack) 2823 2824 def connector_sql( 2825 self, 2826 expression: exp.Connector, 2827 op: str, 2828 stack: t.Optional[t.List[str | exp.Expression]] = None, 2829 ) -> str: 2830 if stack is not None: 2831 if expression.expressions: 2832 stack.append(self.expressions(expression, sep=f" {op} ")) 2833 else: 2834 stack.append(expression.right) 2835 if expression.comments: 2836 for comment in expression.comments: 2837 op += f" /*{self.pad_comment(comment)}*/" 2838 stack.extend((op, expression.left)) 2839 return op 2840 2841 stack = [expression] 2842 sqls: t.List[str] = [] 2843 ops = set() 2844 2845 while stack: 2846 node = stack.pop() 2847 if isinstance(node, exp.Connector): 2848 ops.add(getattr(self, f"{node.key}_sql")(node, stack)) 2849 else: 2850 sql = self.sql(node) 2851 if sqls and sqls[-1] in ops: 2852 sqls[-1] += f" {sql}" 2853 else: 2854 sqls.append(sql) 2855 2856 sep = "\n" if self.pretty and self.too_wide(sqls) else " " 2857 return sep.join(sqls) 2858 2859 def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str: 2860 return self.binary(expression, "&") 2861 2862 def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str: 2863 return self.binary(expression, "<<") 2864 2865 def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str: 2866 return f"~{self.sql(expression, 'this')}" 2867 2868 def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str: 2869 return self.binary(expression, "|") 2870 2871 def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str: 2872 return self.binary(expression, ">>") 2873 2874 def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str: 2875 return self.binary(expression, "^") 2876 2877 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2878 format_sql = self.sql(expression, "format") 2879 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2880 to_sql = self.sql(expression, "to") 2881 to_sql = f" {to_sql}" if to_sql else "" 2882 action = self.sql(expression, "action") 2883 action = f" {action}" if action else "" 2884 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql}{action})" 2885 2886 def currentdate_sql(self, expression: exp.CurrentDate) -> str: 2887 zone = self.sql(expression, "this") 2888 return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE" 2889 2890 def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str: 2891 return self.func("CURRENT_TIMESTAMP", expression.this) 2892 2893 def collate_sql(self, expression: exp.Collate) -> str: 2894 if self.COLLATE_IS_FUNC: 2895 return self.function_fallback_sql(expression) 2896 return self.binary(expression, "COLLATE") 2897 2898 def command_sql(self, expression: exp.Command) -> str: 2899 return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}" 2900 2901 def comment_sql(self, expression: exp.Comment) -> str: 2902 this = self.sql(expression, "this") 2903 kind = expression.args["kind"] 2904 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 2905 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2906 expression_sql = self.sql(expression, "expression") 2907 return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}" 2908 2909 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2910 this = self.sql(expression, "this") 2911 delete = " DELETE" if expression.args.get("delete") else "" 2912 recompress = self.sql(expression, "recompress") 2913 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2914 to_disk = self.sql(expression, "to_disk") 2915 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2916 to_volume = self.sql(expression, "to_volume") 2917 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2918 return f"{this}{delete}{recompress}{to_disk}{to_volume}" 2919 2920 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2921 where = self.sql(expression, "where") 2922 group = self.sql(expression, "group") 2923 aggregates = self.expressions(expression, key="aggregates") 2924 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2925 2926 if not (where or group or aggregates) and len(expression.expressions) == 1: 2927 return f"TTL {self.expressions(expression, flat=True)}" 2928 2929 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}" 2930 2931 def transaction_sql(self, expression: exp.Transaction) -> str: 2932 return "BEGIN" 2933 2934 def commit_sql(self, expression: exp.Commit) -> str: 2935 chain = expression.args.get("chain") 2936 if chain is not None: 2937 chain = " AND CHAIN" if chain else " AND NO CHAIN" 2938 2939 return f"COMMIT{chain or ''}" 2940 2941 def rollback_sql(self, expression: exp.Rollback) -> str: 2942 savepoint = expression.args.get("savepoint") 2943 savepoint = f" TO {savepoint}" if savepoint else "" 2944 return f"ROLLBACK{savepoint}" 2945 2946 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2947 this = self.sql(expression, "this") 2948 2949 dtype = self.sql(expression, "dtype") 2950 if dtype: 2951 collate = self.sql(expression, "collate") 2952 collate = f" COLLATE {collate}" if collate else "" 2953 using = self.sql(expression, "using") 2954 using = f" USING {using}" if using else "" 2955 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2956 2957 default = self.sql(expression, "default") 2958 if default: 2959 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2960 2961 comment = self.sql(expression, "comment") 2962 if comment: 2963 return f"ALTER COLUMN {this} COMMENT {comment}" 2964 2965 if not expression.args.get("drop"): 2966 self.unsupported("Unsupported ALTER COLUMN syntax") 2967 2968 return f"ALTER COLUMN {this} DROP DEFAULT" 2969 2970 def renametable_sql(self, expression: exp.RenameTable) -> str: 2971 if not self.RENAME_TABLE_WITH_DB: 2972 # Remove db from tables 2973 expression = expression.transform( 2974 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 2975 ).assert_is(exp.RenameTable) 2976 this = self.sql(expression, "this") 2977 return f"RENAME TO {this}" 2978 2979 def renamecolumn_sql(self, expression: exp.RenameColumn) -> str: 2980 exists = " IF EXISTS" if expression.args.get("exists") else "" 2981 old_column = self.sql(expression, "this") 2982 new_column = self.sql(expression, "to") 2983 return f"RENAME COLUMN{exists} {old_column} TO {new_column}" 2984 2985 def altertable_sql(self, expression: exp.AlterTable) -> str: 2986 actions = expression.args["actions"] 2987 2988 if isinstance(actions[0], exp.ColumnDef): 2989 actions = self.add_column_sql(expression) 2990 elif isinstance(actions[0], exp.Schema): 2991 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 2992 elif isinstance(actions[0], exp.Delete): 2993 actions = self.expressions(expression, key="actions", flat=True) 2994 else: 2995 actions = self.expressions(expression, key="actions", flat=True) 2996 2997 exists = " IF EXISTS" if expression.args.get("exists") else "" 2998 only = " ONLY" if expression.args.get("only") else "" 2999 options = self.expressions(expression, key="options") 3000 options = f", {options}" if options else "" 3001 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}" 3002 3003 def add_column_sql(self, expression: exp.AlterTable) -> str: 3004 if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD: 3005 return self.expressions( 3006 expression, 3007 key="actions", 3008 prefix="ADD COLUMN ", 3009 ) 3010 return f"ADD {self.expressions(expression, key='actions', flat=True)}" 3011 3012 def droppartition_sql(self, expression: exp.DropPartition) -> str: 3013 expressions = self.expressions(expression) 3014 exists = " IF EXISTS " if expression.args.get("exists") else " " 3015 return f"DROP{exists}{expressions}" 3016 3017 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 3018 return f"ADD {self.expressions(expression)}" 3019 3020 def distinct_sql(self, expression: exp.Distinct) -> str: 3021 this = self.expressions(expression, flat=True) 3022 3023 if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1: 3024 case = exp.case() 3025 for arg in expression.expressions: 3026 case = case.when(arg.is_(exp.null()), exp.null()) 3027 this = self.sql(case.else_(f"({this})")) 3028 3029 this = f" {this}" if this else "" 3030 3031 on = self.sql(expression, "on") 3032 on = f" ON {on}" if on else "" 3033 return f"DISTINCT{this}{on}" 3034 3035 def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: 3036 return self._embed_ignore_nulls(expression, "IGNORE NULLS") 3037 3038 def respectnulls_sql(self, expression: exp.RespectNulls) -> str: 3039 return self._embed_ignore_nulls(expression, "RESPECT NULLS") 3040 3041 def havingmax_sql(self, expression: exp.HavingMax) -> str: 3042 this_sql = self.sql(expression, "this") 3043 expression_sql = self.sql(expression, "expression") 3044 kind = "MAX" if expression.args.get("max") else "MIN" 3045 return f"{this_sql} HAVING {kind} {expression_sql}" 3046 3047 def intdiv_sql(self, expression: exp.IntDiv) -> str: 3048 return self.sql( 3049 exp.Cast( 3050 this=exp.Div(this=expression.this, expression=expression.expression), 3051 to=exp.DataType(this=exp.DataType.Type.INT), 3052 ) 3053 ) 3054 3055 def dpipe_sql(self, expression: exp.DPipe) -> str: 3056 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 3057 return self.func( 3058 "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten()) 3059 ) 3060 return self.binary(expression, "||") 3061 3062 def div_sql(self, expression: exp.Div) -> str: 3063 l, r = expression.left, expression.right 3064 3065 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 3066 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 3067 3068 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 3069 if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES): 3070 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 3071 3072 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 3073 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 3074 return self.sql( 3075 exp.cast( 3076 l / r, 3077 to=exp.DataType.Type.BIGINT, 3078 ) 3079 ) 3080 3081 return self.binary(expression, "/") 3082 3083 def overlaps_sql(self, expression: exp.Overlaps) -> str: 3084 return self.binary(expression, "OVERLAPS") 3085 3086 def distance_sql(self, expression: exp.Distance) -> str: 3087 return self.binary(expression, "<->") 3088 3089 def dot_sql(self, expression: exp.Dot) -> str: 3090 return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}" 3091 3092 def eq_sql(self, expression: exp.EQ) -> str: 3093 return self.binary(expression, "=") 3094 3095 def propertyeq_sql(self, expression: exp.PropertyEQ) -> str: 3096 return self.binary(expression, ":=") 3097 3098 def escape_sql(self, expression: exp.Escape) -> str: 3099 return self.binary(expression, "ESCAPE") 3100 3101 def glob_sql(self, expression: exp.Glob) -> str: 3102 return self.binary(expression, "GLOB") 3103 3104 def gt_sql(self, expression: exp.GT) -> str: 3105 return self.binary(expression, ">") 3106 3107 def gte_sql(self, expression: exp.GTE) -> str: 3108 return self.binary(expression, ">=") 3109 3110 def ilike_sql(self, expression: exp.ILike) -> str: 3111 return self.binary(expression, "ILIKE") 3112 3113 def ilikeany_sql(self, expression: exp.ILikeAny) -> str: 3114 return self.binary(expression, "ILIKE ANY") 3115 3116 def is_sql(self, expression: exp.Is) -> str: 3117 if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean): 3118 return self.sql( 3119 expression.this if expression.expression.this else exp.not_(expression.this) 3120 ) 3121 return self.binary(expression, "IS") 3122 3123 def like_sql(self, expression: exp.Like) -> str: 3124 return self.binary(expression, "LIKE") 3125 3126 def likeany_sql(self, expression: exp.LikeAny) -> str: 3127 return self.binary(expression, "LIKE ANY") 3128 3129 def similarto_sql(self, expression: exp.SimilarTo) -> str: 3130 return self.binary(expression, "SIMILAR TO") 3131 3132 def lt_sql(self, expression: exp.LT) -> str: 3133 return self.binary(expression, "<") 3134 3135 def lte_sql(self, expression: exp.LTE) -> str: 3136 return self.binary(expression, "<=") 3137 3138 def mod_sql(self, expression: exp.Mod) -> str: 3139 return self.binary(expression, "%") 3140 3141 def mul_sql(self, expression: exp.Mul) -> str: 3142 return self.binary(expression, "*") 3143 3144 def neq_sql(self, expression: exp.NEQ) -> str: 3145 return self.binary(expression, "<>") 3146 3147 def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str: 3148 return self.binary(expression, "IS NOT DISTINCT FROM") 3149 3150 def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str: 3151 return self.binary(expression, "IS DISTINCT FROM") 3152 3153 def slice_sql(self, expression: exp.Slice) -> str: 3154 return self.binary(expression, ":") 3155 3156 def sub_sql(self, expression: exp.Sub) -> str: 3157 return self.binary(expression, "-") 3158 3159 def trycast_sql(self, expression: exp.TryCast) -> str: 3160 return self.cast_sql(expression, safe_prefix="TRY_") 3161 3162 def log_sql(self, expression: exp.Log) -> str: 3163 this = expression.this 3164 expr = expression.expression 3165 3166 if self.dialect.LOG_BASE_FIRST is False: 3167 this, expr = expr, this 3168 elif self.dialect.LOG_BASE_FIRST is None and expr: 3169 if this.name in ("2", "10"): 3170 return self.func(f"LOG{this.name}", expr) 3171 3172 self.unsupported(f"Unsupported logarithm with base {self.sql(this)}") 3173 3174 return self.func("LOG", this, expr) 3175 3176 def use_sql(self, expression: exp.Use) -> str: 3177 kind = self.sql(expression, "kind") 3178 kind = f" {kind}" if kind else "" 3179 this = self.sql(expression, "this") 3180 this = f" {this}" if this else "" 3181 return f"USE{kind}{this}" 3182 3183 def binary(self, expression: exp.Binary, op: str) -> str: 3184 op = self.maybe_comment(op, comments=expression.comments) 3185 return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}" 3186 3187 def function_fallback_sql(self, expression: exp.Func) -> str: 3188 args = [] 3189 3190 for key in expression.arg_types: 3191 arg_value = expression.args.get(key) 3192 3193 if isinstance(arg_value, list): 3194 for value in arg_value: 3195 args.append(value) 3196 elif arg_value is not None: 3197 args.append(arg_value) 3198 3199 if self.normalize_functions: 3200 name = expression.sql_name() 3201 else: 3202 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 3203 3204 return self.func(name, *args) 3205 3206 def func( 3207 self, 3208 name: str, 3209 *args: t.Optional[exp.Expression | str], 3210 prefix: str = "(", 3211 suffix: str = ")", 3212 ) -> str: 3213 return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}" 3214 3215 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 3216 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 3217 if self.pretty and self.too_wide(arg_sqls): 3218 return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 3219 return ", ".join(arg_sqls) 3220 3221 def too_wide(self, args: t.Iterable) -> bool: 3222 return sum(len(arg) for arg in args) > self.max_text_width 3223 3224 def format_time( 3225 self, 3226 expression: exp.Expression, 3227 inverse_time_mapping: t.Optional[t.Dict[str, str]] = None, 3228 inverse_time_trie: t.Optional[t.Dict] = None, 3229 ) -> t.Optional[str]: 3230 return format_time( 3231 self.sql(expression, "format"), 3232 inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING, 3233 inverse_time_trie or self.dialect.INVERSE_TIME_TRIE, 3234 ) 3235 3236 def expressions( 3237 self, 3238 expression: t.Optional[exp.Expression] = None, 3239 key: t.Optional[str] = None, 3240 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 3241 flat: bool = False, 3242 indent: bool = True, 3243 skip_first: bool = False, 3244 skip_last: bool = False, 3245 sep: str = ", ", 3246 prefix: str = "", 3247 dynamic: bool = False, 3248 new_line: bool = False, 3249 ) -> str: 3250 expressions = expression.args.get(key or "expressions") if expression else sqls 3251 3252 if not expressions: 3253 return "" 3254 3255 if flat: 3256 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 3257 3258 num_sqls = len(expressions) 3259 3260 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 3261 if self.pretty and not self.leading_comma: 3262 stripped_sep = sep.strip() 3263 3264 result_sqls = [] 3265 for i, e in enumerate(expressions): 3266 sql = self.sql(e, comment=False) 3267 if not sql: 3268 continue 3269 3270 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 3271 3272 if self.pretty: 3273 if self.leading_comma: 3274 result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}") 3275 else: 3276 result_sqls.append( 3277 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 3278 ) 3279 else: 3280 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 3281 3282 if self.pretty and (not dynamic or self.too_wide(result_sqls)): 3283 if new_line: 3284 result_sqls.insert(0, "") 3285 result_sqls.append("") 3286 result_sql = "\n".join(result_sqls) 3287 else: 3288 result_sql = "".join(result_sqls) 3289 return ( 3290 self.indent(result_sql, skip_first=skip_first, skip_last=skip_last) 3291 if indent 3292 else result_sql 3293 ) 3294 3295 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 3296 flat = flat or isinstance(expression.parent, exp.Properties) 3297 expressions_sql = self.expressions(expression, flat=flat) 3298 if flat: 3299 return f"{op} {expressions_sql}" 3300 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}" 3301 3302 def naked_property(self, expression: exp.Property) -> str: 3303 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 3304 if not property_name: 3305 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 3306 return f"{property_name} {self.sql(expression, 'this')}" 3307 3308 def tag_sql(self, expression: exp.Tag) -> str: 3309 return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}" 3310 3311 def token_sql(self, token_type: TokenType) -> str: 3312 return self.TOKEN_MAPPING.get(token_type, token_type.name) 3313 3314 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 3315 this = self.sql(expression, "this") 3316 expressions = self.no_identify(self.expressions, expression) 3317 expressions = ( 3318 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 3319 ) 3320 return f"{this}{expressions}" 3321 3322 def joinhint_sql(self, expression: exp.JoinHint) -> str: 3323 this = self.sql(expression, "this") 3324 expressions = self.expressions(expression, flat=True) 3325 return f"{this}({expressions})" 3326 3327 def kwarg_sql(self, expression: exp.Kwarg) -> str: 3328 return self.binary(expression, "=>") 3329 3330 def when_sql(self, expression: exp.When) -> str: 3331 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 3332 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 3333 condition = self.sql(expression, "condition") 3334 condition = f" AND {condition}" if condition else "" 3335 3336 then_expression = expression.args.get("then") 3337 if isinstance(then_expression, exp.Insert): 3338 then = f"INSERT {self.sql(then_expression, 'this')}" 3339 if "expression" in then_expression.args: 3340 then += f" VALUES {self.sql(then_expression, 'expression')}" 3341 elif isinstance(then_expression, exp.Update): 3342 if isinstance(then_expression.args.get("expressions"), exp.Star): 3343 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 3344 else: 3345 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 3346 else: 3347 then = self.sql(then_expression) 3348 return f"WHEN {matched}{source}{condition} THEN {then}" 3349 3350 def merge_sql(self, expression: exp.Merge) -> str: 3351 table = expression.this 3352 table_alias = "" 3353 3354 hints = table.args.get("hints") 3355 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 3356 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 3357 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 3358 3359 this = self.sql(table) 3360 using = f"USING {self.sql(expression, 'using')}" 3361 on = f"ON {self.sql(expression, 'on')}" 3362 expressions = self.expressions(expression, sep=" ") 3363 3364 return self.prepend_ctes( 3365 expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}" 3366 ) 3367 3368 def tochar_sql(self, expression: exp.ToChar) -> str: 3369 if expression.args.get("format"): 3370 self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function") 3371 3372 return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT)) 3373 3374 def tonumber_sql(self, expression: exp.ToNumber) -> str: 3375 if not self.SUPPORTS_TO_NUMBER: 3376 self.unsupported("Unsupported TO_NUMBER function") 3377 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3378 3379 fmt = expression.args.get("format") 3380 if not fmt: 3381 self.unsupported("Conversion format is required for TO_NUMBER") 3382 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3383 3384 return self.func("TO_NUMBER", expression.this, fmt) 3385 3386 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 3387 this = self.sql(expression, "this") 3388 kind = self.sql(expression, "kind") 3389 settings_sql = self.expressions(expression, key="settings", sep=" ") 3390 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 3391 return f"{this}({kind}{args})" 3392 3393 def dictrange_sql(self, expression: exp.DictRange) -> str: 3394 this = self.sql(expression, "this") 3395 max = self.sql(expression, "max") 3396 min = self.sql(expression, "min") 3397 return f"{this}(MIN {min} MAX {max})" 3398 3399 def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str: 3400 return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}" 3401 3402 def oncluster_sql(self, expression: exp.OnCluster) -> str: 3403 return "" 3404 3405 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3406 expressions = self.expressions(expression, key="expressions", flat=True) 3407 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3408 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3409 buckets = self.sql(expression, "buckets") 3410 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS" 3411 3412 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3413 this = self.sql(expression, "this") 3414 having = self.sql(expression, "having") 3415 3416 if having: 3417 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3418 3419 return self.func("ANY_VALUE", this) 3420 3421 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3422 transform = self.func("TRANSFORM", *expression.expressions) 3423 row_format_before = self.sql(expression, "row_format_before") 3424 row_format_before = f" {row_format_before}" if row_format_before else "" 3425 record_writer = self.sql(expression, "record_writer") 3426 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3427 using = f" USING {self.sql(expression, 'command_script')}" 3428 schema = self.sql(expression, "schema") 3429 schema = f" AS {schema}" if schema else "" 3430 row_format_after = self.sql(expression, "row_format_after") 3431 row_format_after = f" {row_format_after}" if row_format_after else "" 3432 record_reader = self.sql(expression, "record_reader") 3433 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3434 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}" 3435 3436 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3437 key_block_size = self.sql(expression, "key_block_size") 3438 if key_block_size: 3439 return f"KEY_BLOCK_SIZE = {key_block_size}" 3440 3441 using = self.sql(expression, "using") 3442 if using: 3443 return f"USING {using}" 3444 3445 parser = self.sql(expression, "parser") 3446 if parser: 3447 return f"WITH PARSER {parser}" 3448 3449 comment = self.sql(expression, "comment") 3450 if comment: 3451 return f"COMMENT {comment}" 3452 3453 visible = expression.args.get("visible") 3454 if visible is not None: 3455 return "VISIBLE" if visible else "INVISIBLE" 3456 3457 engine_attr = self.sql(expression, "engine_attr") 3458 if engine_attr: 3459 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3460 3461 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3462 if secondary_engine_attr: 3463 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3464 3465 self.unsupported("Unsupported index constraint option.") 3466 return "" 3467 3468 def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str: 3469 enforced = " ENFORCED" if expression.args.get("enforced") else "" 3470 return f"CHECK ({self.sql(expression, 'this')}){enforced}" 3471 3472 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3473 kind = self.sql(expression, "kind") 3474 kind = f"{kind} INDEX" if kind else "INDEX" 3475 this = self.sql(expression, "this") 3476 this = f" {this}" if this else "" 3477 index_type = self.sql(expression, "index_type") 3478 index_type = f" USING {index_type}" if index_type else "" 3479 expressions = self.expressions(expression, flat=True) 3480 expressions = f" ({expressions})" if expressions else "" 3481 options = self.expressions(expression, key="options", sep=" ") 3482 options = f" {options}" if options else "" 3483 return f"{kind}{this}{index_type}{expressions}{options}" 3484 3485 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3486 if self.NVL2_SUPPORTED: 3487 return self.function_fallback_sql(expression) 3488 3489 case = exp.Case().when( 3490 expression.this.is_(exp.null()).not_(copy=False), 3491 expression.args["true"], 3492 copy=False, 3493 ) 3494 else_cond = expression.args.get("false") 3495 if else_cond: 3496 case.else_(else_cond, copy=False) 3497 3498 return self.sql(case) 3499 3500 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3501 this = self.sql(expression, "this") 3502 expr = self.sql(expression, "expression") 3503 iterator = self.sql(expression, "iterator") 3504 condition = self.sql(expression, "condition") 3505 condition = f" IF {condition}" if condition else "" 3506 return f"{this} FOR {expr} IN {iterator}{condition}" 3507 3508 def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str: 3509 return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})" 3510 3511 def opclass_sql(self, expression: exp.Opclass) -> str: 3512 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 3513 3514 def predict_sql(self, expression: exp.Predict) -> str: 3515 model = self.sql(expression, "this") 3516 model = f"MODEL {model}" 3517 table = self.sql(expression, "expression") 3518 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3519 parameters = self.sql(expression, "params_struct") 3520 return self.func("PREDICT", model, table, parameters or None) 3521 3522 def forin_sql(self, expression: exp.ForIn) -> str: 3523 this = self.sql(expression, "this") 3524 expression_sql = self.sql(expression, "expression") 3525 return f"FOR {this} DO {expression_sql}" 3526 3527 def refresh_sql(self, expression: exp.Refresh) -> str: 3528 this = self.sql(expression, "this") 3529 table = "" if isinstance(expression.this, exp.Literal) else "TABLE " 3530 return f"REFRESH {table}{this}" 3531 3532 def operator_sql(self, expression: exp.Operator) -> str: 3533 return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})") 3534 3535 def toarray_sql(self, expression: exp.ToArray) -> str: 3536 arg = expression.this 3537 if not arg.type: 3538 from sqlglot.optimizer.annotate_types import annotate_types 3539 3540 arg = annotate_types(arg) 3541 3542 if arg.is_type(exp.DataType.Type.ARRAY): 3543 return self.sql(arg) 3544 3545 cond_for_null = arg.is_(exp.null()) 3546 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False))) 3547 3548 def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str: 3549 this = expression.this 3550 if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME): 3551 return self.sql(this) 3552 3553 return self.sql(exp.cast(this, exp.DataType.Type.TIME)) 3554 3555 def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str: 3556 this = expression.this 3557 if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP): 3558 return self.sql(this) 3559 3560 return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP)) 3561 3562 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3563 this = expression.this 3564 time_format = self.format_time(expression) 3565 3566 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3567 return self.sql( 3568 exp.cast( 3569 exp.StrToTime(this=this, format=expression.args["format"]), 3570 exp.DataType.Type.DATE, 3571 ) 3572 ) 3573 3574 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3575 return self.sql(this) 3576 3577 return self.sql(exp.cast(this, exp.DataType.Type.DATE)) 3578 3579 def unixdate_sql(self, expression: exp.UnixDate) -> str: 3580 return self.sql( 3581 exp.func( 3582 "DATEDIFF", 3583 expression.this, 3584 exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE), 3585 "day", 3586 ) 3587 ) 3588 3589 def lastday_sql(self, expression: exp.LastDay) -> str: 3590 if self.LAST_DAY_SUPPORTS_DATE_PART: 3591 return self.function_fallback_sql(expression) 3592 3593 unit = expression.text("unit") 3594 if unit and unit != "MONTH": 3595 self.unsupported("Date parts are not supported in LAST_DAY.") 3596 3597 return self.func("LAST_DAY", expression.this) 3598 3599 def dateadd_sql(self, expression: exp.DateAdd) -> str: 3600 from sqlglot.dialects.dialect import unit_to_str 3601 3602 return self.func( 3603 "DATE_ADD", expression.this, expression.expression, unit_to_str(expression) 3604 ) 3605 3606 def arrayany_sql(self, expression: exp.ArrayAny) -> str: 3607 if self.CAN_IMPLEMENT_ARRAY_ANY: 3608 filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression) 3609 filtered_not_empty = exp.ArraySize(this=filtered).neq(0) 3610 original_is_empty = exp.ArraySize(this=expression.this).eq(0) 3611 return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty))) 3612 3613 from sqlglot.dialects import Dialect 3614 3615 # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect 3616 if self.dialect.__class__ != Dialect: 3617 self.unsupported("ARRAY_ANY is unsupported") 3618 3619 return self.function_fallback_sql(expression) 3620 3621 def generateseries_sql(self, expression: exp.GenerateSeries) -> str: 3622 expression.set("is_end_exclusive", None) 3623 return self.function_fallback_sql(expression) 3624 3625 def struct_sql(self, expression: exp.Struct) -> str: 3626 expression.set( 3627 "expressions", 3628 [ 3629 exp.alias_(e.expression, e.name if e.this.is_string else e.this) 3630 if isinstance(e, exp.PropertyEQ) 3631 else e 3632 for e in expression.expressions 3633 ], 3634 ) 3635 3636 return self.function_fallback_sql(expression) 3637 3638 def partitionrange_sql(self, expression: exp.PartitionRange) -> str: 3639 low = self.sql(expression, "this") 3640 high = self.sql(expression, "expression") 3641 3642 return f"{low} TO {high}" 3643 3644 def truncatetable_sql(self, expression: exp.TruncateTable) -> str: 3645 target = "DATABASE" if expression.args.get("is_database") else "TABLE" 3646 tables = f" {self.expressions(expression)}" 3647 3648 exists = " IF EXISTS" if expression.args.get("exists") else "" 3649 3650 on_cluster = self.sql(expression, "cluster") 3651 on_cluster = f" {on_cluster}" if on_cluster else "" 3652 3653 identity = self.sql(expression, "identity") 3654 identity = f" {identity} IDENTITY" if identity else "" 3655 3656 option = self.sql(expression, "option") 3657 option = f" {option}" if option else "" 3658 3659 partition = self.sql(expression, "partition") 3660 partition = f" {partition}" if partition else "" 3661 3662 return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}" 3663 3664 # This transpiles T-SQL's CONVERT function 3665 # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16 3666 def convert_sql(self, expression: exp.Convert) -> str: 3667 to = expression.this 3668 value = expression.expression 3669 style = expression.args.get("style") 3670 safe = expression.args.get("safe") 3671 strict = expression.args.get("strict") 3672 3673 if not to or not value: 3674 return "" 3675 3676 # Retrieve length of datatype and override to default if not specified 3677 if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3678 to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False) 3679 3680 transformed: t.Optional[exp.Expression] = None 3681 cast = exp.Cast if strict else exp.TryCast 3682 3683 # Check whether a conversion with format (T-SQL calls this 'style') is applicable 3684 if isinstance(style, exp.Literal) and style.is_int: 3685 from sqlglot.dialects.tsql import TSQL 3686 3687 style_value = style.name 3688 converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value) 3689 if not converted_style: 3690 self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}") 3691 3692 fmt = exp.Literal.string(converted_style) 3693 3694 if to.this == exp.DataType.Type.DATE: 3695 transformed = exp.StrToDate(this=value, format=fmt) 3696 elif to.this == exp.DataType.Type.DATETIME: 3697 transformed = exp.StrToTime(this=value, format=fmt) 3698 elif to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3699 transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe) 3700 elif to.this == exp.DataType.Type.TEXT: 3701 transformed = exp.TimeToStr(this=value, format=fmt) 3702 3703 if not transformed: 3704 transformed = cast(this=value, to=to, safe=safe) 3705 3706 return self.sql(transformed) 3707 3708 def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str: 3709 this = expression.this 3710 if isinstance(this, exp.JSONPathWildcard): 3711 this = self.json_path_part(this) 3712 return f".{this}" if this else "" 3713 3714 if exp.SAFE_IDENTIFIER_RE.match(this): 3715 return f".{this}" 3716 3717 this = self.json_path_part(this) 3718 return f"[{this}]" if self.JSON_PATH_BRACKETED_KEY_SUPPORTED else f".{this}" 3719 3720 def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str: 3721 this = self.json_path_part(expression.this) 3722 return f"[{this}]" if this else "" 3723 3724 def _simplify_unless_literal(self, expression: E) -> E: 3725 if not isinstance(expression, exp.Literal): 3726 from sqlglot.optimizer.simplify import simplify 3727 3728 expression = simplify(expression, dialect=self.dialect) 3729 3730 return expression 3731 3732 def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str: 3733 if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"): 3734 # The first modifier here will be the one closest to the AggFunc's arg 3735 mods = sorted( 3736 expression.find_all(exp.HavingMax, exp.Order, exp.Limit), 3737 key=lambda x: 0 3738 if isinstance(x, exp.HavingMax) 3739 else (1 if isinstance(x, exp.Order) else 2), 3740 ) 3741 3742 if mods: 3743 mod = mods[0] 3744 this = expression.__class__(this=mod.this.copy()) 3745 this.meta["inline"] = True 3746 mod.this.replace(this) 3747 return self.sql(expression.this) 3748 3749 agg_func = expression.find(exp.AggFunc) 3750 3751 if agg_func: 3752 return self.sql(agg_func)[:-1] + f" {text})" 3753 3754 return f"{self.sql(expression, 'this')} {text}" 3755 3756 def _replace_line_breaks(self, string: str) -> str: 3757 """We don't want to extra indent line breaks so we temporarily replace them with sentinels.""" 3758 if self.pretty: 3759 return string.replace("\n", self.SENTINEL_LINE_BREAK) 3760 return string
Generator converts a given syntax tree to the corresponding SQL string.
Arguments:
- pretty: Whether to format the produced SQL string. Default: False.
- identify: Determines when an identifier should be quoted. Possible values are: False (default): Never quote, except in cases where it's mandatory by the dialect. True or 'always': Always quote. 'safe': Only quote identifiers that are case insensitive.
- normalize: Whether to normalize identifiers to lowercase. Default: False.
- pad: The pad size in a formatted string. For example, this affects the indentation of a projection in a query, relative to its nesting level. Default: 2.
- indent: The indentation size in a formatted string. For example, this affects the
indentation of subqueries and filters under a
WHERE
clause. Default: 2. - normalize_functions: How to normalize function names. Possible values are: "upper" or True (default): Convert names to uppercase. "lower": Convert names to lowercase. False: Disables function name normalization.
- unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. Default ErrorLevel.WARN.
- max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. This is only relevant if unsupported_level is ErrorLevel.RAISE. Default: 3
- leading_comma: Whether the comma is leading or trailing in select expressions. This is only relevant when generating in pretty mode. Default: False
- max_text_width: The max number of characters in a segment before creating new lines in pretty mode. The default is on the smaller end because the length only represents a segment and not the true line length. Default: 80
- comments: Whether to preserve comments in the output SQL code. Default: True
Generator( pretty: Optional[bool] = None, identify: str | bool = False, normalize: bool = False, pad: int = 2, indent: int = 2, normalize_functions: Union[str, bool, NoneType] = None, unsupported_level: sqlglot.errors.ErrorLevel = <ErrorLevel.WARN: 'WARN'>, max_unsupported: int = 3, leading_comma: bool = False, max_text_width: int = 80, comments: bool = True, dialect: Union[str, sqlglot.dialects.dialect.Dialect, Type[sqlglot.dialects.dialect.Dialect], NoneType] = None)
533 def __init__( 534 self, 535 pretty: t.Optional[bool] = None, 536 identify: str | bool = False, 537 normalize: bool = False, 538 pad: int = 2, 539 indent: int = 2, 540 normalize_functions: t.Optional[str | bool] = None, 541 unsupported_level: ErrorLevel = ErrorLevel.WARN, 542 max_unsupported: int = 3, 543 leading_comma: bool = False, 544 max_text_width: int = 80, 545 comments: bool = True, 546 dialect: DialectType = None, 547 ): 548 import sqlglot 549 from sqlglot.dialects import Dialect 550 551 self.pretty = pretty if pretty is not None else sqlglot.pretty 552 self.identify = identify 553 self.normalize = normalize 554 self.pad = pad 555 self._indent = indent 556 self.unsupported_level = unsupported_level 557 self.max_unsupported = max_unsupported 558 self.leading_comma = leading_comma 559 self.max_text_width = max_text_width 560 self.comments = comments 561 self.dialect = Dialect.get_or_raise(dialect) 562 563 # This is both a Dialect property and a Generator argument, so we prioritize the latter 564 self.normalize_functions = ( 565 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 566 ) 567 568 self.unsupported_messages: t.List[str] = [] 569 self._escaped_quote_end: str = ( 570 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 571 ) 572 self._escaped_identifier_end: str = ( 573 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 574 )
TRANSFORMS: Dict[Type[sqlglot.expressions.Expression], Callable[..., str]] =
{<class 'sqlglot.expressions.JSONPathFilter'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathKey'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRecursive'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRoot'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathScript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSelector'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSlice'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSubscript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathUnion'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathWildcard'>: <function <lambda>>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.BackupProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CaseSpecificColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CommentColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DateFormatColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DefaultColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EphemeralColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExcludeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.GlobalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IcebergProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InheritsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONExtract'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONExtractScalar'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetConfigProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SharingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StabilityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Timestamp'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UnloggedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithOperator'>: <function Generator.<lambda>>}
SUPPORTED_JSON_PATH_PARTS =
{<class 'sqlglot.expressions.JSONPathScript'>, <class 'sqlglot.expressions.JSONPathRoot'>, <class 'sqlglot.expressions.JSONPathRecursive'>, <class 'sqlglot.expressions.JSONPathKey'>, <class 'sqlglot.expressions.JSONPathWildcard'>, <class 'sqlglot.expressions.JSONPathFilter'>, <class 'sqlglot.expressions.JSONPathUnion'>, <class 'sqlglot.expressions.JSONPathSubscript'>, <class 'sqlglot.expressions.JSONPathSelector'>, <class 'sqlglot.expressions.JSONPathSlice'>}
TYPE_MAPPING =
{<Type.NCHAR: 'NCHAR'>: 'CHAR', <Type.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <Type.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <Type.LONGTEXT: 'LONGTEXT'>: 'TEXT', <Type.TINYTEXT: 'TINYTEXT'>: 'TEXT', <Type.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <Type.LONGBLOB: 'LONGBLOB'>: 'BLOB', <Type.TINYBLOB: 'TINYBLOB'>: 'BLOB', <Type.INET: 'INET'>: 'INET', <Type.ROWVERSION: 'ROWVERSION'>: 'VARBINARY'}
TIME_PART_SINGULARS =
{'MICROSECONDS': 'MICROSECOND', 'SECONDS': 'SECOND', 'MINUTES': 'MINUTE', 'HOURS': 'HOUR', 'DAYS': 'DAY', 'WEEKS': 'WEEK', 'MONTHS': 'MONTH', 'QUARTERS': 'QUARTER', 'YEARS': 'YEAR'}
AFTER_HAVING_MODIFIER_TRANSFORMS =
{'cluster': <function Generator.<lambda>>, 'distribute': <function Generator.<lambda>>, 'qualify': <function Generator.<lambda>>, 'sort': <function Generator.<lambda>>, 'windows': <function Generator.<lambda>>}
PROPERTIES_LOCATION =
{<class 'sqlglot.expressions.AlgorithmProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.AutoIncrementProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BackupProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BlockCompressionProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CharacterSetProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ChecksumProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CollateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Cluster'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ClusteredByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DataBlocksizeProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.DefinerProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DictRange'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistStyleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EngineProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExternalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.FallbackProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.FileFormatProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.FreespaceProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.GlobalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.HeapProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.InheritsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IcebergProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.InputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IsolatedLoadingProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.JournalProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.LanguageProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LikeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LocationProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockingProperty'>: <Location.POST_ALIAS: 'POST_ALIAS'>, <class 'sqlglot.expressions.LogProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.MaterializedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.MergeBlockRatioProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.OnProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OnCommitProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.Order'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OutputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PartitionedByProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.PartitionedOfProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ReturnsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatDelimitedProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatSerdeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SampleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SchemaCommentProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SerdeProperties'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Set'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SettingsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SetProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SetConfigProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SharingProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SequenceProperties'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SortKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StabilityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TemporaryProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ToTableProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TransientProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.TransformModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.MergeTreeTTL'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.UnloggedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.VolatileProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.WithDataProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.WithSystemVersioningProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>}
WITH_SEPARATED_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] =
(<class 'sqlglot.expressions.Create'>, <class 'sqlglot.expressions.Delete'>, <class 'sqlglot.expressions.Drop'>, <class 'sqlglot.expressions.From'>, <class 'sqlglot.expressions.Insert'>, <class 'sqlglot.expressions.Join'>, <class 'sqlglot.expressions.Select'>, <class 'sqlglot.expressions.Union'>, <class 'sqlglot.expressions.Update'>, <class 'sqlglot.expressions.Where'>, <class 'sqlglot.expressions.With'>)
EXCLUDE_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] =
(<class 'sqlglot.expressions.Binary'>, <class 'sqlglot.expressions.Union'>)
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] =
(<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
PARAMETERIZABLE_TEXT_TYPES =
{<Type.VARCHAR: 'VARCHAR'>, <Type.CHAR: 'CHAR'>, <Type.NCHAR: 'NCHAR'>, <Type.NVARCHAR: 'NVARCHAR'>}
576 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 577 """ 578 Generates the SQL string corresponding to the given syntax tree. 579 580 Args: 581 expression: The syntax tree. 582 copy: Whether to copy the expression. The generator performs mutations so 583 it is safer to copy. 584 585 Returns: 586 The SQL string corresponding to `expression`. 587 """ 588 if copy: 589 expression = expression.copy() 590 591 expression = self.preprocess(expression) 592 593 self.unsupported_messages = [] 594 sql = self.sql(expression).strip() 595 596 if self.pretty: 597 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 598 599 if self.unsupported_level == ErrorLevel.IGNORE: 600 return sql 601 602 if self.unsupported_level == ErrorLevel.WARN: 603 for msg in self.unsupported_messages: 604 logger.warning(msg) 605 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 606 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 607 608 return sql
Generates the SQL string corresponding to the given syntax tree.
Arguments:
- expression: The syntax tree.
- copy: Whether to copy the expression. The generator performs mutations so it is safer to copy.
Returns:
The SQL string corresponding to
expression
.
def
preprocess( self, expression: sqlglot.expressions.Expression) -> sqlglot.expressions.Expression:
610 def preprocess(self, expression: exp.Expression) -> exp.Expression: 611 """Apply generic preprocessing transformations to a given expression.""" 612 if ( 613 not expression.parent 614 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 615 and any(node.parent is not expression for node in expression.find_all(exp.With)) 616 ): 617 from sqlglot.transforms import move_ctes_to_top_level 618 619 expression = move_ctes_to_top_level(expression) 620 621 if self.ENSURE_BOOLS: 622 from sqlglot.transforms import ensure_bools 623 624 expression = ensure_bools(expression) 625 626 return expression
Apply generic preprocessing transformations to a given expression.
def
maybe_comment( self, sql: str, expression: Optional[sqlglot.expressions.Expression] = None, comments: Optional[List[str]] = None, separated: bool = False) -> str:
644 def maybe_comment( 645 self, 646 sql: str, 647 expression: t.Optional[exp.Expression] = None, 648 comments: t.Optional[t.List[str]] = None, 649 separated: bool = False, 650 ) -> str: 651 comments = ( 652 ((expression and expression.comments) if comments is None else comments) # type: ignore 653 if self.comments 654 else None 655 ) 656 657 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 658 return sql 659 660 comments_sql = " ".join( 661 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 662 ) 663 664 if not comments_sql: 665 return sql 666 667 comments_sql = self._replace_line_breaks(comments_sql) 668 669 if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS): 670 return ( 671 f"{self.sep()}{comments_sql}{sql}" 672 if not sql or sql[0].isspace() 673 else f"{comments_sql}{self.sep()}{sql}" 674 ) 675 676 return f"{sql} {comments_sql}"
678 def wrap(self, expression: exp.Expression | str) -> str: 679 this_sql = self.indent( 680 ( 681 self.sql(expression) 682 if isinstance(expression, exp.UNWRAPPED_QUERIES) 683 else self.sql(expression, "this") 684 ), 685 level=1, 686 pad=0, 687 ) 688 return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}"
def
indent( self, sql: str, level: int = 0, pad: Optional[int] = None, skip_first: bool = False, skip_last: bool = False) -> str:
704 def indent( 705 self, 706 sql: str, 707 level: int = 0, 708 pad: t.Optional[int] = None, 709 skip_first: bool = False, 710 skip_last: bool = False, 711 ) -> str: 712 if not self.pretty: 713 return sql 714 715 pad = self.pad if pad is None else pad 716 lines = sql.split("\n") 717 718 return "\n".join( 719 ( 720 line 721 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 722 else f"{' ' * (level * self._indent + pad)}{line}" 723 ) 724 for i, line in enumerate(lines) 725 )
def
sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str:
727 def sql( 728 self, 729 expression: t.Optional[str | exp.Expression], 730 key: t.Optional[str] = None, 731 comment: bool = True, 732 ) -> str: 733 if not expression: 734 return "" 735 736 if isinstance(expression, str): 737 return expression 738 739 if key: 740 value = expression.args.get(key) 741 if value: 742 return self.sql(value) 743 return "" 744 745 transform = self.TRANSFORMS.get(expression.__class__) 746 747 if callable(transform): 748 sql = transform(self, expression) 749 elif isinstance(expression, exp.Expression): 750 exp_handler_name = f"{expression.key}_sql" 751 752 if hasattr(self, exp_handler_name): 753 sql = getattr(self, exp_handler_name)(expression) 754 elif isinstance(expression, exp.Func): 755 sql = self.function_fallback_sql(expression) 756 elif isinstance(expression, exp.Property): 757 sql = self.property_sql(expression) 758 else: 759 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 760 else: 761 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 762 763 return self.maybe_comment(sql, expression) if self.comments and comment else sql
770 def cache_sql(self, expression: exp.Cache) -> str: 771 lazy = " LAZY" if expression.args.get("lazy") else "" 772 table = self.sql(expression, "this") 773 options = expression.args.get("options") 774 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 775 sql = self.sql(expression, "expression") 776 sql = f" AS{self.sep()}{sql}" if sql else "" 777 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 778 return self.prepend_ctes(expression, sql)
780 def characterset_sql(self, expression: exp.CharacterSet) -> str: 781 if isinstance(expression.parent, exp.Cast): 782 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 783 default = "DEFAULT " if expression.args.get("default") else "" 784 return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
798 def column_sql(self, expression: exp.Column) -> str: 799 join_mark = " (+)" if expression.args.get("join_mark") else "" 800 801 if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED: 802 join_mark = "" 803 self.unsupported("Outer join syntax using the (+) operator is not supported.") 804 805 return f"{self.column_parts(expression)}{join_mark}"
813 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 814 column = self.sql(expression, "this") 815 kind = self.sql(expression, "kind") 816 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 817 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 818 kind = f"{sep}{kind}" if kind else "" 819 constraints = f" {constraints}" if constraints else "" 820 position = self.sql(expression, "position") 821 position = f" {position}" if position else "" 822 823 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 824 kind = "" 825 826 return f"{exists}{column}{kind}{constraints}{position}"
def
computedcolumnconstraint_sql(self, expression: sqlglot.expressions.ComputedColumnConstraint) -> str:
833 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 834 this = self.sql(expression, "this") 835 if expression.args.get("not_null"): 836 persisted = " PERSISTED NOT NULL" 837 elif expression.args.get("persisted"): 838 persisted = " PERSISTED" 839 else: 840 persisted = "" 841 return f"AS {this}{persisted}"
def
compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str:
def
generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str:
854 def generatedasidentitycolumnconstraint_sql( 855 self, expression: exp.GeneratedAsIdentityColumnConstraint 856 ) -> str: 857 this = "" 858 if expression.this is not None: 859 on_null = " ON NULL" if expression.args.get("on_null") else "" 860 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 861 862 start = expression.args.get("start") 863 start = f"START WITH {start}" if start else "" 864 increment = expression.args.get("increment") 865 increment = f" INCREMENT BY {increment}" if increment else "" 866 minvalue = expression.args.get("minvalue") 867 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 868 maxvalue = expression.args.get("maxvalue") 869 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 870 cycle = expression.args.get("cycle") 871 cycle_sql = "" 872 873 if cycle is not None: 874 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 875 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 876 877 sequence_opts = "" 878 if start or increment or cycle_sql: 879 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 880 sequence_opts = f" ({sequence_opts.strip()})" 881 882 expr = self.sql(expression, "expression") 883 expr = f"({expr})" if expr else "IDENTITY" 884 885 return f"GENERATED{this} AS {expr}{sequence_opts}"
def
generatedasrowcolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsRowColumnConstraint) -> str:
def
periodforsystemtimeconstraint_sql( self, expression: sqlglot.expressions.PeriodForSystemTimeConstraint) -> str:
def
notnullcolumnconstraint_sql(self, expression: sqlglot.expressions.NotNullColumnConstraint) -> str:
def
transformcolumnconstraint_sql(self, expression: sqlglot.expressions.TransformColumnConstraint) -> str:
def
primarykeycolumnconstraint_sql(self, expression: sqlglot.expressions.PrimaryKeyColumnConstraint) -> str:
def
uniquecolumnconstraint_sql(self, expression: sqlglot.expressions.UniqueColumnConstraint) -> str:
911 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 912 this = self.sql(expression, "this") 913 this = f" {this}" if this else "" 914 index_type = expression.args.get("index_type") 915 index_type = f" USING {index_type}" if index_type else "" 916 on_conflict = self.sql(expression, "on_conflict") 917 on_conflict = f" {on_conflict}" if on_conflict else "" 918 return f"UNIQUE{this}{index_type}{on_conflict}"
923 def create_sql(self, expression: exp.Create) -> str: 924 kind = self.sql(expression, "kind") 925 properties = expression.args.get("properties") 926 properties_locs = self.locate_properties(properties) if properties else defaultdict() 927 928 this = self.createable_sql(expression, properties_locs) 929 930 properties_sql = "" 931 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 932 exp.Properties.Location.POST_WITH 933 ): 934 properties_sql = self.sql( 935 exp.Properties( 936 expressions=[ 937 *properties_locs[exp.Properties.Location.POST_SCHEMA], 938 *properties_locs[exp.Properties.Location.POST_WITH], 939 ] 940 ) 941 ) 942 943 begin = " BEGIN" if expression.args.get("begin") else "" 944 end = " END" if expression.args.get("end") else "" 945 946 expression_sql = self.sql(expression, "expression") 947 if expression_sql: 948 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 949 950 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 951 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 952 postalias_props_sql = self.properties( 953 exp.Properties( 954 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 955 ), 956 wrapped=False, 957 ) 958 expression_sql = f" AS {postalias_props_sql}{expression_sql}" 959 else: 960 expression_sql = f" AS{expression_sql}" 961 962 postindex_props_sql = "" 963 if properties_locs.get(exp.Properties.Location.POST_INDEX): 964 postindex_props_sql = self.properties( 965 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 966 wrapped=False, 967 prefix=" ", 968 ) 969 970 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 971 indexes = f" {indexes}" if indexes else "" 972 index_sql = indexes + postindex_props_sql 973 974 replace = " OR REPLACE" if expression.args.get("replace") else "" 975 unique = " UNIQUE" if expression.args.get("unique") else "" 976 977 postcreate_props_sql = "" 978 if properties_locs.get(exp.Properties.Location.POST_CREATE): 979 postcreate_props_sql = self.properties( 980 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 981 sep=" ", 982 prefix=" ", 983 wrapped=False, 984 ) 985 986 modifiers = "".join((replace, unique, postcreate_props_sql)) 987 988 postexpression_props_sql = "" 989 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 990 postexpression_props_sql = self.properties( 991 exp.Properties( 992 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 993 ), 994 sep=" ", 995 prefix=" ", 996 wrapped=False, 997 ) 998 999 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 1000 no_schema_binding = ( 1001 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 1002 ) 1003 1004 clone = self.sql(expression, "clone") 1005 clone = f" {clone}" if clone else "" 1006 1007 expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 1008 return self.prepend_ctes(expression, expression_sql)
1010 def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str: 1011 start = self.sql(expression, "start") 1012 start = f"START WITH {start}" if start else "" 1013 increment = self.sql(expression, "increment") 1014 increment = f" INCREMENT BY {increment}" if increment else "" 1015 minvalue = self.sql(expression, "minvalue") 1016 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 1017 maxvalue = self.sql(expression, "maxvalue") 1018 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 1019 owned = self.sql(expression, "owned") 1020 owned = f" OWNED BY {owned}" if owned else "" 1021 1022 cache = expression.args.get("cache") 1023 if cache is None: 1024 cache_str = "" 1025 elif cache is True: 1026 cache_str = " CACHE" 1027 else: 1028 cache_str = f" CACHE {cache}" 1029 1030 options = self.expressions(expression, key="options", flat=True, sep=" ") 1031 options = f" {options}" if options else "" 1032 1033 return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
1035 def clone_sql(self, expression: exp.Clone) -> str: 1036 this = self.sql(expression, "this") 1037 shallow = "SHALLOW " if expression.args.get("shallow") else "" 1038 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 1039 return f"{shallow}{keyword} {this}"
1066 def cte_sql(self, expression: exp.CTE) -> str: 1067 alias = self.sql(expression, "alias") 1068 1069 materialized = expression.args.get("materialized") 1070 if materialized is False: 1071 materialized = "NOT MATERIALIZED " 1072 elif materialized: 1073 materialized = "MATERIALIZED " 1074 1075 return f"{alias} AS {materialized or ''}{self.wrap(expression)}"
1077 def tablealias_sql(self, expression: exp.TableAlias) -> str: 1078 alias = self.sql(expression, "this") 1079 columns = self.expressions(expression, key="columns", flat=True) 1080 columns = f"({columns})" if columns else "" 1081 1082 if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS: 1083 columns = "" 1084 self.unsupported("Named columns are not supported in table alias.") 1085 1086 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 1087 alias = "_t" 1088 1089 return f"{alias}{columns}"
1109 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 1110 this = self.sql(expression, "this") 1111 escape = expression.args.get("escape") 1112 1113 if self.dialect.UNICODE_START: 1114 escape = f" UESCAPE {self.sql(escape)}" if escape else "" 1115 return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}" 1116 1117 if escape: 1118 pattern = re.compile(rf"{escape.name}(\d+)") 1119 else: 1120 pattern = ESCAPED_UNICODE_RE 1121 1122 this = pattern.sub(r"\\u\1", this) 1123 return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}"
1135 def datatype_sql(self, expression: exp.DataType) -> str: 1136 type_value = expression.this 1137 1138 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 1139 type_sql = self.sql(expression, "kind") 1140 else: 1141 type_sql = ( 1142 self.TYPE_MAPPING.get(type_value, type_value.value) 1143 if isinstance(type_value, exp.DataType.Type) 1144 else type_value 1145 ) 1146 1147 nested = "" 1148 interior = self.expressions(expression, flat=True) 1149 values = "" 1150 1151 if interior: 1152 if expression.args.get("nested"): 1153 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 1154 if expression.args.get("values") is not None: 1155 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 1156 values = self.expressions(expression, key="values", flat=True) 1157 values = f"{delimiters[0]}{values}{delimiters[1]}" 1158 elif type_value == exp.DataType.Type.INTERVAL: 1159 nested = f" {interior}" 1160 else: 1161 nested = f"({interior})" 1162 1163 type_sql = f"{type_sql}{nested}{values}" 1164 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 1165 exp.DataType.Type.TIMETZ, 1166 exp.DataType.Type.TIMESTAMPTZ, 1167 ): 1168 type_sql = f"{type_sql} WITH TIME ZONE" 1169 1170 return type_sql
1172 def directory_sql(self, expression: exp.Directory) -> str: 1173 local = "LOCAL " if expression.args.get("local") else "" 1174 row_format = self.sql(expression, "row_format") 1175 row_format = f" {row_format}" if row_format else "" 1176 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
1178 def delete_sql(self, expression: exp.Delete) -> str: 1179 this = self.sql(expression, "this") 1180 this = f" FROM {this}" if this else "" 1181 using = self.sql(expression, "using") 1182 using = f" USING {using}" if using else "" 1183 where = self.sql(expression, "where") 1184 returning = self.sql(expression, "returning") 1185 limit = self.sql(expression, "limit") 1186 tables = self.expressions(expression, key="tables") 1187 tables = f" {tables}" if tables else "" 1188 if self.RETURNING_END: 1189 expression_sql = f"{this}{using}{where}{returning}{limit}" 1190 else: 1191 expression_sql = f"{returning}{this}{using}{where}{limit}" 1192 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
1194 def drop_sql(self, expression: exp.Drop) -> str: 1195 this = self.sql(expression, "this") 1196 expressions = self.expressions(expression, flat=True) 1197 expressions = f" ({expressions})" if expressions else "" 1198 kind = expression.args["kind"] 1199 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1200 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1201 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1202 cascade = " CASCADE" if expression.args.get("cascade") else "" 1203 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1204 purge = " PURGE" if expression.args.get("purge") else "" 1205 return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{expressions}{cascade}{constraints}{purge}"
1213 def fetch_sql(self, expression: exp.Fetch) -> str: 1214 direction = expression.args.get("direction") 1215 direction = f" {direction}" if direction else "" 1216 count = expression.args.get("count") 1217 count = f" {count}" if count else "" 1218 if expression.args.get("percent"): 1219 count = f"{count} PERCENT" 1220 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1221 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
1223 def filter_sql(self, expression: exp.Filter) -> str: 1224 if self.AGGREGATE_FILTER_SUPPORTED: 1225 this = self.sql(expression, "this") 1226 where = self.sql(expression, "expression").strip() 1227 return f"{this} FILTER({where})" 1228 1229 agg = expression.this 1230 agg_arg = agg.this 1231 cond = expression.expression.this 1232 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1233 return self.sql(agg)
1242 def indexparameters_sql(self, expression: exp.IndexParameters) -> str: 1243 using = self.sql(expression, "using") 1244 using = f" USING {using}" if using else "" 1245 columns = self.expressions(expression, key="columns", flat=True) 1246 columns = f"({columns})" if columns else "" 1247 partition_by = self.expressions(expression, key="partition_by", flat=True) 1248 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1249 where = self.sql(expression, "where") 1250 include = self.expressions(expression, key="include", flat=True) 1251 if include: 1252 include = f" INCLUDE ({include})" 1253 with_storage = self.expressions(expression, key="with_storage", flat=True) 1254 with_storage = f" WITH ({with_storage})" if with_storage else "" 1255 tablespace = self.sql(expression, "tablespace") 1256 tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else "" 1257 1258 return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}"
1260 def index_sql(self, expression: exp.Index) -> str: 1261 unique = "UNIQUE " if expression.args.get("unique") else "" 1262 primary = "PRIMARY " if expression.args.get("primary") else "" 1263 amp = "AMP " if expression.args.get("amp") else "" 1264 name = self.sql(expression, "this") 1265 name = f"{name} " if name else "" 1266 table = self.sql(expression, "table") 1267 table = f"{self.INDEX_ON} {table}" if table else "" 1268 1269 index = "INDEX " if not table else "" 1270 1271 params = self.sql(expression, "params") 1272 return f"{unique}{primary}{amp}{index}{name}{table}{params}"
1274 def identifier_sql(self, expression: exp.Identifier) -> str: 1275 text = expression.name 1276 lower = text.lower() 1277 text = lower if self.normalize and not expression.quoted else text 1278 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1279 if ( 1280 expression.quoted 1281 or self.dialect.can_identify(text, self.identify) 1282 or lower in self.RESERVED_KEYWORDS 1283 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1284 ): 1285 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1286 return text
1288 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1289 input_format = self.sql(expression, "input_format") 1290 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1291 output_format = self.sql(expression, "output_format") 1292 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1293 return self.sep().join((input_format, output_format))
1302 def properties_sql(self, expression: exp.Properties) -> str: 1303 root_properties = [] 1304 with_properties = [] 1305 1306 for p in expression.expressions: 1307 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1308 if p_loc == exp.Properties.Location.POST_WITH: 1309 with_properties.append(p) 1310 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1311 root_properties.append(p) 1312 1313 return self.root_properties( 1314 exp.Properties(expressions=root_properties) 1315 ) + self.with_properties(exp.Properties(expressions=with_properties))
def
properties( self, properties: sqlglot.expressions.Properties, prefix: str = '', sep: str = ', ', suffix: str = '', wrapped: bool = True) -> str:
1322 def properties( 1323 self, 1324 properties: exp.Properties, 1325 prefix: str = "", 1326 sep: str = ", ", 1327 suffix: str = "", 1328 wrapped: bool = True, 1329 ) -> str: 1330 if properties.expressions: 1331 expressions = self.expressions(properties, sep=sep, indent=False) 1332 if expressions: 1333 expressions = self.wrap(expressions) if wrapped else expressions 1334 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1335 return ""
1340 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1341 properties_locs = defaultdict(list) 1342 for p in properties.expressions: 1343 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1344 if p_loc != exp.Properties.Location.UNSUPPORTED: 1345 properties_locs[p_loc].append(p) 1346 else: 1347 self.unsupported(f"Unsupported property {p.key}") 1348 1349 return properties_locs
def
property_name( self, expression: sqlglot.expressions.Property, string_key: bool = False) -> str:
1356 def property_sql(self, expression: exp.Property) -> str: 1357 property_cls = expression.__class__ 1358 if property_cls == exp.Property: 1359 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1360 1361 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1362 if not property_name: 1363 self.unsupported(f"Unsupported property {expression.key}") 1364 1365 return f"{property_name}={self.sql(expression, 'this')}"
1367 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1368 if self.SUPPORTS_CREATE_TABLE_LIKE: 1369 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1370 options = f" {options}" if options else "" 1371 1372 like = f"LIKE {self.sql(expression, 'this')}{options}" 1373 if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema): 1374 like = f"({like})" 1375 1376 return like 1377 1378 if expression.expressions: 1379 self.unsupported("Transpilation of LIKE property options is unsupported") 1380 1381 select = exp.select("*").from_(expression.this).limit(0) 1382 return f"AS {self.sql(select)}"
1389 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1390 no = "NO " if expression.args.get("no") else "" 1391 local = expression.args.get("local") 1392 local = f"{local} " if local else "" 1393 dual = "DUAL " if expression.args.get("dual") else "" 1394 before = "BEFORE " if expression.args.get("before") else "" 1395 after = "AFTER " if expression.args.get("after") else "" 1396 return f"{no}{local}{dual}{before}{after}JOURNAL"
def
mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str:
1412 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1413 if expression.args.get("no"): 1414 return "NO MERGEBLOCKRATIO" 1415 if expression.args.get("default"): 1416 return "DEFAULT MERGEBLOCKRATIO" 1417 1418 percent = " PERCENT" if expression.args.get("percent") else "" 1419 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
1421 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1422 default = expression.args.get("default") 1423 minimum = expression.args.get("minimum") 1424 maximum = expression.args.get("maximum") 1425 if default or minimum or maximum: 1426 if default: 1427 prop = "DEFAULT" 1428 elif minimum: 1429 prop = "MINIMUM" 1430 else: 1431 prop = "MAXIMUM" 1432 return f"{prop} DATABLOCKSIZE" 1433 units = expression.args.get("units") 1434 units = f" {units}" if units else "" 1435 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
def
blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str:
1437 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1438 autotemp = expression.args.get("autotemp") 1439 always = expression.args.get("always") 1440 default = expression.args.get("default") 1441 manual = expression.args.get("manual") 1442 never = expression.args.get("never") 1443 1444 if autotemp is not None: 1445 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1446 elif always: 1447 prop = "ALWAYS" 1448 elif default: 1449 prop = "DEFAULT" 1450 elif manual: 1451 prop = "MANUAL" 1452 elif never: 1453 prop = "NEVER" 1454 return f"BLOCKCOMPRESSION={prop}"
def
isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str:
1456 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1457 no = expression.args.get("no") 1458 no = " NO" if no else "" 1459 concurrent = expression.args.get("concurrent") 1460 concurrent = " CONCURRENT" if concurrent else "" 1461 target = self.sql(expression, "target") 1462 target = f" {target}" if target else "" 1463 return f"WITH{no}{concurrent} ISOLATED LOADING{target}"
1465 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1466 if isinstance(expression.this, list): 1467 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1468 if expression.this: 1469 modulus = self.sql(expression, "this") 1470 remainder = self.sql(expression, "expression") 1471 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1472 1473 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1474 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1475 return f"FROM ({from_expressions}) TO ({to_expressions})"
1477 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1478 this = self.sql(expression, "this") 1479 1480 for_values_or_default = expression.expression 1481 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1482 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1483 else: 1484 for_values_or_default = " DEFAULT" 1485 1486 return f"PARTITION OF {this}{for_values_or_default}"
1488 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1489 kind = expression.args.get("kind") 1490 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1491 for_or_in = expression.args.get("for_or_in") 1492 for_or_in = f" {for_or_in}" if for_or_in else "" 1493 lock_type = expression.args.get("lock_type") 1494 override = " OVERRIDE" if expression.args.get("override") else "" 1495 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
1497 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1498 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1499 statistics = expression.args.get("statistics") 1500 statistics_sql = "" 1501 if statistics is not None: 1502 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1503 return f"{data_sql}{statistics_sql}"
def
withsystemversioningproperty_sql( self, expression: sqlglot.expressions.WithSystemVersioningProperty) -> str:
1505 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1506 sql = "WITH(SYSTEM_VERSIONING=ON" 1507 1508 if expression.this: 1509 history_table = self.sql(expression, "this") 1510 sql = f"{sql}(HISTORY_TABLE={history_table}" 1511 1512 if expression.expression: 1513 data_consistency_check = self.sql(expression, "expression") 1514 sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}" 1515 1516 sql = f"{sql})" 1517 1518 return f"{sql})"
1520 def insert_sql(self, expression: exp.Insert) -> str: 1521 hint = self.sql(expression, "hint") 1522 overwrite = expression.args.get("overwrite") 1523 1524 if isinstance(expression.this, exp.Directory): 1525 this = " OVERWRITE" if overwrite else " INTO" 1526 else: 1527 this = self.INSERT_OVERWRITE if overwrite else " INTO" 1528 1529 stored = self.sql(expression, "stored") 1530 stored = f" {stored}" if stored else "" 1531 alternative = expression.args.get("alternative") 1532 alternative = f" OR {alternative}" if alternative else "" 1533 ignore = " IGNORE" if expression.args.get("ignore") else "" 1534 is_function = expression.args.get("is_function") 1535 if is_function: 1536 this = f"{this} FUNCTION" 1537 this = f"{this} {self.sql(expression, 'this')}" 1538 1539 exists = " IF EXISTS" if expression.args.get("exists") else "" 1540 where = self.sql(expression, "where") 1541 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1542 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1543 on_conflict = self.sql(expression, "conflict") 1544 on_conflict = f" {on_conflict}" if on_conflict else "" 1545 by_name = " BY NAME" if expression.args.get("by_name") else "" 1546 returning = self.sql(expression, "returning") 1547 1548 if self.RETURNING_END: 1549 expression_sql = f"{expression_sql}{on_conflict}{returning}" 1550 else: 1551 expression_sql = f"{returning}{expression_sql}{on_conflict}" 1552 1553 sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{where}{expression_sql}" 1554 return self.prepend_ctes(expression, sql)
1578 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1579 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1580 1581 constraint = self.sql(expression, "constraint") 1582 constraint = f" ON CONSTRAINT {constraint}" if constraint else "" 1583 1584 conflict_keys = self.expressions(expression, key="conflict_keys", flat=True) 1585 conflict_keys = f"({conflict_keys}) " if conflict_keys else " " 1586 action = self.sql(expression, "action") 1587 1588 expressions = self.expressions(expression, flat=True) 1589 if expressions: 1590 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1591 expressions = f" {set_keyword}{expressions}" 1592 1593 return f"{conflict}{constraint}{conflict_keys}{action}{expressions}"
def
rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str:
1598 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1599 fields = self.sql(expression, "fields") 1600 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1601 escaped = self.sql(expression, "escaped") 1602 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1603 items = self.sql(expression, "collection_items") 1604 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1605 keys = self.sql(expression, "map_keys") 1606 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1607 lines = self.sql(expression, "lines") 1608 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1609 null = self.sql(expression, "null") 1610 null = f" NULL DEFINED AS {null}" if null else "" 1611 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
1639 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1640 table = self.table_parts(expression) 1641 only = "ONLY " if expression.args.get("only") else "" 1642 partition = self.sql(expression, "partition") 1643 partition = f" {partition}" if partition else "" 1644 version = self.sql(expression, "version") 1645 version = f" {version}" if version else "" 1646 alias = self.sql(expression, "alias") 1647 alias = f"{sep}{alias}" if alias else "" 1648 hints = self.expressions(expression, key="hints", sep=" ") 1649 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1650 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 1651 pivots = f" {pivots}" if pivots else "" 1652 joins = self.indent( 1653 self.expressions(expression, key="joins", sep="", flat=True), skip_first=True 1654 ) 1655 laterals = self.expressions(expression, key="laterals", sep="") 1656 1657 file_format = self.sql(expression, "format") 1658 if file_format: 1659 pattern = self.sql(expression, "pattern") 1660 pattern = f", PATTERN => {pattern}" if pattern else "" 1661 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1662 1663 ordinality = expression.args.get("ordinality") or "" 1664 if ordinality: 1665 ordinality = f" WITH ORDINALITY{alias}" 1666 alias = "" 1667 1668 when = self.sql(expression, "when") 1669 if when: 1670 table = f"{table} {when}" 1671 1672 return f"{only}{table}{partition}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}"
def
tablesample_sql( self, expression: sqlglot.expressions.TableSample, sep: str = ' AS ', tablesample_keyword: Optional[str] = None) -> str:
1674 def tablesample_sql( 1675 self, 1676 expression: exp.TableSample, 1677 sep: str = " AS ", 1678 tablesample_keyword: t.Optional[str] = None, 1679 ) -> str: 1680 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1681 table = expression.this.copy() 1682 table.set("alias", None) 1683 this = self.sql(table) 1684 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1685 else: 1686 this = self.sql(expression, "this") 1687 alias = "" 1688 1689 method = self.sql(expression, "method") 1690 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1691 numerator = self.sql(expression, "bucket_numerator") 1692 denominator = self.sql(expression, "bucket_denominator") 1693 field = self.sql(expression, "bucket_field") 1694 field = f" ON {field}" if field else "" 1695 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1696 seed = self.sql(expression, "seed") 1697 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1698 1699 size = self.sql(expression, "size") 1700 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1701 size = f"{size} ROWS" 1702 1703 percent = self.sql(expression, "percent") 1704 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1705 percent = f"{percent} PERCENT" 1706 1707 expr = f"{bucket}{percent}{size}" 1708 if self.TABLESAMPLE_REQUIRES_PARENS: 1709 expr = f"({expr})" 1710 1711 return ( 1712 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1713 )
1715 def pivot_sql(self, expression: exp.Pivot) -> str: 1716 expressions = self.expressions(expression, flat=True) 1717 1718 if expression.this: 1719 this = self.sql(expression, "this") 1720 if not expressions: 1721 return f"UNPIVOT {this}" 1722 1723 on = f"{self.seg('ON')} {expressions}" 1724 using = self.expressions(expression, key="using", flat=True) 1725 using = f"{self.seg('USING')} {using}" if using else "" 1726 group = self.sql(expression, "group") 1727 return f"PIVOT {this}{on}{using}{group}" 1728 1729 alias = self.sql(expression, "alias") 1730 alias = f" AS {alias}" if alias else "" 1731 direction = "UNPIVOT" if expression.unpivot else "PIVOT" 1732 field = self.sql(expression, "field") 1733 include_nulls = expression.args.get("include_nulls") 1734 if include_nulls is not None: 1735 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1736 else: 1737 nulls = "" 1738 return f"{direction}{nulls}({expressions} FOR {field}){alias}"
1749 def update_sql(self, expression: exp.Update) -> str: 1750 this = self.sql(expression, "this") 1751 set_sql = self.expressions(expression, flat=True) 1752 from_sql = self.sql(expression, "from") 1753 where_sql = self.sql(expression, "where") 1754 returning = self.sql(expression, "returning") 1755 order = self.sql(expression, "order") 1756 limit = self.sql(expression, "limit") 1757 if self.RETURNING_END: 1758 expression_sql = f"{from_sql}{where_sql}{returning}" 1759 else: 1760 expression_sql = f"{returning}{from_sql}{where_sql}" 1761 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1762 return self.prepend_ctes(expression, sql)
1764 def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str: 1765 values_as_table = values_as_table and self.VALUES_AS_TABLE 1766 1767 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1768 if values_as_table or not expression.find_ancestor(exp.From, exp.Join): 1769 args = self.expressions(expression) 1770 alias = self.sql(expression, "alias") 1771 values = f"VALUES{self.seg('')}{args}" 1772 values = ( 1773 f"({values})" 1774 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1775 else values 1776 ) 1777 return f"{values} AS {alias}" if alias else values 1778 1779 # Converts `VALUES...` expression into a series of select unions. 1780 alias_node = expression.args.get("alias") 1781 column_names = alias_node and alias_node.columns 1782 1783 selects: t.List[exp.Query] = [] 1784 1785 for i, tup in enumerate(expression.expressions): 1786 row = tup.expressions 1787 1788 if i == 0 and column_names: 1789 row = [ 1790 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1791 ] 1792 1793 selects.append(exp.Select(expressions=row)) 1794 1795 if self.pretty: 1796 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1797 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1798 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1799 query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1800 return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False)) 1801 1802 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1803 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1804 return f"({unions}){alias}"
1817 def group_sql(self, expression: exp.Group) -> str: 1818 group_by_all = expression.args.get("all") 1819 if group_by_all is True: 1820 modifier = " ALL" 1821 elif group_by_all is False: 1822 modifier = " DISTINCT" 1823 else: 1824 modifier = "" 1825 1826 group_by = self.op_expressions(f"GROUP BY{modifier}", expression) 1827 1828 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1829 grouping_sets = ( 1830 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1831 ) 1832 1833 cube = expression.args.get("cube", []) 1834 if seq_get(cube, 0) is True: 1835 return f"{group_by}{self.seg('WITH CUBE')}" 1836 else: 1837 cube_sql = self.expressions(expression, key="cube", indent=False) 1838 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1839 1840 rollup = expression.args.get("rollup", []) 1841 if seq_get(rollup, 0) is True: 1842 return f"{group_by}{self.seg('WITH ROLLUP')}" 1843 else: 1844 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1845 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1846 1847 groupings = csv( 1848 grouping_sets, 1849 cube_sql, 1850 rollup_sql, 1851 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1852 sep=self.GROUPINGS_SEP, 1853 ) 1854 1855 if expression.args.get("expressions") and groupings: 1856 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1857 1858 return f"{group_by}{groupings}"
1864 def connect_sql(self, expression: exp.Connect) -> str: 1865 start = self.sql(expression, "start") 1866 start = self.seg(f"START WITH {start}") if start else "" 1867 nocycle = " NOCYCLE" if expression.args.get("nocycle") else "" 1868 connect = self.sql(expression, "connect") 1869 connect = self.seg(f"CONNECT BY{nocycle} {connect}") 1870 return start + connect
1875 def join_sql(self, expression: exp.Join) -> str: 1876 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1877 side = None 1878 else: 1879 side = expression.side 1880 1881 op_sql = " ".join( 1882 op 1883 for op in ( 1884 expression.method, 1885 "GLOBAL" if expression.args.get("global") else None, 1886 side, 1887 expression.kind, 1888 expression.hint if self.JOIN_HINTS else None, 1889 ) 1890 if op 1891 ) 1892 match_cond = self.sql(expression, "match_condition") 1893 match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else "" 1894 on_sql = self.sql(expression, "on") 1895 using = expression.args.get("using") 1896 1897 if not on_sql and using: 1898 on_sql = csv(*(self.sql(column) for column in using)) 1899 1900 this = expression.this 1901 this_sql = self.sql(this) 1902 1903 if on_sql: 1904 on_sql = self.indent(on_sql, skip_first=True) 1905 space = self.seg(" " * self.pad) if self.pretty else " " 1906 if using: 1907 on_sql = f"{space}USING ({on_sql})" 1908 else: 1909 on_sql = f"{space}ON {on_sql}" 1910 elif not op_sql: 1911 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 1912 return f" {this_sql}" 1913 1914 return f", {this_sql}" 1915 1916 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1917 return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}"
1924 def lateral_op(self, expression: exp.Lateral) -> str: 1925 cross_apply = expression.args.get("cross_apply") 1926 1927 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 1928 if cross_apply is True: 1929 op = "INNER JOIN " 1930 elif cross_apply is False: 1931 op = "LEFT JOIN " 1932 else: 1933 op = "" 1934 1935 return f"{op}LATERAL"
1937 def lateral_sql(self, expression: exp.Lateral) -> str: 1938 this = self.sql(expression, "this") 1939 1940 if expression.args.get("view"): 1941 alias = expression.args["alias"] 1942 columns = self.expressions(alias, key="columns", flat=True) 1943 table = f" {alias.name}" if alias.name else "" 1944 columns = f" AS {columns}" if columns else "" 1945 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1946 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1947 1948 alias = self.sql(expression, "alias") 1949 alias = f" AS {alias}" if alias else "" 1950 return f"{self.lateral_op(expression)} {this}{alias}"
1952 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1953 this = self.sql(expression, "this") 1954 1955 args = [ 1956 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1957 for e in (expression.args.get(k) for k in ("offset", "expression")) 1958 if e 1959 ] 1960 1961 args_sql = ", ".join(self.sql(e) for e in args) 1962 args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql 1963 expressions = self.expressions(expression, flat=True) 1964 expressions = f" BY {expressions}" if expressions else "" 1965 1966 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"
1968 def offset_sql(self, expression: exp.Offset) -> str: 1969 this = self.sql(expression, "this") 1970 value = expression.expression 1971 value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value 1972 expressions = self.expressions(expression, flat=True) 1973 expressions = f" BY {expressions}" if expressions else "" 1974 return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
1976 def setitem_sql(self, expression: exp.SetItem) -> str: 1977 kind = self.sql(expression, "kind") 1978 kind = f"{kind} " if kind else "" 1979 this = self.sql(expression, "this") 1980 expressions = self.expressions(expression) 1981 collate = self.sql(expression, "collate") 1982 collate = f" COLLATE {collate}" if collate else "" 1983 global_ = "GLOBAL " if expression.args.get("global") else "" 1984 return f"{global_}{kind}{this}{expressions}{collate}"
1986 def set_sql(self, expression: exp.Set) -> str: 1987 expressions = ( 1988 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 1989 ) 1990 tag = " TAG" if expression.args.get("tag") else "" 1991 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
1996 def lock_sql(self, expression: exp.Lock) -> str: 1997 if not self.LOCKING_READS_SUPPORTED: 1998 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 1999 return "" 2000 2001 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 2002 expressions = self.expressions(expression, flat=True) 2003 expressions = f" OF {expressions}" if expressions else "" 2004 wait = expression.args.get("wait") 2005 2006 if wait is not None: 2007 if isinstance(wait, exp.Literal): 2008 wait = f" WAIT {self.sql(wait)}" 2009 else: 2010 wait = " NOWAIT" if wait else " SKIP LOCKED" 2011 2012 return f"{lock_type}{expressions}{wait or ''}"
def
escape_str(self, text: str, escape_backslash: bool = True) -> str:
2020 def escape_str(self, text: str, escape_backslash: bool = True) -> str: 2021 if self.dialect.ESCAPED_SEQUENCES: 2022 to_escaped = self.dialect.ESCAPED_SEQUENCES 2023 text = "".join( 2024 to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text 2025 ) 2026 2027 return self._replace_line_breaks(text).replace( 2028 self.dialect.QUOTE_END, self._escaped_quote_end 2029 )
2031 def loaddata_sql(self, expression: exp.LoadData) -> str: 2032 local = " LOCAL" if expression.args.get("local") else "" 2033 inpath = f" INPATH {self.sql(expression, 'inpath')}" 2034 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 2035 this = f" INTO TABLE {self.sql(expression, 'this')}" 2036 partition = self.sql(expression, "partition") 2037 partition = f" {partition}" if partition else "" 2038 input_format = self.sql(expression, "input_format") 2039 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 2040 serde = self.sql(expression, "serde") 2041 serde = f" SERDE {serde}" if serde else "" 2042 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
2050 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 2051 this = self.sql(expression, "this") 2052 this = f"{this} " if this else this 2053 siblings = "SIBLINGS " if expression.args.get("siblings") else "" 2054 order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore 2055 interpolated_values = [ 2056 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 2057 for named_expression in expression.args.get("interpolate") or [] 2058 ] 2059 interpolate = ( 2060 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 2061 ) 2062 return f"{order}{interpolate}"
2064 def withfill_sql(self, expression: exp.WithFill) -> str: 2065 from_sql = self.sql(expression, "from") 2066 from_sql = f" FROM {from_sql}" if from_sql else "" 2067 to_sql = self.sql(expression, "to") 2068 to_sql = f" TO {to_sql}" if to_sql else "" 2069 step_sql = self.sql(expression, "step") 2070 step_sql = f" STEP {step_sql}" if step_sql else "" 2071 return f"WITH FILL{from_sql}{to_sql}{step_sql}"
2082 def ordered_sql(self, expression: exp.Ordered) -> str: 2083 desc = expression.args.get("desc") 2084 asc = not desc 2085 2086 nulls_first = expression.args.get("nulls_first") 2087 nulls_last = not nulls_first 2088 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 2089 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 2090 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 2091 2092 this = self.sql(expression, "this") 2093 2094 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 2095 nulls_sort_change = "" 2096 if nulls_first and ( 2097 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 2098 ): 2099 nulls_sort_change = " NULLS FIRST" 2100 elif ( 2101 nulls_last 2102 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 2103 and not nulls_are_last 2104 ): 2105 nulls_sort_change = " NULLS LAST" 2106 2107 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 2108 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 2109 window = expression.find_ancestor(exp.Window, exp.Select) 2110 if isinstance(window, exp.Window) and window.args.get("spec"): 2111 self.unsupported( 2112 f"'{nulls_sort_change.strip()}' translation not supported in window functions" 2113 ) 2114 nulls_sort_change = "" 2115 elif self.NULL_ORDERING_SUPPORTED is None: 2116 if expression.this.is_int: 2117 self.unsupported( 2118 f"'{nulls_sort_change.strip()}' translation not supported with positional ordering" 2119 ) 2120 elif not isinstance(expression.this, exp.Rand): 2121 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 2122 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 2123 nulls_sort_change = "" 2124 2125 with_fill = self.sql(expression, "with_fill") 2126 with_fill = f" {with_fill}" if with_fill else "" 2127 2128 return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
2138 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 2139 partition = self.partition_by_sql(expression) 2140 order = self.sql(expression, "order") 2141 measures = self.expressions(expression, key="measures") 2142 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 2143 rows = self.sql(expression, "rows") 2144 rows = self.seg(rows) if rows else "" 2145 after = self.sql(expression, "after") 2146 after = self.seg(after) if after else "" 2147 pattern = self.sql(expression, "pattern") 2148 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 2149 definition_sqls = [ 2150 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 2151 for definition in expression.args.get("define", []) 2152 ] 2153 definitions = self.expressions(sqls=definition_sqls) 2154 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 2155 body = "".join( 2156 ( 2157 partition, 2158 order, 2159 measures, 2160 rows, 2161 after, 2162 pattern, 2163 define, 2164 ) 2165 ) 2166 alias = self.sql(expression, "alias") 2167 alias = f" {alias}" if alias else "" 2168 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
2170 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 2171 limit = expression.args.get("limit") 2172 2173 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 2174 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 2175 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 2176 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 2177 2178 options = self.expressions(expression, key="options") 2179 if options: 2180 options = f" OPTION{self.wrap(options)}" 2181 2182 return csv( 2183 *sqls, 2184 *[self.sql(join) for join in expression.args.get("joins") or []], 2185 self.sql(expression, "connect"), 2186 self.sql(expression, "match"), 2187 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 2188 self.sql(expression, "prewhere"), 2189 self.sql(expression, "where"), 2190 self.sql(expression, "group"), 2191 self.sql(expression, "having"), 2192 *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()], 2193 self.sql(expression, "order"), 2194 *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit), 2195 *self.after_limit_modifiers(expression), 2196 options, 2197 sep="", 2198 )
def
offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]:
2203 def offset_limit_modifiers( 2204 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 2205 ) -> t.List[str]: 2206 return [ 2207 self.sql(expression, "offset") if fetch else self.sql(limit), 2208 self.sql(limit) if fetch else self.sql(expression, "offset"), 2209 ]
2216 def select_sql(self, expression: exp.Select) -> str: 2217 into = expression.args.get("into") 2218 if not self.SUPPORTS_SELECT_INTO and into: 2219 into.pop() 2220 2221 hint = self.sql(expression, "hint") 2222 distinct = self.sql(expression, "distinct") 2223 distinct = f" {distinct}" if distinct else "" 2224 kind = self.sql(expression, "kind") 2225 2226 limit = expression.args.get("limit") 2227 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP: 2228 top = self.limit_sql(limit, top=True) 2229 limit.pop() 2230 else: 2231 top = "" 2232 2233 expressions = self.expressions(expression) 2234 2235 if kind: 2236 if kind in self.SELECT_KINDS: 2237 kind = f" AS {kind}" 2238 else: 2239 if kind == "STRUCT": 2240 expressions = self.expressions( 2241 sqls=[ 2242 self.sql( 2243 exp.Struct( 2244 expressions=[ 2245 exp.PropertyEQ(this=e.args.get("alias"), expression=e.this) 2246 if isinstance(e, exp.Alias) 2247 else e 2248 for e in expression.expressions 2249 ] 2250 ) 2251 ) 2252 ] 2253 ) 2254 kind = "" 2255 2256 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2257 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2258 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2259 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2260 sql = self.query_modifiers( 2261 expression, 2262 f"SELECT{top_distinct}{kind}{expressions}", 2263 self.sql(expression, "into", comment=False), 2264 self.sql(expression, "from", comment=False), 2265 ) 2266 2267 sql = self.prepend_ctes(expression, sql) 2268 2269 if not self.SUPPORTS_SELECT_INTO and into: 2270 if into.args.get("temporary"): 2271 table_kind = " TEMPORARY" 2272 elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"): 2273 table_kind = " UNLOGGED" 2274 else: 2275 table_kind = "" 2276 sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}" 2277 2278 return sql
2290 def star_sql(self, expression: exp.Star) -> str: 2291 except_ = self.expressions(expression, key="except", flat=True) 2292 except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else "" 2293 replace = self.expressions(expression, key="replace", flat=True) 2294 replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else "" 2295 return f"*{except_}{replace}"
2311 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2312 alias = self.sql(expression, "alias") 2313 alias = f"{sep}{alias}" if alias else "" 2314 2315 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2316 pivots = f" {pivots}" if pivots else "" 2317 2318 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2319 return self.prepend_ctes(expression, sql)
2325 def set_operations(self, expression: exp.Union) -> str: 2326 if not self.OUTER_UNION_MODIFIERS: 2327 limit = expression.args.get("limit") 2328 order = expression.args.get("order") 2329 2330 if limit or order: 2331 select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False) 2332 2333 if limit: 2334 select = select.limit(limit.pop(), copy=False) 2335 if order: 2336 select = select.order_by(order.pop(), copy=False) 2337 return self.sql(select) 2338 2339 sqls: t.List[str] = [] 2340 stack: t.List[t.Union[str, exp.Expression]] = [expression] 2341 2342 while stack: 2343 node = stack.pop() 2344 2345 if isinstance(node, exp.Union): 2346 stack.append(node.expression) 2347 stack.append( 2348 self.maybe_comment( 2349 getattr(self, f"{node.key}_op")(node), 2350 comments=node.comments, 2351 separated=True, 2352 ) 2353 ) 2354 stack.append(node.this) 2355 else: 2356 sqls.append(self.sql(node)) 2357 2358 this = self.sep().join(sqls) 2359 this = self.query_modifiers(expression, this) 2360 return self.prepend_ctes(expression, this)
2371 def unnest_sql(self, expression: exp.Unnest) -> str: 2372 args = self.expressions(expression, flat=True) 2373 2374 alias = expression.args.get("alias") 2375 offset = expression.args.get("offset") 2376 2377 if self.UNNEST_WITH_ORDINALITY: 2378 if alias and isinstance(offset, exp.Expression): 2379 alias.append("columns", offset) 2380 2381 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2382 columns = alias.columns 2383 alias = self.sql(columns[0]) if columns else "" 2384 else: 2385 alias = self.sql(alias) 2386 2387 alias = f" AS {alias}" if alias else alias 2388 if self.UNNEST_WITH_ORDINALITY: 2389 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2390 else: 2391 if isinstance(offset, exp.Expression): 2392 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2393 elif offset: 2394 suffix = f"{alias} WITH OFFSET" 2395 else: 2396 suffix = alias 2397 2398 return f"UNNEST({args}){suffix}"
2407 def window_sql(self, expression: exp.Window) -> str: 2408 this = self.sql(expression, "this") 2409 partition = self.partition_by_sql(expression) 2410 order = expression.args.get("order") 2411 order = self.order_sql(order, flat=True) if order else "" 2412 spec = self.sql(expression, "spec") 2413 alias = self.sql(expression, "alias") 2414 over = self.sql(expression, "over") or "OVER" 2415 2416 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2417 2418 first = expression.args.get("first") 2419 if first is None: 2420 first = "" 2421 else: 2422 first = "FIRST" if first else "LAST" 2423 2424 if not partition and not order and not spec and alias: 2425 return f"{this} {alias}" 2426 2427 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2428 return f"{this} ({args})"
def
partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
2434 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2435 kind = self.sql(expression, "kind") 2436 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2437 end = ( 2438 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2439 or "CURRENT ROW" 2440 ) 2441 return f"{kind} BETWEEN {start} AND {end}"
def
bracket_offset_expressions( self, expression: sqlglot.expressions.Bracket) -> List[sqlglot.expressions.Expression]:
2469 def any_sql(self, expression: exp.Any) -> str: 2470 this = self.sql(expression, "this") 2471 if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)): 2472 if isinstance(expression.this, exp.UNWRAPPED_QUERIES): 2473 this = self.wrap(this) 2474 return f"ANY{this}" 2475 return f"ANY {this}"
2480 def case_sql(self, expression: exp.Case) -> str: 2481 this = self.sql(expression, "this") 2482 statements = [f"CASE {this}" if this else "CASE"] 2483 2484 for e in expression.args["ifs"]: 2485 statements.append(f"WHEN {self.sql(e, 'this')}") 2486 statements.append(f"THEN {self.sql(e, 'true')}") 2487 2488 default = self.sql(expression, "default") 2489 2490 if default: 2491 statements.append(f"ELSE {default}") 2492 2493 statements.append("END") 2494 2495 if self.pretty and self.too_wide(statements): 2496 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2497 2498 return " ".join(statements)
2515 def trim_sql(self, expression: exp.Trim) -> str: 2516 trim_type = self.sql(expression, "position") 2517 2518 if trim_type == "LEADING": 2519 return self.func("LTRIM", expression.this) 2520 elif trim_type == "TRAILING": 2521 return self.func("RTRIM", expression.this) 2522 else: 2523 return self.func("TRIM", expression.this, expression.expression)
def
convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]:
2525 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2526 args = expression.expressions 2527 if isinstance(expression, exp.ConcatWs): 2528 args = args[1:] # Skip the delimiter 2529 2530 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2531 args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args] 2532 2533 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2534 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2535 2536 return args
2538 def concat_sql(self, expression: exp.Concat) -> str: 2539 expressions = self.convert_concat_args(expression) 2540 2541 # Some dialects don't allow a single-argument CONCAT call 2542 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2543 return self.sql(expressions[0]) 2544 2545 return self.func("CONCAT", *expressions)
2556 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2557 expressions = self.expressions(expression, flat=True) 2558 reference = self.sql(expression, "reference") 2559 reference = f" {reference}" if reference else "" 2560 delete = self.sql(expression, "delete") 2561 delete = f" ON DELETE {delete}" if delete else "" 2562 update = self.sql(expression, "update") 2563 update = f" ON UPDATE {update}" if update else "" 2564 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
2566 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2567 expressions = self.expressions(expression, flat=True) 2568 options = self.expressions(expression, key="options", flat=True, sep=" ") 2569 options = f" {options}" if options else "" 2570 return f"PRIMARY KEY ({expressions}){options}"
2587 def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str: 2588 if isinstance(expression, exp.JSONPathPart): 2589 transform = self.TRANSFORMS.get(expression.__class__) 2590 if not callable(transform): 2591 self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}") 2592 return "" 2593 2594 return transform(self, expression) 2595 2596 if isinstance(expression, int): 2597 return str(expression) 2598 2599 if self.JSON_PATH_SINGLE_QUOTE_ESCAPE: 2600 escaped = expression.replace("'", "\\'") 2601 escaped = f"\\'{expression}\\'" 2602 else: 2603 escaped = expression.replace('"', '\\"') 2604 escaped = f'"{escaped}"' 2605 2606 return escaped
def
jsonobject_sql( self, expression: sqlglot.expressions.JSONObject | sqlglot.expressions.JSONObjectAgg) -> str:
2611 def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str: 2612 null_handling = expression.args.get("null_handling") 2613 null_handling = f" {null_handling}" if null_handling else "" 2614 2615 unique_keys = expression.args.get("unique_keys") 2616 if unique_keys is not None: 2617 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2618 else: 2619 unique_keys = "" 2620 2621 return_type = self.sql(expression, "return_type") 2622 return_type = f" RETURNING {return_type}" if return_type else "" 2623 encoding = self.sql(expression, "encoding") 2624 encoding = f" ENCODING {encoding}" if encoding else "" 2625 2626 return self.func( 2627 "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG", 2628 *expression.expressions, 2629 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2630 )
2635 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2636 null_handling = expression.args.get("null_handling") 2637 null_handling = f" {null_handling}" if null_handling else "" 2638 return_type = self.sql(expression, "return_type") 2639 return_type = f" RETURNING {return_type}" if return_type else "" 2640 strict = " STRICT" if expression.args.get("strict") else "" 2641 return self.func( 2642 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2643 )
2645 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2646 this = self.sql(expression, "this") 2647 order = self.sql(expression, "order") 2648 null_handling = expression.args.get("null_handling") 2649 null_handling = f" {null_handling}" if null_handling else "" 2650 return_type = self.sql(expression, "return_type") 2651 return_type = f" RETURNING {return_type}" if return_type else "" 2652 strict = " STRICT" if expression.args.get("strict") else "" 2653 return self.func( 2654 "JSON_ARRAYAGG", 2655 this, 2656 suffix=f"{order}{null_handling}{return_type}{strict})", 2657 )
2659 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2660 path = self.sql(expression, "path") 2661 path = f" PATH {path}" if path else "" 2662 nested_schema = self.sql(expression, "nested_schema") 2663 2664 if nested_schema: 2665 return f"NESTED{path} {nested_schema}" 2666 2667 this = self.sql(expression, "this") 2668 kind = self.sql(expression, "kind") 2669 kind = f" {kind}" if kind else "" 2670 return f"{this}{kind}{path}"
2675 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2676 this = self.sql(expression, "this") 2677 path = self.sql(expression, "path") 2678 path = f", {path}" if path else "" 2679 error_handling = expression.args.get("error_handling") 2680 error_handling = f" {error_handling}" if error_handling else "" 2681 empty_handling = expression.args.get("empty_handling") 2682 empty_handling = f" {empty_handling}" if empty_handling else "" 2683 schema = self.sql(expression, "schema") 2684 return self.func( 2685 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2686 )
2688 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2689 this = self.sql(expression, "this") 2690 kind = self.sql(expression, "kind") 2691 path = self.sql(expression, "path") 2692 path = f" {path}" if path else "" 2693 as_json = " AS JSON" if expression.args.get("as_json") else "" 2694 return f"{this} {kind}{path}{as_json}"
2696 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2697 this = self.sql(expression, "this") 2698 path = self.sql(expression, "path") 2699 path = f", {path}" if path else "" 2700 expressions = self.expressions(expression) 2701 with_ = ( 2702 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2703 if expressions 2704 else "" 2705 ) 2706 return f"OPENJSON({this}{path}){with_}"
2708 def in_sql(self, expression: exp.In) -> str: 2709 query = expression.args.get("query") 2710 unnest = expression.args.get("unnest") 2711 field = expression.args.get("field") 2712 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2713 2714 if query: 2715 in_sql = self.sql(query) 2716 elif unnest: 2717 in_sql = self.in_unnest_op(unnest) 2718 elif field: 2719 in_sql = self.sql(field) 2720 else: 2721 in_sql = f"({self.expressions(expression, flat=True)})" 2722 2723 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
2728 def interval_sql(self, expression: exp.Interval) -> str: 2729 unit = self.sql(expression, "unit") 2730 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2731 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2732 unit = f" {unit}" if unit else "" 2733 2734 if self.SINGLE_STRING_INTERVAL: 2735 this = expression.this.name if expression.this else "" 2736 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2737 2738 this = self.sql(expression, "this") 2739 if this: 2740 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2741 this = f" {this}" if unwrapped else f" ({this})" 2742 2743 return f"INTERVAL{this}{unit}"
2748 def reference_sql(self, expression: exp.Reference) -> str: 2749 this = self.sql(expression, "this") 2750 expressions = self.expressions(expression, flat=True) 2751 expressions = f"({expressions})" if expressions else "" 2752 options = self.expressions(expression, key="options", flat=True, sep=" ") 2753 options = f" {options}" if options else "" 2754 return f"REFERENCES {this}{expressions}{options}"
2777 def pivotalias_sql(self, expression: exp.PivotAlias) -> str: 2778 alias = expression.args["alias"] 2779 identifier_alias = isinstance(alias, exp.Identifier) 2780 2781 if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2782 alias.replace(exp.Literal.string(alias.output_name)) 2783 elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2784 alias.replace(exp.to_identifier(alias.output_name)) 2785 2786 return self.alias_sql(expression)
def
and_sql( self, expression: sqlglot.expressions.And, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
def
or_sql( self, expression: sqlglot.expressions.Or, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
def
xor_sql( self, expression: sqlglot.expressions.Xor, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
def
connector_sql( self, expression: sqlglot.expressions.Connector, op: str, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
2824 def connector_sql( 2825 self, 2826 expression: exp.Connector, 2827 op: str, 2828 stack: t.Optional[t.List[str | exp.Expression]] = None, 2829 ) -> str: 2830 if stack is not None: 2831 if expression.expressions: 2832 stack.append(self.expressions(expression, sep=f" {op} ")) 2833 else: 2834 stack.append(expression.right) 2835 if expression.comments: 2836 for comment in expression.comments: 2837 op += f" /*{self.pad_comment(comment)}*/" 2838 stack.extend((op, expression.left)) 2839 return op 2840 2841 stack = [expression] 2842 sqls: t.List[str] = [] 2843 ops = set() 2844 2845 while stack: 2846 node = stack.pop() 2847 if isinstance(node, exp.Connector): 2848 ops.add(getattr(self, f"{node.key}_sql")(node, stack)) 2849 else: 2850 sql = self.sql(node) 2851 if sqls and sqls[-1] in ops: 2852 sqls[-1] += f" {sql}" 2853 else: 2854 sqls.append(sql) 2855 2856 sep = "\n" if self.pretty and self.too_wide(sqls) else " " 2857 return sep.join(sqls)
def
cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str:
2877 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2878 format_sql = self.sql(expression, "format") 2879 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2880 to_sql = self.sql(expression, "to") 2881 to_sql = f" {to_sql}" if to_sql else "" 2882 action = self.sql(expression, "action") 2883 action = f" {action}" if action else "" 2884 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql}{action})"
2901 def comment_sql(self, expression: exp.Comment) -> str: 2902 this = self.sql(expression, "this") 2903 kind = expression.args["kind"] 2904 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 2905 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2906 expression_sql = self.sql(expression, "expression") 2907 return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"
2909 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2910 this = self.sql(expression, "this") 2911 delete = " DELETE" if expression.args.get("delete") else "" 2912 recompress = self.sql(expression, "recompress") 2913 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2914 to_disk = self.sql(expression, "to_disk") 2915 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2916 to_volume = self.sql(expression, "to_volume") 2917 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2918 return f"{this}{delete}{recompress}{to_disk}{to_volume}"
2920 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2921 where = self.sql(expression, "where") 2922 group = self.sql(expression, "group") 2923 aggregates = self.expressions(expression, key="aggregates") 2924 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2925 2926 if not (where or group or aggregates) and len(expression.expressions) == 1: 2927 return f"TTL {self.expressions(expression, flat=True)}" 2928 2929 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
2946 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2947 this = self.sql(expression, "this") 2948 2949 dtype = self.sql(expression, "dtype") 2950 if dtype: 2951 collate = self.sql(expression, "collate") 2952 collate = f" COLLATE {collate}" if collate else "" 2953 using = self.sql(expression, "using") 2954 using = f" USING {using}" if using else "" 2955 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2956 2957 default = self.sql(expression, "default") 2958 if default: 2959 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2960 2961 comment = self.sql(expression, "comment") 2962 if comment: 2963 return f"ALTER COLUMN {this} COMMENT {comment}" 2964 2965 if not expression.args.get("drop"): 2966 self.unsupported("Unsupported ALTER COLUMN syntax") 2967 2968 return f"ALTER COLUMN {this} DROP DEFAULT"
2970 def renametable_sql(self, expression: exp.RenameTable) -> str: 2971 if not self.RENAME_TABLE_WITH_DB: 2972 # Remove db from tables 2973 expression = expression.transform( 2974 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 2975 ).assert_is(exp.RenameTable) 2976 this = self.sql(expression, "this") 2977 return f"RENAME TO {this}"
2985 def altertable_sql(self, expression: exp.AlterTable) -> str: 2986 actions = expression.args["actions"] 2987 2988 if isinstance(actions[0], exp.ColumnDef): 2989 actions = self.add_column_sql(expression) 2990 elif isinstance(actions[0], exp.Schema): 2991 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 2992 elif isinstance(actions[0], exp.Delete): 2993 actions = self.expressions(expression, key="actions", flat=True) 2994 else: 2995 actions = self.expressions(expression, key="actions", flat=True) 2996 2997 exists = " IF EXISTS" if expression.args.get("exists") else "" 2998 only = " ONLY" if expression.args.get("only") else "" 2999 options = self.expressions(expression, key="options") 3000 options = f", {options}" if options else "" 3001 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}"
3020 def distinct_sql(self, expression: exp.Distinct) -> str: 3021 this = self.expressions(expression, flat=True) 3022 3023 if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1: 3024 case = exp.case() 3025 for arg in expression.expressions: 3026 case = case.when(arg.is_(exp.null()), exp.null()) 3027 this = self.sql(case.else_(f"({this})")) 3028 3029 this = f" {this}" if this else "" 3030 3031 on = self.sql(expression, "on") 3032 on = f" ON {on}" if on else "" 3033 return f"DISTINCT{this}{on}"
3062 def div_sql(self, expression: exp.Div) -> str: 3063 l, r = expression.left, expression.right 3064 3065 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 3066 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 3067 3068 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 3069 if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES): 3070 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 3071 3072 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 3073 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 3074 return self.sql( 3075 exp.cast( 3076 l / r, 3077 to=exp.DataType.Type.BIGINT, 3078 ) 3079 ) 3080 3081 return self.binary(expression, "/")
3162 def log_sql(self, expression: exp.Log) -> str: 3163 this = expression.this 3164 expr = expression.expression 3165 3166 if self.dialect.LOG_BASE_FIRST is False: 3167 this, expr = expr, this 3168 elif self.dialect.LOG_BASE_FIRST is None and expr: 3169 if this.name in ("2", "10"): 3170 return self.func(f"LOG{this.name}", expr) 3171 3172 self.unsupported(f"Unsupported logarithm with base {self.sql(this)}") 3173 3174 return self.func("LOG", this, expr)
3187 def function_fallback_sql(self, expression: exp.Func) -> str: 3188 args = [] 3189 3190 for key in expression.arg_types: 3191 arg_value = expression.args.get(key) 3192 3193 if isinstance(arg_value, list): 3194 for value in arg_value: 3195 args.append(value) 3196 elif arg_value is not None: 3197 args.append(arg_value) 3198 3199 if self.normalize_functions: 3200 name = expression.sql_name() 3201 else: 3202 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 3203 3204 return self.func(name, *args)
def
func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')') -> str:
3215 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 3216 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 3217 if self.pretty and self.too_wide(arg_sqls): 3218 return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 3219 return ", ".join(arg_sqls)
def
format_time( self, expression: sqlglot.expressions.Expression, inverse_time_mapping: Optional[Dict[str, str]] = None, inverse_time_trie: Optional[Dict] = None) -> Optional[str]:
3224 def format_time( 3225 self, 3226 expression: exp.Expression, 3227 inverse_time_mapping: t.Optional[t.Dict[str, str]] = None, 3228 inverse_time_trie: t.Optional[t.Dict] = None, 3229 ) -> t.Optional[str]: 3230 return format_time( 3231 self.sql(expression, "format"), 3232 inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING, 3233 inverse_time_trie or self.dialect.INVERSE_TIME_TRIE, 3234 )
def
expressions( self, expression: Optional[sqlglot.expressions.Expression] = None, key: Optional[str] = None, sqls: Optional[Collection[Union[str, sqlglot.expressions.Expression]]] = None, flat: bool = False, indent: bool = True, skip_first: bool = False, skip_last: bool = False, sep: str = ', ', prefix: str = '', dynamic: bool = False, new_line: bool = False) -> str:
3236 def expressions( 3237 self, 3238 expression: t.Optional[exp.Expression] = None, 3239 key: t.Optional[str] = None, 3240 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 3241 flat: bool = False, 3242 indent: bool = True, 3243 skip_first: bool = False, 3244 skip_last: bool = False, 3245 sep: str = ", ", 3246 prefix: str = "", 3247 dynamic: bool = False, 3248 new_line: bool = False, 3249 ) -> str: 3250 expressions = expression.args.get(key or "expressions") if expression else sqls 3251 3252 if not expressions: 3253 return "" 3254 3255 if flat: 3256 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 3257 3258 num_sqls = len(expressions) 3259 3260 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 3261 if self.pretty and not self.leading_comma: 3262 stripped_sep = sep.strip() 3263 3264 result_sqls = [] 3265 for i, e in enumerate(expressions): 3266 sql = self.sql(e, comment=False) 3267 if not sql: 3268 continue 3269 3270 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 3271 3272 if self.pretty: 3273 if self.leading_comma: 3274 result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}") 3275 else: 3276 result_sqls.append( 3277 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 3278 ) 3279 else: 3280 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 3281 3282 if self.pretty and (not dynamic or self.too_wide(result_sqls)): 3283 if new_line: 3284 result_sqls.insert(0, "") 3285 result_sqls.append("") 3286 result_sql = "\n".join(result_sqls) 3287 else: 3288 result_sql = "".join(result_sqls) 3289 return ( 3290 self.indent(result_sql, skip_first=skip_first, skip_last=skip_last) 3291 if indent 3292 else result_sql 3293 )
def
op_expressions( self, op: str, expression: sqlglot.expressions.Expression, flat: bool = False) -> str:
3295 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 3296 flat = flat or isinstance(expression.parent, exp.Properties) 3297 expressions_sql = self.expressions(expression, flat=flat) 3298 if flat: 3299 return f"{op} {expressions_sql}" 3300 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
3302 def naked_property(self, expression: exp.Property) -> str: 3303 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 3304 if not property_name: 3305 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 3306 return f"{property_name} {self.sql(expression, 'this')}"
3314 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 3315 this = self.sql(expression, "this") 3316 expressions = self.no_identify(self.expressions, expression) 3317 expressions = ( 3318 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 3319 ) 3320 return f"{this}{expressions}"
3330 def when_sql(self, expression: exp.When) -> str: 3331 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 3332 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 3333 condition = self.sql(expression, "condition") 3334 condition = f" AND {condition}" if condition else "" 3335 3336 then_expression = expression.args.get("then") 3337 if isinstance(then_expression, exp.Insert): 3338 then = f"INSERT {self.sql(then_expression, 'this')}" 3339 if "expression" in then_expression.args: 3340 then += f" VALUES {self.sql(then_expression, 'expression')}" 3341 elif isinstance(then_expression, exp.Update): 3342 if isinstance(then_expression.args.get("expressions"), exp.Star): 3343 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 3344 else: 3345 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 3346 else: 3347 then = self.sql(then_expression) 3348 return f"WHEN {matched}{source}{condition} THEN {then}"
3350 def merge_sql(self, expression: exp.Merge) -> str: 3351 table = expression.this 3352 table_alias = "" 3353 3354 hints = table.args.get("hints") 3355 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 3356 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 3357 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 3358 3359 this = self.sql(table) 3360 using = f"USING {self.sql(expression, 'using')}" 3361 on = f"ON {self.sql(expression, 'on')}" 3362 expressions = self.expressions(expression, sep=" ") 3363 3364 return self.prepend_ctes( 3365 expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}" 3366 )
3374 def tonumber_sql(self, expression: exp.ToNumber) -> str: 3375 if not self.SUPPORTS_TO_NUMBER: 3376 self.unsupported("Unsupported TO_NUMBER function") 3377 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3378 3379 fmt = expression.args.get("format") 3380 if not fmt: 3381 self.unsupported("Conversion format is required for TO_NUMBER") 3382 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3383 3384 return self.func("TO_NUMBER", expression.this, fmt)
3386 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 3387 this = self.sql(expression, "this") 3388 kind = self.sql(expression, "kind") 3389 settings_sql = self.expressions(expression, key="settings", sep=" ") 3390 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 3391 return f"{this}({kind}{args})"
3405 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3406 expressions = self.expressions(expression, key="expressions", flat=True) 3407 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3408 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3409 buckets = self.sql(expression, "buckets") 3410 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
3412 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3413 this = self.sql(expression, "this") 3414 having = self.sql(expression, "having") 3415 3416 if having: 3417 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3418 3419 return self.func("ANY_VALUE", this)
3421 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3422 transform = self.func("TRANSFORM", *expression.expressions) 3423 row_format_before = self.sql(expression, "row_format_before") 3424 row_format_before = f" {row_format_before}" if row_format_before else "" 3425 record_writer = self.sql(expression, "record_writer") 3426 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3427 using = f" USING {self.sql(expression, 'command_script')}" 3428 schema = self.sql(expression, "schema") 3429 schema = f" AS {schema}" if schema else "" 3430 row_format_after = self.sql(expression, "row_format_after") 3431 row_format_after = f" {row_format_after}" if row_format_after else "" 3432 record_reader = self.sql(expression, "record_reader") 3433 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3434 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
3436 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3437 key_block_size = self.sql(expression, "key_block_size") 3438 if key_block_size: 3439 return f"KEY_BLOCK_SIZE = {key_block_size}" 3440 3441 using = self.sql(expression, "using") 3442 if using: 3443 return f"USING {using}" 3444 3445 parser = self.sql(expression, "parser") 3446 if parser: 3447 return f"WITH PARSER {parser}" 3448 3449 comment = self.sql(expression, "comment") 3450 if comment: 3451 return f"COMMENT {comment}" 3452 3453 visible = expression.args.get("visible") 3454 if visible is not None: 3455 return "VISIBLE" if visible else "INVISIBLE" 3456 3457 engine_attr = self.sql(expression, "engine_attr") 3458 if engine_attr: 3459 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3460 3461 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3462 if secondary_engine_attr: 3463 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3464 3465 self.unsupported("Unsupported index constraint option.") 3466 return ""
3472 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3473 kind = self.sql(expression, "kind") 3474 kind = f"{kind} INDEX" if kind else "INDEX" 3475 this = self.sql(expression, "this") 3476 this = f" {this}" if this else "" 3477 index_type = self.sql(expression, "index_type") 3478 index_type = f" USING {index_type}" if index_type else "" 3479 expressions = self.expressions(expression, flat=True) 3480 expressions = f" ({expressions})" if expressions else "" 3481 options = self.expressions(expression, key="options", sep=" ") 3482 options = f" {options}" if options else "" 3483 return f"{kind}{this}{index_type}{expressions}{options}"
3485 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3486 if self.NVL2_SUPPORTED: 3487 return self.function_fallback_sql(expression) 3488 3489 case = exp.Case().when( 3490 expression.this.is_(exp.null()).not_(copy=False), 3491 expression.args["true"], 3492 copy=False, 3493 ) 3494 else_cond = expression.args.get("false") 3495 if else_cond: 3496 case.else_(else_cond, copy=False) 3497 3498 return self.sql(case)
3500 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3501 this = self.sql(expression, "this") 3502 expr = self.sql(expression, "expression") 3503 iterator = self.sql(expression, "iterator") 3504 condition = self.sql(expression, "condition") 3505 condition = f" IF {condition}" if condition else "" 3506 return f"{this} FOR {expr} IN {iterator}{condition}"
3514 def predict_sql(self, expression: exp.Predict) -> str: 3515 model = self.sql(expression, "this") 3516 model = f"MODEL {model}" 3517 table = self.sql(expression, "expression") 3518 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3519 parameters = self.sql(expression, "params_struct") 3520 return self.func("PREDICT", model, table, parameters or None)
3535 def toarray_sql(self, expression: exp.ToArray) -> str: 3536 arg = expression.this 3537 if not arg.type: 3538 from sqlglot.optimizer.annotate_types import annotate_types 3539 3540 arg = annotate_types(arg) 3541 3542 if arg.is_type(exp.DataType.Type.ARRAY): 3543 return self.sql(arg) 3544 3545 cond_for_null = arg.is_(exp.null()) 3546 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
3562 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3563 this = expression.this 3564 time_format = self.format_time(expression) 3565 3566 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3567 return self.sql( 3568 exp.cast( 3569 exp.StrToTime(this=this, format=expression.args["format"]), 3570 exp.DataType.Type.DATE, 3571 ) 3572 ) 3573 3574 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3575 return self.sql(this) 3576 3577 return self.sql(exp.cast(this, exp.DataType.Type.DATE))
3589 def lastday_sql(self, expression: exp.LastDay) -> str: 3590 if self.LAST_DAY_SUPPORTS_DATE_PART: 3591 return self.function_fallback_sql(expression) 3592 3593 unit = expression.text("unit") 3594 if unit and unit != "MONTH": 3595 self.unsupported("Date parts are not supported in LAST_DAY.") 3596 3597 return self.func("LAST_DAY", expression.this)
3606 def arrayany_sql(self, expression: exp.ArrayAny) -> str: 3607 if self.CAN_IMPLEMENT_ARRAY_ANY: 3608 filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression) 3609 filtered_not_empty = exp.ArraySize(this=filtered).neq(0) 3610 original_is_empty = exp.ArraySize(this=expression.this).eq(0) 3611 return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty))) 3612 3613 from sqlglot.dialects import Dialect 3614 3615 # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect 3616 if self.dialect.__class__ != Dialect: 3617 self.unsupported("ARRAY_ANY is unsupported") 3618 3619 return self.function_fallback_sql(expression)
3625 def struct_sql(self, expression: exp.Struct) -> str: 3626 expression.set( 3627 "expressions", 3628 [ 3629 exp.alias_(e.expression, e.name if e.this.is_string else e.this) 3630 if isinstance(e, exp.PropertyEQ) 3631 else e 3632 for e in expression.expressions 3633 ], 3634 ) 3635 3636 return self.function_fallback_sql(expression)
3644 def truncatetable_sql(self, expression: exp.TruncateTable) -> str: 3645 target = "DATABASE" if expression.args.get("is_database") else "TABLE" 3646 tables = f" {self.expressions(expression)}" 3647 3648 exists = " IF EXISTS" if expression.args.get("exists") else "" 3649 3650 on_cluster = self.sql(expression, "cluster") 3651 on_cluster = f" {on_cluster}" if on_cluster else "" 3652 3653 identity = self.sql(expression, "identity") 3654 identity = f" {identity} IDENTITY" if identity else "" 3655 3656 option = self.sql(expression, "option") 3657 option = f" {option}" if option else "" 3658 3659 partition = self.sql(expression, "partition") 3660 partition = f" {partition}" if partition else "" 3661 3662 return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
3666 def convert_sql(self, expression: exp.Convert) -> str: 3667 to = expression.this 3668 value = expression.expression 3669 style = expression.args.get("style") 3670 safe = expression.args.get("safe") 3671 strict = expression.args.get("strict") 3672 3673 if not to or not value: 3674 return "" 3675 3676 # Retrieve length of datatype and override to default if not specified 3677 if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3678 to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False) 3679 3680 transformed: t.Optional[exp.Expression] = None 3681 cast = exp.Cast if strict else exp.TryCast 3682 3683 # Check whether a conversion with format (T-SQL calls this 'style') is applicable 3684 if isinstance(style, exp.Literal) and style.is_int: 3685 from sqlglot.dialects.tsql import TSQL 3686 3687 style_value = style.name 3688 converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value) 3689 if not converted_style: 3690 self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}") 3691 3692 fmt = exp.Literal.string(converted_style) 3693 3694 if to.this == exp.DataType.Type.DATE: 3695 transformed = exp.StrToDate(this=value, format=fmt) 3696 elif to.this == exp.DataType.Type.DATETIME: 3697 transformed = exp.StrToTime(this=value, format=fmt) 3698 elif to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3699 transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe) 3700 elif to.this == exp.DataType.Type.TEXT: 3701 transformed = exp.TimeToStr(this=value, format=fmt) 3702 3703 if not transformed: 3704 transformed = cast(this=value, to=to, safe=safe) 3705 3706 return self.sql(transformed)