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 } 353 354 STAR_MAPPING = { 355 "except": "EXCEPT", 356 "replace": "REPLACE", 357 } 358 359 TIME_PART_SINGULARS = { 360 "MICROSECONDS": "MICROSECOND", 361 "SECONDS": "SECOND", 362 "MINUTES": "MINUTE", 363 "HOURS": "HOUR", 364 "DAYS": "DAY", 365 "WEEKS": "WEEK", 366 "MONTHS": "MONTH", 367 "QUARTERS": "QUARTER", 368 "YEARS": "YEAR", 369 } 370 371 AFTER_HAVING_MODIFIER_TRANSFORMS = { 372 "cluster": lambda self, e: self.sql(e, "cluster"), 373 "distribute": lambda self, e: self.sql(e, "distribute"), 374 "qualify": lambda self, e: self.sql(e, "qualify"), 375 "sort": lambda self, e: self.sql(e, "sort"), 376 "windows": lambda self, e: ( 377 self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True) 378 if e.args.get("windows") 379 else "" 380 ), 381 } 382 383 TOKEN_MAPPING: t.Dict[TokenType, str] = {} 384 385 STRUCT_DELIMITER = ("<", ">") 386 387 PARAMETER_TOKEN = "@" 388 NAMED_PLACEHOLDER_TOKEN = ":" 389 390 PROPERTIES_LOCATION = { 391 exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE, 392 exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA, 393 exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA, 394 exp.BackupProperty: exp.Properties.Location.POST_SCHEMA, 395 exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME, 396 exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA, 397 exp.ChecksumProperty: exp.Properties.Location.POST_NAME, 398 exp.CollateProperty: exp.Properties.Location.POST_SCHEMA, 399 exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA, 400 exp.Cluster: exp.Properties.Location.POST_SCHEMA, 401 exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA, 402 exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME, 403 exp.DefinerProperty: exp.Properties.Location.POST_CREATE, 404 exp.DictRange: exp.Properties.Location.POST_SCHEMA, 405 exp.DictProperty: exp.Properties.Location.POST_SCHEMA, 406 exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA, 407 exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA, 408 exp.EngineProperty: exp.Properties.Location.POST_SCHEMA, 409 exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA, 410 exp.ExternalProperty: exp.Properties.Location.POST_CREATE, 411 exp.FallbackProperty: exp.Properties.Location.POST_NAME, 412 exp.FileFormatProperty: exp.Properties.Location.POST_WITH, 413 exp.FreespaceProperty: exp.Properties.Location.POST_NAME, 414 exp.GlobalProperty: exp.Properties.Location.POST_CREATE, 415 exp.HeapProperty: exp.Properties.Location.POST_WITH, 416 exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA, 417 exp.IcebergProperty: exp.Properties.Location.POST_CREATE, 418 exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA, 419 exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME, 420 exp.JournalProperty: exp.Properties.Location.POST_NAME, 421 exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA, 422 exp.LikeProperty: exp.Properties.Location.POST_SCHEMA, 423 exp.LocationProperty: exp.Properties.Location.POST_SCHEMA, 424 exp.LockProperty: exp.Properties.Location.POST_SCHEMA, 425 exp.LockingProperty: exp.Properties.Location.POST_ALIAS, 426 exp.LogProperty: exp.Properties.Location.POST_NAME, 427 exp.MaterializedProperty: exp.Properties.Location.POST_CREATE, 428 exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME, 429 exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION, 430 exp.OnProperty: exp.Properties.Location.POST_SCHEMA, 431 exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION, 432 exp.Order: exp.Properties.Location.POST_SCHEMA, 433 exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA, 434 exp.PartitionedByProperty: exp.Properties.Location.POST_WITH, 435 exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA, 436 exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA, 437 exp.Property: exp.Properties.Location.POST_WITH, 438 exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA, 439 exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA, 440 exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA, 441 exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA, 442 exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA, 443 exp.SampleProperty: exp.Properties.Location.POST_SCHEMA, 444 exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA, 445 exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA, 446 exp.Set: exp.Properties.Location.POST_SCHEMA, 447 exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA, 448 exp.SetProperty: exp.Properties.Location.POST_CREATE, 449 exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA, 450 exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION, 451 exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION, 452 exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA, 453 exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA, 454 exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE, 455 exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA, 456 exp.TemporaryProperty: exp.Properties.Location.POST_CREATE, 457 exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA, 458 exp.TransientProperty: exp.Properties.Location.POST_CREATE, 459 exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA, 460 exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA, 461 exp.UnloggedProperty: exp.Properties.Location.POST_CREATE, 462 exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA, 463 exp.VolatileProperty: exp.Properties.Location.POST_CREATE, 464 exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION, 465 exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME, 466 exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA, 467 } 468 469 # Keywords that can't be used as unquoted identifier names 470 RESERVED_KEYWORDS: t.Set[str] = set() 471 472 # Expressions whose comments are separated from them for better formatting 473 WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 474 exp.Create, 475 exp.Delete, 476 exp.Drop, 477 exp.From, 478 exp.Insert, 479 exp.Join, 480 exp.Select, 481 exp.Union, 482 exp.Update, 483 exp.Where, 484 exp.With, 485 ) 486 487 # Expressions that should not have their comments generated in maybe_comment 488 EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 489 exp.Binary, 490 exp.Union, 491 ) 492 493 # Expressions that can remain unwrapped when appearing in the context of an INTERVAL 494 UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = ( 495 exp.Column, 496 exp.Literal, 497 exp.Neg, 498 exp.Paren, 499 ) 500 501 PARAMETERIZABLE_TEXT_TYPES = { 502 exp.DataType.Type.NVARCHAR, 503 exp.DataType.Type.VARCHAR, 504 exp.DataType.Type.CHAR, 505 exp.DataType.Type.NCHAR, 506 } 507 508 # Expressions that need to have all CTEs under them bubbled up to them 509 EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set() 510 511 SENTINEL_LINE_BREAK = "__SQLGLOT__LB__" 512 513 __slots__ = ( 514 "pretty", 515 "identify", 516 "normalize", 517 "pad", 518 "_indent", 519 "normalize_functions", 520 "unsupported_level", 521 "max_unsupported", 522 "leading_comma", 523 "max_text_width", 524 "comments", 525 "dialect", 526 "unsupported_messages", 527 "_escaped_quote_end", 528 "_escaped_identifier_end", 529 ) 530 531 def __init__( 532 self, 533 pretty: t.Optional[bool] = None, 534 identify: str | bool = False, 535 normalize: bool = False, 536 pad: int = 2, 537 indent: int = 2, 538 normalize_functions: t.Optional[str | bool] = None, 539 unsupported_level: ErrorLevel = ErrorLevel.WARN, 540 max_unsupported: int = 3, 541 leading_comma: bool = False, 542 max_text_width: int = 80, 543 comments: bool = True, 544 dialect: DialectType = None, 545 ): 546 import sqlglot 547 from sqlglot.dialects import Dialect 548 549 self.pretty = pretty if pretty is not None else sqlglot.pretty 550 self.identify = identify 551 self.normalize = normalize 552 self.pad = pad 553 self._indent = indent 554 self.unsupported_level = unsupported_level 555 self.max_unsupported = max_unsupported 556 self.leading_comma = leading_comma 557 self.max_text_width = max_text_width 558 self.comments = comments 559 self.dialect = Dialect.get_or_raise(dialect) 560 561 # This is both a Dialect property and a Generator argument, so we prioritize the latter 562 self.normalize_functions = ( 563 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 564 ) 565 566 self.unsupported_messages: t.List[str] = [] 567 self._escaped_quote_end: str = ( 568 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 569 ) 570 self._escaped_identifier_end: str = ( 571 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 572 ) 573 574 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 575 """ 576 Generates the SQL string corresponding to the given syntax tree. 577 578 Args: 579 expression: The syntax tree. 580 copy: Whether to copy the expression. The generator performs mutations so 581 it is safer to copy. 582 583 Returns: 584 The SQL string corresponding to `expression`. 585 """ 586 if copy: 587 expression = expression.copy() 588 589 expression = self.preprocess(expression) 590 591 self.unsupported_messages = [] 592 sql = self.sql(expression).strip() 593 594 if self.pretty: 595 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 596 597 if self.unsupported_level == ErrorLevel.IGNORE: 598 return sql 599 600 if self.unsupported_level == ErrorLevel.WARN: 601 for msg in self.unsupported_messages: 602 logger.warning(msg) 603 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 604 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 605 606 return sql 607 608 def preprocess(self, expression: exp.Expression) -> exp.Expression: 609 """Apply generic preprocessing transformations to a given expression.""" 610 if ( 611 not expression.parent 612 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 613 and any(node.parent is not expression for node in expression.find_all(exp.With)) 614 ): 615 from sqlglot.transforms import move_ctes_to_top_level 616 617 expression = move_ctes_to_top_level(expression) 618 619 if self.ENSURE_BOOLS: 620 from sqlglot.transforms import ensure_bools 621 622 expression = ensure_bools(expression) 623 624 return expression 625 626 def unsupported(self, message: str) -> None: 627 if self.unsupported_level == ErrorLevel.IMMEDIATE: 628 raise UnsupportedError(message) 629 self.unsupported_messages.append(message) 630 631 def sep(self, sep: str = " ") -> str: 632 return f"{sep.strip()}\n" if self.pretty else sep 633 634 def seg(self, sql: str, sep: str = " ") -> str: 635 return f"{self.sep(sep)}{sql}" 636 637 def pad_comment(self, comment: str) -> str: 638 comment = " " + comment if comment[0].strip() else comment 639 comment = comment + " " if comment[-1].strip() else comment 640 return comment 641 642 def maybe_comment( 643 self, 644 sql: str, 645 expression: t.Optional[exp.Expression] = None, 646 comments: t.Optional[t.List[str]] = None, 647 separated: bool = False, 648 ) -> str: 649 comments = ( 650 ((expression and expression.comments) if comments is None else comments) # type: ignore 651 if self.comments 652 else None 653 ) 654 655 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 656 return sql 657 658 comments_sql = " ".join( 659 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 660 ) 661 662 if not comments_sql: 663 return sql 664 665 comments_sql = self._replace_line_breaks(comments_sql) 666 667 if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS): 668 return ( 669 f"{self.sep()}{comments_sql}{sql}" 670 if not sql or sql[0].isspace() 671 else f"{comments_sql}{self.sep()}{sql}" 672 ) 673 674 return f"{sql} {comments_sql}" 675 676 def wrap(self, expression: exp.Expression | str) -> str: 677 this_sql = self.indent( 678 ( 679 self.sql(expression) 680 if isinstance(expression, exp.UNWRAPPED_QUERIES) 681 else self.sql(expression, "this") 682 ), 683 level=1, 684 pad=0, 685 ) 686 return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}" 687 688 def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str: 689 original = self.identify 690 self.identify = False 691 result = func(*args, **kwargs) 692 self.identify = original 693 return result 694 695 def normalize_func(self, name: str) -> str: 696 if self.normalize_functions == "upper" or self.normalize_functions is True: 697 return name.upper() 698 if self.normalize_functions == "lower": 699 return name.lower() 700 return name 701 702 def indent( 703 self, 704 sql: str, 705 level: int = 0, 706 pad: t.Optional[int] = None, 707 skip_first: bool = False, 708 skip_last: bool = False, 709 ) -> str: 710 if not self.pretty: 711 return sql 712 713 pad = self.pad if pad is None else pad 714 lines = sql.split("\n") 715 716 return "\n".join( 717 ( 718 line 719 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 720 else f"{' ' * (level * self._indent + pad)}{line}" 721 ) 722 for i, line in enumerate(lines) 723 ) 724 725 def sql( 726 self, 727 expression: t.Optional[str | exp.Expression], 728 key: t.Optional[str] = None, 729 comment: bool = True, 730 ) -> str: 731 if not expression: 732 return "" 733 734 if isinstance(expression, str): 735 return expression 736 737 if key: 738 value = expression.args.get(key) 739 if value: 740 return self.sql(value) 741 return "" 742 743 transform = self.TRANSFORMS.get(expression.__class__) 744 745 if callable(transform): 746 sql = transform(self, expression) 747 elif isinstance(expression, exp.Expression): 748 exp_handler_name = f"{expression.key}_sql" 749 750 if hasattr(self, exp_handler_name): 751 sql = getattr(self, exp_handler_name)(expression) 752 elif isinstance(expression, exp.Func): 753 sql = self.function_fallback_sql(expression) 754 elif isinstance(expression, exp.Property): 755 sql = self.property_sql(expression) 756 else: 757 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 758 else: 759 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 760 761 return self.maybe_comment(sql, expression) if self.comments and comment else sql 762 763 def uncache_sql(self, expression: exp.Uncache) -> str: 764 table = self.sql(expression, "this") 765 exists_sql = " IF EXISTS" if expression.args.get("exists") else "" 766 return f"UNCACHE TABLE{exists_sql} {table}" 767 768 def cache_sql(self, expression: exp.Cache) -> str: 769 lazy = " LAZY" if expression.args.get("lazy") else "" 770 table = self.sql(expression, "this") 771 options = expression.args.get("options") 772 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 773 sql = self.sql(expression, "expression") 774 sql = f" AS{self.sep()}{sql}" if sql else "" 775 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 776 return self.prepend_ctes(expression, sql) 777 778 def characterset_sql(self, expression: exp.CharacterSet) -> str: 779 if isinstance(expression.parent, exp.Cast): 780 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 781 default = "DEFAULT " if expression.args.get("default") else "" 782 return f"{default}CHARACTER SET={self.sql(expression, 'this')}" 783 784 def column_parts(self, expression: exp.Column) -> str: 785 return ".".join( 786 self.sql(part) 787 for part in ( 788 expression.args.get("catalog"), 789 expression.args.get("db"), 790 expression.args.get("table"), 791 expression.args.get("this"), 792 ) 793 if part 794 ) 795 796 def column_sql(self, expression: exp.Column) -> str: 797 join_mark = " (+)" if expression.args.get("join_mark") else "" 798 799 if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED: 800 join_mark = "" 801 self.unsupported("Outer join syntax using the (+) operator is not supported.") 802 803 return f"{self.column_parts(expression)}{join_mark}" 804 805 def columnposition_sql(self, expression: exp.ColumnPosition) -> str: 806 this = self.sql(expression, "this") 807 this = f" {this}" if this else "" 808 position = self.sql(expression, "position") 809 return f"{position}{this}" 810 811 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 812 column = self.sql(expression, "this") 813 kind = self.sql(expression, "kind") 814 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 815 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 816 kind = f"{sep}{kind}" if kind else "" 817 constraints = f" {constraints}" if constraints else "" 818 position = self.sql(expression, "position") 819 position = f" {position}" if position else "" 820 821 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 822 kind = "" 823 824 return f"{exists}{column}{kind}{constraints}{position}" 825 826 def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str: 827 this = self.sql(expression, "this") 828 kind_sql = self.sql(expression, "kind").strip() 829 return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql 830 831 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 832 this = self.sql(expression, "this") 833 if expression.args.get("not_null"): 834 persisted = " PERSISTED NOT NULL" 835 elif expression.args.get("persisted"): 836 persisted = " PERSISTED" 837 else: 838 persisted = "" 839 return f"AS {this}{persisted}" 840 841 def autoincrementcolumnconstraint_sql(self, _) -> str: 842 return self.token_sql(TokenType.AUTO_INCREMENT) 843 844 def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str: 845 if isinstance(expression.this, list): 846 this = self.wrap(self.expressions(expression, key="this", flat=True)) 847 else: 848 this = self.sql(expression, "this") 849 850 return f"COMPRESS {this}" 851 852 def generatedasidentitycolumnconstraint_sql( 853 self, expression: exp.GeneratedAsIdentityColumnConstraint 854 ) -> str: 855 this = "" 856 if expression.this is not None: 857 on_null = " ON NULL" if expression.args.get("on_null") else "" 858 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 859 860 start = expression.args.get("start") 861 start = f"START WITH {start}" if start else "" 862 increment = expression.args.get("increment") 863 increment = f" INCREMENT BY {increment}" if increment else "" 864 minvalue = expression.args.get("minvalue") 865 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 866 maxvalue = expression.args.get("maxvalue") 867 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 868 cycle = expression.args.get("cycle") 869 cycle_sql = "" 870 871 if cycle is not None: 872 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 873 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 874 875 sequence_opts = "" 876 if start or increment or cycle_sql: 877 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 878 sequence_opts = f" ({sequence_opts.strip()})" 879 880 expr = self.sql(expression, "expression") 881 expr = f"({expr})" if expr else "IDENTITY" 882 883 return f"GENERATED{this} AS {expr}{sequence_opts}" 884 885 def generatedasrowcolumnconstraint_sql( 886 self, expression: exp.GeneratedAsRowColumnConstraint 887 ) -> str: 888 start = "START" if expression.args.get("start") else "END" 889 hidden = " HIDDEN" if expression.args.get("hidden") else "" 890 return f"GENERATED ALWAYS AS ROW {start}{hidden}" 891 892 def periodforsystemtimeconstraint_sql( 893 self, expression: exp.PeriodForSystemTimeConstraint 894 ) -> str: 895 return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})" 896 897 def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str: 898 return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL" 899 900 def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str: 901 return f"AS {self.sql(expression, 'this')}" 902 903 def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str: 904 desc = expression.args.get("desc") 905 if desc is not None: 906 return f"PRIMARY KEY{' DESC' if desc else ' ASC'}" 907 return "PRIMARY KEY" 908 909 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 910 this = self.sql(expression, "this") 911 this = f" {this}" if this else "" 912 index_type = expression.args.get("index_type") 913 index_type = f" USING {index_type}" if index_type else "" 914 on_conflict = self.sql(expression, "on_conflict") 915 on_conflict = f" {on_conflict}" if on_conflict else "" 916 return f"UNIQUE{this}{index_type}{on_conflict}" 917 918 def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str: 919 return self.sql(expression, "this") 920 921 def create_sql(self, expression: exp.Create) -> str: 922 kind = self.sql(expression, "kind") 923 properties = expression.args.get("properties") 924 properties_locs = self.locate_properties(properties) if properties else defaultdict() 925 926 this = self.createable_sql(expression, properties_locs) 927 928 properties_sql = "" 929 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 930 exp.Properties.Location.POST_WITH 931 ): 932 properties_sql = self.sql( 933 exp.Properties( 934 expressions=[ 935 *properties_locs[exp.Properties.Location.POST_SCHEMA], 936 *properties_locs[exp.Properties.Location.POST_WITH], 937 ] 938 ) 939 ) 940 941 begin = " BEGIN" if expression.args.get("begin") else "" 942 end = " END" if expression.args.get("end") else "" 943 944 expression_sql = self.sql(expression, "expression") 945 if expression_sql: 946 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 947 948 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 949 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 950 postalias_props_sql = self.properties( 951 exp.Properties( 952 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 953 ), 954 wrapped=False, 955 ) 956 expression_sql = f" AS {postalias_props_sql}{expression_sql}" 957 else: 958 expression_sql = f" AS{expression_sql}" 959 960 postindex_props_sql = "" 961 if properties_locs.get(exp.Properties.Location.POST_INDEX): 962 postindex_props_sql = self.properties( 963 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 964 wrapped=False, 965 prefix=" ", 966 ) 967 968 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 969 indexes = f" {indexes}" if indexes else "" 970 index_sql = indexes + postindex_props_sql 971 972 replace = " OR REPLACE" if expression.args.get("replace") else "" 973 unique = " UNIQUE" if expression.args.get("unique") else "" 974 975 postcreate_props_sql = "" 976 if properties_locs.get(exp.Properties.Location.POST_CREATE): 977 postcreate_props_sql = self.properties( 978 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 979 sep=" ", 980 prefix=" ", 981 wrapped=False, 982 ) 983 984 modifiers = "".join((replace, unique, postcreate_props_sql)) 985 986 postexpression_props_sql = "" 987 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 988 postexpression_props_sql = self.properties( 989 exp.Properties( 990 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 991 ), 992 sep=" ", 993 prefix=" ", 994 wrapped=False, 995 ) 996 997 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 998 no_schema_binding = ( 999 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 1000 ) 1001 1002 clone = self.sql(expression, "clone") 1003 clone = f" {clone}" if clone else "" 1004 1005 expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 1006 return self.prepend_ctes(expression, expression_sql) 1007 1008 def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str: 1009 start = self.sql(expression, "start") 1010 start = f"START WITH {start}" if start else "" 1011 increment = self.sql(expression, "increment") 1012 increment = f" INCREMENT BY {increment}" if increment else "" 1013 minvalue = self.sql(expression, "minvalue") 1014 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 1015 maxvalue = self.sql(expression, "maxvalue") 1016 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 1017 owned = self.sql(expression, "owned") 1018 owned = f" OWNED BY {owned}" if owned else "" 1019 1020 cache = expression.args.get("cache") 1021 if cache is None: 1022 cache_str = "" 1023 elif cache is True: 1024 cache_str = " CACHE" 1025 else: 1026 cache_str = f" CACHE {cache}" 1027 1028 options = self.expressions(expression, key="options", flat=True, sep=" ") 1029 options = f" {options}" if options else "" 1030 1031 return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip() 1032 1033 def clone_sql(self, expression: exp.Clone) -> str: 1034 this = self.sql(expression, "this") 1035 shallow = "SHALLOW " if expression.args.get("shallow") else "" 1036 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 1037 return f"{shallow}{keyword} {this}" 1038 1039 def describe_sql(self, expression: exp.Describe) -> str: 1040 style = expression.args.get("style") 1041 style = f" {style}" if style else "" 1042 return f"DESCRIBE{style} {self.sql(expression, 'this')}" 1043 1044 def heredoc_sql(self, expression: exp.Heredoc) -> str: 1045 tag = self.sql(expression, "tag") 1046 return f"${tag}${self.sql(expression, 'this')}${tag}$" 1047 1048 def prepend_ctes(self, expression: exp.Expression, sql: str) -> str: 1049 with_ = self.sql(expression, "with") 1050 if with_: 1051 sql = f"{with_}{self.sep()}{sql}" 1052 return sql 1053 1054 def with_sql(self, expression: exp.With) -> str: 1055 sql = self.expressions(expression, flat=True) 1056 recursive = ( 1057 "RECURSIVE " 1058 if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive") 1059 else "" 1060 ) 1061 1062 return f"WITH {recursive}{sql}" 1063 1064 def cte_sql(self, expression: exp.CTE) -> str: 1065 alias = self.sql(expression, "alias") 1066 1067 materialized = expression.args.get("materialized") 1068 if materialized is False: 1069 materialized = "NOT MATERIALIZED " 1070 elif materialized: 1071 materialized = "MATERIALIZED " 1072 1073 return f"{alias} AS {materialized or ''}{self.wrap(expression)}" 1074 1075 def tablealias_sql(self, expression: exp.TableAlias) -> str: 1076 alias = self.sql(expression, "this") 1077 columns = self.expressions(expression, key="columns", flat=True) 1078 columns = f"({columns})" if columns else "" 1079 1080 if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS: 1081 columns = "" 1082 self.unsupported("Named columns are not supported in table alias.") 1083 1084 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 1085 alias = "_t" 1086 1087 return f"{alias}{columns}" 1088 1089 def bitstring_sql(self, expression: exp.BitString) -> str: 1090 this = self.sql(expression, "this") 1091 if self.dialect.BIT_START: 1092 return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}" 1093 return f"{int(this, 2)}" 1094 1095 def hexstring_sql(self, expression: exp.HexString) -> str: 1096 this = self.sql(expression, "this") 1097 if self.dialect.HEX_START: 1098 return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}" 1099 return f"{int(this, 16)}" 1100 1101 def bytestring_sql(self, expression: exp.ByteString) -> str: 1102 this = self.sql(expression, "this") 1103 if self.dialect.BYTE_START: 1104 return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}" 1105 return this 1106 1107 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 1108 this = self.sql(expression, "this") 1109 escape = expression.args.get("escape") 1110 1111 if self.dialect.UNICODE_START: 1112 escape = f" UESCAPE {self.sql(escape)}" if escape else "" 1113 return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}" 1114 1115 if escape: 1116 pattern = re.compile(rf"{escape.name}(\d+)") 1117 else: 1118 pattern = ESCAPED_UNICODE_RE 1119 1120 this = pattern.sub(r"\\u\1", this) 1121 return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}" 1122 1123 def rawstring_sql(self, expression: exp.RawString) -> str: 1124 string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False) 1125 return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}" 1126 1127 def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str: 1128 this = self.sql(expression, "this") 1129 specifier = self.sql(expression, "expression") 1130 specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else "" 1131 return f"{this}{specifier}" 1132 1133 def datatype_sql(self, expression: exp.DataType) -> str: 1134 type_value = expression.this 1135 1136 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 1137 type_sql = self.sql(expression, "kind") 1138 else: 1139 type_sql = ( 1140 self.TYPE_MAPPING.get(type_value, type_value.value) 1141 if isinstance(type_value, exp.DataType.Type) 1142 else type_value 1143 ) 1144 1145 nested = "" 1146 interior = self.expressions(expression, flat=True) 1147 values = "" 1148 1149 if interior: 1150 if expression.args.get("nested"): 1151 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 1152 if expression.args.get("values") is not None: 1153 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 1154 values = self.expressions(expression, key="values", flat=True) 1155 values = f"{delimiters[0]}{values}{delimiters[1]}" 1156 elif type_value == exp.DataType.Type.INTERVAL: 1157 nested = f" {interior}" 1158 else: 1159 nested = f"({interior})" 1160 1161 type_sql = f"{type_sql}{nested}{values}" 1162 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 1163 exp.DataType.Type.TIMETZ, 1164 exp.DataType.Type.TIMESTAMPTZ, 1165 ): 1166 type_sql = f"{type_sql} WITH TIME ZONE" 1167 1168 return type_sql 1169 1170 def directory_sql(self, expression: exp.Directory) -> str: 1171 local = "LOCAL " if expression.args.get("local") else "" 1172 row_format = self.sql(expression, "row_format") 1173 row_format = f" {row_format}" if row_format else "" 1174 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}" 1175 1176 def delete_sql(self, expression: exp.Delete) -> str: 1177 this = self.sql(expression, "this") 1178 this = f" FROM {this}" if this else "" 1179 using = self.sql(expression, "using") 1180 using = f" USING {using}" if using else "" 1181 where = self.sql(expression, "where") 1182 returning = self.sql(expression, "returning") 1183 limit = self.sql(expression, "limit") 1184 tables = self.expressions(expression, key="tables") 1185 tables = f" {tables}" if tables else "" 1186 if self.RETURNING_END: 1187 expression_sql = f"{this}{using}{where}{returning}{limit}" 1188 else: 1189 expression_sql = f"{returning}{this}{using}{where}{limit}" 1190 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}") 1191 1192 def drop_sql(self, expression: exp.Drop) -> str: 1193 this = self.sql(expression, "this") 1194 expressions = self.expressions(expression, flat=True) 1195 expressions = f" ({expressions})" if expressions else "" 1196 kind = expression.args["kind"] 1197 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1198 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1199 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1200 cascade = " CASCADE" if expression.args.get("cascade") else "" 1201 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1202 purge = " PURGE" if expression.args.get("purge") else "" 1203 return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{expressions}{cascade}{constraints}{purge}" 1204 1205 def except_sql(self, expression: exp.Except) -> str: 1206 return self.set_operations(expression) 1207 1208 def except_op(self, expression: exp.Except) -> str: 1209 return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}" 1210 1211 def fetch_sql(self, expression: exp.Fetch) -> str: 1212 direction = expression.args.get("direction") 1213 direction = f" {direction}" if direction else "" 1214 count = expression.args.get("count") 1215 count = f" {count}" if count else "" 1216 if expression.args.get("percent"): 1217 count = f"{count} PERCENT" 1218 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1219 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}" 1220 1221 def filter_sql(self, expression: exp.Filter) -> str: 1222 if self.AGGREGATE_FILTER_SUPPORTED: 1223 this = self.sql(expression, "this") 1224 where = self.sql(expression, "expression").strip() 1225 return f"{this} FILTER({where})" 1226 1227 agg = expression.this 1228 agg_arg = agg.this 1229 cond = expression.expression.this 1230 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1231 return self.sql(agg) 1232 1233 def hint_sql(self, expression: exp.Hint) -> str: 1234 if not self.QUERY_HINTS: 1235 self.unsupported("Hints are not supported") 1236 return "" 1237 1238 return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */" 1239 1240 def indexparameters_sql(self, expression: exp.IndexParameters) -> str: 1241 using = self.sql(expression, "using") 1242 using = f" USING {using}" if using else "" 1243 columns = self.expressions(expression, key="columns", flat=True) 1244 columns = f"({columns})" if columns else "" 1245 partition_by = self.expressions(expression, key="partition_by", flat=True) 1246 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1247 where = self.sql(expression, "where") 1248 include = self.expressions(expression, key="include", flat=True) 1249 if include: 1250 include = f" INCLUDE ({include})" 1251 with_storage = self.expressions(expression, key="with_storage", flat=True) 1252 with_storage = f" WITH ({with_storage})" if with_storage else "" 1253 tablespace = self.sql(expression, "tablespace") 1254 tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else "" 1255 1256 return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}" 1257 1258 def index_sql(self, expression: exp.Index) -> str: 1259 unique = "UNIQUE " if expression.args.get("unique") else "" 1260 primary = "PRIMARY " if expression.args.get("primary") else "" 1261 amp = "AMP " if expression.args.get("amp") else "" 1262 name = self.sql(expression, "this") 1263 name = f"{name} " if name else "" 1264 table = self.sql(expression, "table") 1265 table = f"{self.INDEX_ON} {table}" if table else "" 1266 1267 index = "INDEX " if not table else "" 1268 1269 params = self.sql(expression, "params") 1270 return f"{unique}{primary}{amp}{index}{name}{table}{params}" 1271 1272 def identifier_sql(self, expression: exp.Identifier) -> str: 1273 text = expression.name 1274 lower = text.lower() 1275 text = lower if self.normalize and not expression.quoted else text 1276 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1277 if ( 1278 expression.quoted 1279 or self.dialect.can_identify(text, self.identify) 1280 or lower in self.RESERVED_KEYWORDS 1281 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1282 ): 1283 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1284 return text 1285 1286 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1287 input_format = self.sql(expression, "input_format") 1288 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1289 output_format = self.sql(expression, "output_format") 1290 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1291 return self.sep().join((input_format, output_format)) 1292 1293 def national_sql(self, expression: exp.National, prefix: str = "N") -> str: 1294 string = self.sql(exp.Literal.string(expression.name)) 1295 return f"{prefix}{string}" 1296 1297 def partition_sql(self, expression: exp.Partition) -> str: 1298 return f"PARTITION({self.expressions(expression, flat=True)})" 1299 1300 def properties_sql(self, expression: exp.Properties) -> str: 1301 root_properties = [] 1302 with_properties = [] 1303 1304 for p in expression.expressions: 1305 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1306 if p_loc == exp.Properties.Location.POST_WITH: 1307 with_properties.append(p) 1308 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1309 root_properties.append(p) 1310 1311 return self.root_properties( 1312 exp.Properties(expressions=root_properties) 1313 ) + self.with_properties(exp.Properties(expressions=with_properties)) 1314 1315 def root_properties(self, properties: exp.Properties) -> str: 1316 if properties.expressions: 1317 return self.sep() + self.expressions(properties, indent=False, sep=" ") 1318 return "" 1319 1320 def properties( 1321 self, 1322 properties: exp.Properties, 1323 prefix: str = "", 1324 sep: str = ", ", 1325 suffix: str = "", 1326 wrapped: bool = True, 1327 ) -> str: 1328 if properties.expressions: 1329 expressions = self.expressions(properties, sep=sep, indent=False) 1330 if expressions: 1331 expressions = self.wrap(expressions) if wrapped else expressions 1332 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1333 return "" 1334 1335 def with_properties(self, properties: exp.Properties) -> str: 1336 return self.properties(properties, prefix=self.seg("WITH")) 1337 1338 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1339 properties_locs = defaultdict(list) 1340 for p in properties.expressions: 1341 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1342 if p_loc != exp.Properties.Location.UNSUPPORTED: 1343 properties_locs[p_loc].append(p) 1344 else: 1345 self.unsupported(f"Unsupported property {p.key}") 1346 1347 return properties_locs 1348 1349 def property_name(self, expression: exp.Property, string_key: bool = False) -> str: 1350 if isinstance(expression.this, exp.Dot): 1351 return self.sql(expression, "this") 1352 return f"'{expression.name}'" if string_key else expression.name 1353 1354 def property_sql(self, expression: exp.Property) -> str: 1355 property_cls = expression.__class__ 1356 if property_cls == exp.Property: 1357 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1358 1359 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1360 if not property_name: 1361 self.unsupported(f"Unsupported property {expression.key}") 1362 1363 return f"{property_name}={self.sql(expression, 'this')}" 1364 1365 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1366 if self.SUPPORTS_CREATE_TABLE_LIKE: 1367 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1368 options = f" {options}" if options else "" 1369 1370 like = f"LIKE {self.sql(expression, 'this')}{options}" 1371 if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema): 1372 like = f"({like})" 1373 1374 return like 1375 1376 if expression.expressions: 1377 self.unsupported("Transpilation of LIKE property options is unsupported") 1378 1379 select = exp.select("*").from_(expression.this).limit(0) 1380 return f"AS {self.sql(select)}" 1381 1382 def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str: 1383 no = "NO " if expression.args.get("no") else "" 1384 protection = " PROTECTION" if expression.args.get("protection") else "" 1385 return f"{no}FALLBACK{protection}" 1386 1387 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1388 no = "NO " if expression.args.get("no") else "" 1389 local = expression.args.get("local") 1390 local = f"{local} " if local else "" 1391 dual = "DUAL " if expression.args.get("dual") else "" 1392 before = "BEFORE " if expression.args.get("before") else "" 1393 after = "AFTER " if expression.args.get("after") else "" 1394 return f"{no}{local}{dual}{before}{after}JOURNAL" 1395 1396 def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str: 1397 freespace = self.sql(expression, "this") 1398 percent = " PERCENT" if expression.args.get("percent") else "" 1399 return f"FREESPACE={freespace}{percent}" 1400 1401 def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str: 1402 if expression.args.get("default"): 1403 property = "DEFAULT" 1404 elif expression.args.get("on"): 1405 property = "ON" 1406 else: 1407 property = "OFF" 1408 return f"CHECKSUM={property}" 1409 1410 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1411 if expression.args.get("no"): 1412 return "NO MERGEBLOCKRATIO" 1413 if expression.args.get("default"): 1414 return "DEFAULT MERGEBLOCKRATIO" 1415 1416 percent = " PERCENT" if expression.args.get("percent") else "" 1417 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}" 1418 1419 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1420 default = expression.args.get("default") 1421 minimum = expression.args.get("minimum") 1422 maximum = expression.args.get("maximum") 1423 if default or minimum or maximum: 1424 if default: 1425 prop = "DEFAULT" 1426 elif minimum: 1427 prop = "MINIMUM" 1428 else: 1429 prop = "MAXIMUM" 1430 return f"{prop} DATABLOCKSIZE" 1431 units = expression.args.get("units") 1432 units = f" {units}" if units else "" 1433 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}" 1434 1435 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1436 autotemp = expression.args.get("autotemp") 1437 always = expression.args.get("always") 1438 default = expression.args.get("default") 1439 manual = expression.args.get("manual") 1440 never = expression.args.get("never") 1441 1442 if autotemp is not None: 1443 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1444 elif always: 1445 prop = "ALWAYS" 1446 elif default: 1447 prop = "DEFAULT" 1448 elif manual: 1449 prop = "MANUAL" 1450 elif never: 1451 prop = "NEVER" 1452 return f"BLOCKCOMPRESSION={prop}" 1453 1454 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1455 no = expression.args.get("no") 1456 no = " NO" if no else "" 1457 concurrent = expression.args.get("concurrent") 1458 concurrent = " CONCURRENT" if concurrent else "" 1459 target = self.sql(expression, "target") 1460 target = f" {target}" if target else "" 1461 return f"WITH{no}{concurrent} ISOLATED LOADING{target}" 1462 1463 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1464 if isinstance(expression.this, list): 1465 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1466 if expression.this: 1467 modulus = self.sql(expression, "this") 1468 remainder = self.sql(expression, "expression") 1469 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1470 1471 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1472 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1473 return f"FROM ({from_expressions}) TO ({to_expressions})" 1474 1475 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1476 this = self.sql(expression, "this") 1477 1478 for_values_or_default = expression.expression 1479 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1480 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1481 else: 1482 for_values_or_default = " DEFAULT" 1483 1484 return f"PARTITION OF {this}{for_values_or_default}" 1485 1486 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1487 kind = expression.args.get("kind") 1488 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1489 for_or_in = expression.args.get("for_or_in") 1490 for_or_in = f" {for_or_in}" if for_or_in else "" 1491 lock_type = expression.args.get("lock_type") 1492 override = " OVERRIDE" if expression.args.get("override") else "" 1493 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}" 1494 1495 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1496 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1497 statistics = expression.args.get("statistics") 1498 statistics_sql = "" 1499 if statistics is not None: 1500 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1501 return f"{data_sql}{statistics_sql}" 1502 1503 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1504 sql = "WITH(SYSTEM_VERSIONING=ON" 1505 1506 if expression.this: 1507 history_table = self.sql(expression, "this") 1508 sql = f"{sql}(HISTORY_TABLE={history_table}" 1509 1510 if expression.expression: 1511 data_consistency_check = self.sql(expression, "expression") 1512 sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}" 1513 1514 sql = f"{sql})" 1515 1516 return f"{sql})" 1517 1518 def insert_sql(self, expression: exp.Insert) -> str: 1519 hint = self.sql(expression, "hint") 1520 overwrite = expression.args.get("overwrite") 1521 1522 if isinstance(expression.this, exp.Directory): 1523 this = " OVERWRITE" if overwrite else " INTO" 1524 else: 1525 this = self.INSERT_OVERWRITE if overwrite else " INTO" 1526 1527 stored = self.sql(expression, "stored") 1528 stored = f" {stored}" if stored else "" 1529 alternative = expression.args.get("alternative") 1530 alternative = f" OR {alternative}" if alternative else "" 1531 ignore = " IGNORE" if expression.args.get("ignore") else "" 1532 is_function = expression.args.get("is_function") 1533 if is_function: 1534 this = f"{this} FUNCTION" 1535 this = f"{this} {self.sql(expression, 'this')}" 1536 1537 exists = " IF EXISTS" if expression.args.get("exists") else "" 1538 where = self.sql(expression, "where") 1539 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1540 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1541 on_conflict = self.sql(expression, "conflict") 1542 on_conflict = f" {on_conflict}" if on_conflict else "" 1543 by_name = " BY NAME" if expression.args.get("by_name") else "" 1544 returning = self.sql(expression, "returning") 1545 1546 if self.RETURNING_END: 1547 expression_sql = f"{expression_sql}{on_conflict}{returning}" 1548 else: 1549 expression_sql = f"{returning}{expression_sql}{on_conflict}" 1550 1551 sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{where}{expression_sql}" 1552 return self.prepend_ctes(expression, sql) 1553 1554 def intersect_sql(self, expression: exp.Intersect) -> str: 1555 return self.set_operations(expression) 1556 1557 def intersect_op(self, expression: exp.Intersect) -> str: 1558 return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}" 1559 1560 def introducer_sql(self, expression: exp.Introducer) -> str: 1561 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 1562 1563 def kill_sql(self, expression: exp.Kill) -> str: 1564 kind = self.sql(expression, "kind") 1565 kind = f" {kind}" if kind else "" 1566 this = self.sql(expression, "this") 1567 this = f" {this}" if this else "" 1568 return f"KILL{kind}{this}" 1569 1570 def pseudotype_sql(self, expression: exp.PseudoType) -> str: 1571 return expression.name 1572 1573 def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str: 1574 return expression.name 1575 1576 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1577 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1578 1579 constraint = self.sql(expression, "constraint") 1580 constraint = f" ON CONSTRAINT {constraint}" if constraint else "" 1581 1582 conflict_keys = self.expressions(expression, key="conflict_keys", flat=True) 1583 conflict_keys = f"({conflict_keys}) " if conflict_keys else " " 1584 action = self.sql(expression, "action") 1585 1586 expressions = self.expressions(expression, flat=True) 1587 if expressions: 1588 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1589 expressions = f" {set_keyword}{expressions}" 1590 1591 return f"{conflict}{constraint}{conflict_keys}{action}{expressions}" 1592 1593 def returning_sql(self, expression: exp.Returning) -> str: 1594 return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}" 1595 1596 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1597 fields = self.sql(expression, "fields") 1598 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1599 escaped = self.sql(expression, "escaped") 1600 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1601 items = self.sql(expression, "collection_items") 1602 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1603 keys = self.sql(expression, "map_keys") 1604 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1605 lines = self.sql(expression, "lines") 1606 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1607 null = self.sql(expression, "null") 1608 null = f" NULL DEFINED AS {null}" if null else "" 1609 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}" 1610 1611 def withtablehint_sql(self, expression: exp.WithTableHint) -> str: 1612 return f"WITH ({self.expressions(expression, flat=True)})" 1613 1614 def indextablehint_sql(self, expression: exp.IndexTableHint) -> str: 1615 this = f"{self.sql(expression, 'this')} INDEX" 1616 target = self.sql(expression, "target") 1617 target = f" FOR {target}" if target else "" 1618 return f"{this}{target} ({self.expressions(expression, flat=True)})" 1619 1620 def historicaldata_sql(self, expression: exp.HistoricalData) -> str: 1621 this = self.sql(expression, "this") 1622 kind = self.sql(expression, "kind") 1623 expr = self.sql(expression, "expression") 1624 return f"{this} ({kind} => {expr})" 1625 1626 def table_parts(self, expression: exp.Table) -> str: 1627 return ".".join( 1628 self.sql(part) 1629 for part in ( 1630 expression.args.get("catalog"), 1631 expression.args.get("db"), 1632 expression.args.get("this"), 1633 ) 1634 if part is not None 1635 ) 1636 1637 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1638 table = self.table_parts(expression) 1639 only = "ONLY " if expression.args.get("only") else "" 1640 partition = self.sql(expression, "partition") 1641 partition = f" {partition}" if partition else "" 1642 version = self.sql(expression, "version") 1643 version = f" {version}" if version else "" 1644 alias = self.sql(expression, "alias") 1645 alias = f"{sep}{alias}" if alias else "" 1646 hints = self.expressions(expression, key="hints", sep=" ") 1647 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1648 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 1649 pivots = f" {pivots}" if pivots else "" 1650 joins = self.indent( 1651 self.expressions(expression, key="joins", sep="", flat=True), skip_first=True 1652 ) 1653 laterals = self.expressions(expression, key="laterals", sep="") 1654 1655 file_format = self.sql(expression, "format") 1656 if file_format: 1657 pattern = self.sql(expression, "pattern") 1658 pattern = f", PATTERN => {pattern}" if pattern else "" 1659 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1660 1661 ordinality = expression.args.get("ordinality") or "" 1662 if ordinality: 1663 ordinality = f" WITH ORDINALITY{alias}" 1664 alias = "" 1665 1666 when = self.sql(expression, "when") 1667 if when: 1668 table = f"{table} {when}" 1669 1670 return f"{only}{table}{partition}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}" 1671 1672 def tablesample_sql( 1673 self, 1674 expression: exp.TableSample, 1675 sep: str = " AS ", 1676 tablesample_keyword: t.Optional[str] = None, 1677 ) -> str: 1678 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1679 table = expression.this.copy() 1680 table.set("alias", None) 1681 this = self.sql(table) 1682 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1683 else: 1684 this = self.sql(expression, "this") 1685 alias = "" 1686 1687 method = self.sql(expression, "method") 1688 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1689 numerator = self.sql(expression, "bucket_numerator") 1690 denominator = self.sql(expression, "bucket_denominator") 1691 field = self.sql(expression, "bucket_field") 1692 field = f" ON {field}" if field else "" 1693 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1694 seed = self.sql(expression, "seed") 1695 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1696 1697 size = self.sql(expression, "size") 1698 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1699 size = f"{size} ROWS" 1700 1701 percent = self.sql(expression, "percent") 1702 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1703 percent = f"{percent} PERCENT" 1704 1705 expr = f"{bucket}{percent}{size}" 1706 if self.TABLESAMPLE_REQUIRES_PARENS: 1707 expr = f"({expr})" 1708 1709 return ( 1710 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1711 ) 1712 1713 def pivot_sql(self, expression: exp.Pivot) -> str: 1714 expressions = self.expressions(expression, flat=True) 1715 1716 if expression.this: 1717 this = self.sql(expression, "this") 1718 if not expressions: 1719 return f"UNPIVOT {this}" 1720 1721 on = f"{self.seg('ON')} {expressions}" 1722 using = self.expressions(expression, key="using", flat=True) 1723 using = f"{self.seg('USING')} {using}" if using else "" 1724 group = self.sql(expression, "group") 1725 return f"PIVOT {this}{on}{using}{group}" 1726 1727 alias = self.sql(expression, "alias") 1728 alias = f" AS {alias}" if alias else "" 1729 direction = "UNPIVOT" if expression.unpivot else "PIVOT" 1730 field = self.sql(expression, "field") 1731 include_nulls = expression.args.get("include_nulls") 1732 if include_nulls is not None: 1733 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1734 else: 1735 nulls = "" 1736 return f"{direction}{nulls}({expressions} FOR {field}){alias}" 1737 1738 def version_sql(self, expression: exp.Version) -> str: 1739 this = f"FOR {expression.name}" 1740 kind = expression.text("kind") 1741 expr = self.sql(expression, "expression") 1742 return f"{this} {kind} {expr}" 1743 1744 def tuple_sql(self, expression: exp.Tuple) -> str: 1745 return f"({self.expressions(expression, flat=True)})" 1746 1747 def update_sql(self, expression: exp.Update) -> str: 1748 this = self.sql(expression, "this") 1749 set_sql = self.expressions(expression, flat=True) 1750 from_sql = self.sql(expression, "from") 1751 where_sql = self.sql(expression, "where") 1752 returning = self.sql(expression, "returning") 1753 order = self.sql(expression, "order") 1754 limit = self.sql(expression, "limit") 1755 if self.RETURNING_END: 1756 expression_sql = f"{from_sql}{where_sql}{returning}" 1757 else: 1758 expression_sql = f"{returning}{from_sql}{where_sql}" 1759 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1760 return self.prepend_ctes(expression, sql) 1761 1762 def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str: 1763 values_as_table = values_as_table and self.VALUES_AS_TABLE 1764 1765 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1766 if values_as_table or not expression.find_ancestor(exp.From, exp.Join): 1767 args = self.expressions(expression) 1768 alias = self.sql(expression, "alias") 1769 values = f"VALUES{self.seg('')}{args}" 1770 values = ( 1771 f"({values})" 1772 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1773 else values 1774 ) 1775 return f"{values} AS {alias}" if alias else values 1776 1777 # Converts `VALUES...` expression into a series of select unions. 1778 alias_node = expression.args.get("alias") 1779 column_names = alias_node and alias_node.columns 1780 1781 selects: t.List[exp.Query] = [] 1782 1783 for i, tup in enumerate(expression.expressions): 1784 row = tup.expressions 1785 1786 if i == 0 and column_names: 1787 row = [ 1788 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1789 ] 1790 1791 selects.append(exp.Select(expressions=row)) 1792 1793 if self.pretty: 1794 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1795 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1796 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1797 query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1798 return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False)) 1799 1800 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1801 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1802 return f"({unions}){alias}" 1803 1804 def var_sql(self, expression: exp.Var) -> str: 1805 return self.sql(expression, "this") 1806 1807 def into_sql(self, expression: exp.Into) -> str: 1808 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1809 unlogged = " UNLOGGED" if expression.args.get("unlogged") else "" 1810 return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}" 1811 1812 def from_sql(self, expression: exp.From) -> str: 1813 return f"{self.seg('FROM')} {self.sql(expression, 'this')}" 1814 1815 def group_sql(self, expression: exp.Group) -> str: 1816 group_by_all = expression.args.get("all") 1817 if group_by_all is True: 1818 modifier = " ALL" 1819 elif group_by_all is False: 1820 modifier = " DISTINCT" 1821 else: 1822 modifier = "" 1823 1824 group_by = self.op_expressions(f"GROUP BY{modifier}", expression) 1825 1826 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1827 grouping_sets = ( 1828 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1829 ) 1830 1831 cube = expression.args.get("cube", []) 1832 if seq_get(cube, 0) is True: 1833 return f"{group_by}{self.seg('WITH CUBE')}" 1834 else: 1835 cube_sql = self.expressions(expression, key="cube", indent=False) 1836 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1837 1838 rollup = expression.args.get("rollup", []) 1839 if seq_get(rollup, 0) is True: 1840 return f"{group_by}{self.seg('WITH ROLLUP')}" 1841 else: 1842 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1843 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1844 1845 groupings = csv( 1846 grouping_sets, 1847 cube_sql, 1848 rollup_sql, 1849 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1850 sep=self.GROUPINGS_SEP, 1851 ) 1852 1853 if expression.args.get("expressions") and groupings: 1854 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1855 1856 return f"{group_by}{groupings}" 1857 1858 def having_sql(self, expression: exp.Having) -> str: 1859 this = self.indent(self.sql(expression, "this")) 1860 return f"{self.seg('HAVING')}{self.sep()}{this}" 1861 1862 def connect_sql(self, expression: exp.Connect) -> str: 1863 start = self.sql(expression, "start") 1864 start = self.seg(f"START WITH {start}") if start else "" 1865 nocycle = " NOCYCLE" if expression.args.get("nocycle") else "" 1866 connect = self.sql(expression, "connect") 1867 connect = self.seg(f"CONNECT BY{nocycle} {connect}") 1868 return start + connect 1869 1870 def prior_sql(self, expression: exp.Prior) -> str: 1871 return f"PRIOR {self.sql(expression, 'this')}" 1872 1873 def join_sql(self, expression: exp.Join) -> str: 1874 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1875 side = None 1876 else: 1877 side = expression.side 1878 1879 op_sql = " ".join( 1880 op 1881 for op in ( 1882 expression.method, 1883 "GLOBAL" if expression.args.get("global") else None, 1884 side, 1885 expression.kind, 1886 expression.hint if self.JOIN_HINTS else None, 1887 ) 1888 if op 1889 ) 1890 match_cond = self.sql(expression, "match_condition") 1891 match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else "" 1892 on_sql = self.sql(expression, "on") 1893 using = expression.args.get("using") 1894 1895 if not on_sql and using: 1896 on_sql = csv(*(self.sql(column) for column in using)) 1897 1898 this = expression.this 1899 this_sql = self.sql(this) 1900 1901 if on_sql: 1902 on_sql = self.indent(on_sql, skip_first=True) 1903 space = self.seg(" " * self.pad) if self.pretty else " " 1904 if using: 1905 on_sql = f"{space}USING ({on_sql})" 1906 else: 1907 on_sql = f"{space}ON {on_sql}" 1908 elif not op_sql: 1909 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 1910 return f" {this_sql}" 1911 1912 return f", {this_sql}" 1913 1914 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1915 return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}" 1916 1917 def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str: 1918 args = self.expressions(expression, flat=True) 1919 args = f"({args})" if len(args.split(",")) > 1 else args 1920 return f"{args} {arrow_sep} {self.sql(expression, 'this')}" 1921 1922 def lateral_op(self, expression: exp.Lateral) -> str: 1923 cross_apply = expression.args.get("cross_apply") 1924 1925 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 1926 if cross_apply is True: 1927 op = "INNER JOIN " 1928 elif cross_apply is False: 1929 op = "LEFT JOIN " 1930 else: 1931 op = "" 1932 1933 return f"{op}LATERAL" 1934 1935 def lateral_sql(self, expression: exp.Lateral) -> str: 1936 this = self.sql(expression, "this") 1937 1938 if expression.args.get("view"): 1939 alias = expression.args["alias"] 1940 columns = self.expressions(alias, key="columns", flat=True) 1941 table = f" {alias.name}" if alias.name else "" 1942 columns = f" AS {columns}" if columns else "" 1943 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1944 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1945 1946 alias = self.sql(expression, "alias") 1947 alias = f" AS {alias}" if alias else "" 1948 return f"{self.lateral_op(expression)} {this}{alias}" 1949 1950 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1951 this = self.sql(expression, "this") 1952 1953 args = [ 1954 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1955 for e in (expression.args.get(k) for k in ("offset", "expression")) 1956 if e 1957 ] 1958 1959 args_sql = ", ".join(self.sql(e) for e in args) 1960 args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql 1961 expressions = self.expressions(expression, flat=True) 1962 expressions = f" BY {expressions}" if expressions else "" 1963 1964 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}" 1965 1966 def offset_sql(self, expression: exp.Offset) -> str: 1967 this = self.sql(expression, "this") 1968 value = expression.expression 1969 value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value 1970 expressions = self.expressions(expression, flat=True) 1971 expressions = f" BY {expressions}" if expressions else "" 1972 return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}" 1973 1974 def setitem_sql(self, expression: exp.SetItem) -> str: 1975 kind = self.sql(expression, "kind") 1976 kind = f"{kind} " if kind else "" 1977 this = self.sql(expression, "this") 1978 expressions = self.expressions(expression) 1979 collate = self.sql(expression, "collate") 1980 collate = f" COLLATE {collate}" if collate else "" 1981 global_ = "GLOBAL " if expression.args.get("global") else "" 1982 return f"{global_}{kind}{this}{expressions}{collate}" 1983 1984 def set_sql(self, expression: exp.Set) -> str: 1985 expressions = ( 1986 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 1987 ) 1988 tag = " TAG" if expression.args.get("tag") else "" 1989 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}" 1990 1991 def pragma_sql(self, expression: exp.Pragma) -> str: 1992 return f"PRAGMA {self.sql(expression, 'this')}" 1993 1994 def lock_sql(self, expression: exp.Lock) -> str: 1995 if not self.LOCKING_READS_SUPPORTED: 1996 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 1997 return "" 1998 1999 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 2000 expressions = self.expressions(expression, flat=True) 2001 expressions = f" OF {expressions}" if expressions else "" 2002 wait = expression.args.get("wait") 2003 2004 if wait is not None: 2005 if isinstance(wait, exp.Literal): 2006 wait = f" WAIT {self.sql(wait)}" 2007 else: 2008 wait = " NOWAIT" if wait else " SKIP LOCKED" 2009 2010 return f"{lock_type}{expressions}{wait or ''}" 2011 2012 def literal_sql(self, expression: exp.Literal) -> str: 2013 text = expression.this or "" 2014 if expression.is_string: 2015 text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}" 2016 return text 2017 2018 def escape_str(self, text: str, escape_backslash: bool = True) -> str: 2019 if self.dialect.ESCAPED_SEQUENCES: 2020 to_escaped = self.dialect.ESCAPED_SEQUENCES 2021 text = "".join( 2022 to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text 2023 ) 2024 2025 return self._replace_line_breaks(text).replace( 2026 self.dialect.QUOTE_END, self._escaped_quote_end 2027 ) 2028 2029 def loaddata_sql(self, expression: exp.LoadData) -> str: 2030 local = " LOCAL" if expression.args.get("local") else "" 2031 inpath = f" INPATH {self.sql(expression, 'inpath')}" 2032 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 2033 this = f" INTO TABLE {self.sql(expression, 'this')}" 2034 partition = self.sql(expression, "partition") 2035 partition = f" {partition}" if partition else "" 2036 input_format = self.sql(expression, "input_format") 2037 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 2038 serde = self.sql(expression, "serde") 2039 serde = f" SERDE {serde}" if serde else "" 2040 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}" 2041 2042 def null_sql(self, *_) -> str: 2043 return "NULL" 2044 2045 def boolean_sql(self, expression: exp.Boolean) -> str: 2046 return "TRUE" if expression.this else "FALSE" 2047 2048 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 2049 this = self.sql(expression, "this") 2050 this = f"{this} " if this else this 2051 siblings = "SIBLINGS " if expression.args.get("siblings") else "" 2052 order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore 2053 interpolated_values = [ 2054 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 2055 for named_expression in expression.args.get("interpolate") or [] 2056 ] 2057 interpolate = ( 2058 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 2059 ) 2060 return f"{order}{interpolate}" 2061 2062 def withfill_sql(self, expression: exp.WithFill) -> str: 2063 from_sql = self.sql(expression, "from") 2064 from_sql = f" FROM {from_sql}" if from_sql else "" 2065 to_sql = self.sql(expression, "to") 2066 to_sql = f" TO {to_sql}" if to_sql else "" 2067 step_sql = self.sql(expression, "step") 2068 step_sql = f" STEP {step_sql}" if step_sql else "" 2069 return f"WITH FILL{from_sql}{to_sql}{step_sql}" 2070 2071 def cluster_sql(self, expression: exp.Cluster) -> str: 2072 return self.op_expressions("CLUSTER BY", expression) 2073 2074 def distribute_sql(self, expression: exp.Distribute) -> str: 2075 return self.op_expressions("DISTRIBUTE BY", expression) 2076 2077 def sort_sql(self, expression: exp.Sort) -> str: 2078 return self.op_expressions("SORT BY", expression) 2079 2080 def ordered_sql(self, expression: exp.Ordered) -> str: 2081 desc = expression.args.get("desc") 2082 asc = not desc 2083 2084 nulls_first = expression.args.get("nulls_first") 2085 nulls_last = not nulls_first 2086 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 2087 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 2088 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 2089 2090 this = self.sql(expression, "this") 2091 2092 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 2093 nulls_sort_change = "" 2094 if nulls_first and ( 2095 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 2096 ): 2097 nulls_sort_change = " NULLS FIRST" 2098 elif ( 2099 nulls_last 2100 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 2101 and not nulls_are_last 2102 ): 2103 nulls_sort_change = " NULLS LAST" 2104 2105 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 2106 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 2107 window = expression.find_ancestor(exp.Window, exp.Select) 2108 if isinstance(window, exp.Window) and window.args.get("spec"): 2109 self.unsupported( 2110 f"'{nulls_sort_change.strip()}' translation not supported in window functions" 2111 ) 2112 nulls_sort_change = "" 2113 elif self.NULL_ORDERING_SUPPORTED is None: 2114 if expression.this.is_int: 2115 self.unsupported( 2116 f"'{nulls_sort_change.strip()}' translation not supported with positional ordering" 2117 ) 2118 elif not isinstance(expression.this, exp.Rand): 2119 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 2120 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 2121 nulls_sort_change = "" 2122 2123 with_fill = self.sql(expression, "with_fill") 2124 with_fill = f" {with_fill}" if with_fill else "" 2125 2126 return f"{this}{sort_order}{nulls_sort_change}{with_fill}" 2127 2128 def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str: 2129 window_frame = self.sql(expression, "window_frame") 2130 window_frame = f"{window_frame} " if window_frame else "" 2131 2132 this = self.sql(expression, "this") 2133 2134 return f"{window_frame}{this}" 2135 2136 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 2137 partition = self.partition_by_sql(expression) 2138 order = self.sql(expression, "order") 2139 measures = self.expressions(expression, key="measures") 2140 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 2141 rows = self.sql(expression, "rows") 2142 rows = self.seg(rows) if rows else "" 2143 after = self.sql(expression, "after") 2144 after = self.seg(after) if after else "" 2145 pattern = self.sql(expression, "pattern") 2146 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 2147 definition_sqls = [ 2148 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 2149 for definition in expression.args.get("define", []) 2150 ] 2151 definitions = self.expressions(sqls=definition_sqls) 2152 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 2153 body = "".join( 2154 ( 2155 partition, 2156 order, 2157 measures, 2158 rows, 2159 after, 2160 pattern, 2161 define, 2162 ) 2163 ) 2164 alias = self.sql(expression, "alias") 2165 alias = f" {alias}" if alias else "" 2166 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}" 2167 2168 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 2169 limit = expression.args.get("limit") 2170 2171 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 2172 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 2173 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 2174 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 2175 2176 options = self.expressions(expression, key="options") 2177 if options: 2178 options = f" OPTION{self.wrap(options)}" 2179 2180 return csv( 2181 *sqls, 2182 *[self.sql(join) for join in expression.args.get("joins") or []], 2183 self.sql(expression, "connect"), 2184 self.sql(expression, "match"), 2185 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 2186 self.sql(expression, "prewhere"), 2187 self.sql(expression, "where"), 2188 self.sql(expression, "group"), 2189 self.sql(expression, "having"), 2190 *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()], 2191 self.sql(expression, "order"), 2192 *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit), 2193 *self.after_limit_modifiers(expression), 2194 options, 2195 sep="", 2196 ) 2197 2198 def queryoption_sql(self, expression: exp.QueryOption) -> str: 2199 return "" 2200 2201 def offset_limit_modifiers( 2202 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 2203 ) -> t.List[str]: 2204 return [ 2205 self.sql(expression, "offset") if fetch else self.sql(limit), 2206 self.sql(limit) if fetch else self.sql(expression, "offset"), 2207 ] 2208 2209 def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]: 2210 locks = self.expressions(expression, key="locks", sep=" ") 2211 locks = f" {locks}" if locks else "" 2212 return [locks, self.sql(expression, "sample")] 2213 2214 def select_sql(self, expression: exp.Select) -> str: 2215 into = expression.args.get("into") 2216 if not self.SUPPORTS_SELECT_INTO and into: 2217 into.pop() 2218 2219 hint = self.sql(expression, "hint") 2220 distinct = self.sql(expression, "distinct") 2221 distinct = f" {distinct}" if distinct else "" 2222 kind = self.sql(expression, "kind") 2223 2224 limit = expression.args.get("limit") 2225 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP: 2226 top = self.limit_sql(limit, top=True) 2227 limit.pop() 2228 else: 2229 top = "" 2230 2231 expressions = self.expressions(expression) 2232 2233 if kind: 2234 if kind in self.SELECT_KINDS: 2235 kind = f" AS {kind}" 2236 else: 2237 if kind == "STRUCT": 2238 expressions = self.expressions( 2239 sqls=[ 2240 self.sql( 2241 exp.Struct( 2242 expressions=[ 2243 exp.PropertyEQ(this=e.args.get("alias"), expression=e.this) 2244 if isinstance(e, exp.Alias) 2245 else e 2246 for e in expression.expressions 2247 ] 2248 ) 2249 ) 2250 ] 2251 ) 2252 kind = "" 2253 2254 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2255 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2256 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2257 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2258 sql = self.query_modifiers( 2259 expression, 2260 f"SELECT{top_distinct}{kind}{expressions}", 2261 self.sql(expression, "into", comment=False), 2262 self.sql(expression, "from", comment=False), 2263 ) 2264 2265 sql = self.prepend_ctes(expression, sql) 2266 2267 if not self.SUPPORTS_SELECT_INTO and into: 2268 if into.args.get("temporary"): 2269 table_kind = " TEMPORARY" 2270 elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"): 2271 table_kind = " UNLOGGED" 2272 else: 2273 table_kind = "" 2274 sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}" 2275 2276 return sql 2277 2278 def schema_sql(self, expression: exp.Schema) -> str: 2279 this = self.sql(expression, "this") 2280 sql = self.schema_columns_sql(expression) 2281 return f"{this} {sql}" if this and sql else this or sql 2282 2283 def schema_columns_sql(self, expression: exp.Schema) -> str: 2284 if expression.expressions: 2285 return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}" 2286 return "" 2287 2288 def star_sql(self, expression: exp.Star) -> str: 2289 except_ = self.expressions(expression, key="except", flat=True) 2290 except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else "" 2291 replace = self.expressions(expression, key="replace", flat=True) 2292 replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else "" 2293 return f"*{except_}{replace}" 2294 2295 def parameter_sql(self, expression: exp.Parameter) -> str: 2296 this = self.sql(expression, "this") 2297 return f"{self.PARAMETER_TOKEN}{this}" 2298 2299 def sessionparameter_sql(self, expression: exp.SessionParameter) -> str: 2300 this = self.sql(expression, "this") 2301 kind = expression.text("kind") 2302 if kind: 2303 kind = f"{kind}." 2304 return f"@@{kind}{this}" 2305 2306 def placeholder_sql(self, expression: exp.Placeholder) -> str: 2307 return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?" 2308 2309 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2310 alias = self.sql(expression, "alias") 2311 alias = f"{sep}{alias}" if alias else "" 2312 2313 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2314 pivots = f" {pivots}" if pivots else "" 2315 2316 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2317 return self.prepend_ctes(expression, sql) 2318 2319 def qualify_sql(self, expression: exp.Qualify) -> str: 2320 this = self.indent(self.sql(expression, "this")) 2321 return f"{self.seg('QUALIFY')}{self.sep()}{this}" 2322 2323 def set_operations(self, expression: exp.Union) -> str: 2324 if not self.OUTER_UNION_MODIFIERS: 2325 limit = expression.args.get("limit") 2326 order = expression.args.get("order") 2327 2328 if limit or order: 2329 select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False) 2330 2331 if limit: 2332 select = select.limit(limit.pop(), copy=False) 2333 if order: 2334 select = select.order_by(order.pop(), copy=False) 2335 return self.sql(select) 2336 2337 sqls: t.List[str] = [] 2338 stack: t.List[t.Union[str, exp.Expression]] = [expression] 2339 2340 while stack: 2341 node = stack.pop() 2342 2343 if isinstance(node, exp.Union): 2344 stack.append(node.expression) 2345 stack.append( 2346 self.maybe_comment( 2347 getattr(self, f"{node.key}_op")(node), 2348 comments=node.comments, 2349 separated=True, 2350 ) 2351 ) 2352 stack.append(node.this) 2353 else: 2354 sqls.append(self.sql(node)) 2355 2356 this = self.sep().join(sqls) 2357 this = self.query_modifiers(expression, this) 2358 return self.prepend_ctes(expression, this) 2359 2360 def union_sql(self, expression: exp.Union) -> str: 2361 return self.set_operations(expression) 2362 2363 def union_op(self, expression: exp.Union) -> str: 2364 kind = " DISTINCT" if self.EXPLICIT_UNION else "" 2365 kind = kind if expression.args.get("distinct") else " ALL" 2366 by_name = " BY NAME" if expression.args.get("by_name") else "" 2367 return f"UNION{kind}{by_name}" 2368 2369 def unnest_sql(self, expression: exp.Unnest) -> str: 2370 args = self.expressions(expression, flat=True) 2371 2372 alias = expression.args.get("alias") 2373 offset = expression.args.get("offset") 2374 2375 if self.UNNEST_WITH_ORDINALITY: 2376 if alias and isinstance(offset, exp.Expression): 2377 alias.append("columns", offset) 2378 2379 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2380 columns = alias.columns 2381 alias = self.sql(columns[0]) if columns else "" 2382 else: 2383 alias = self.sql(alias) 2384 2385 alias = f" AS {alias}" if alias else alias 2386 if self.UNNEST_WITH_ORDINALITY: 2387 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2388 else: 2389 if isinstance(offset, exp.Expression): 2390 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2391 elif offset: 2392 suffix = f"{alias} WITH OFFSET" 2393 else: 2394 suffix = alias 2395 2396 return f"UNNEST({args}){suffix}" 2397 2398 def prewhere_sql(self, expression: exp.PreWhere) -> str: 2399 return "" 2400 2401 def where_sql(self, expression: exp.Where) -> str: 2402 this = self.indent(self.sql(expression, "this")) 2403 return f"{self.seg('WHERE')}{self.sep()}{this}" 2404 2405 def window_sql(self, expression: exp.Window) -> str: 2406 this = self.sql(expression, "this") 2407 partition = self.partition_by_sql(expression) 2408 order = expression.args.get("order") 2409 order = self.order_sql(order, flat=True) if order else "" 2410 spec = self.sql(expression, "spec") 2411 alias = self.sql(expression, "alias") 2412 over = self.sql(expression, "over") or "OVER" 2413 2414 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2415 2416 first = expression.args.get("first") 2417 if first is None: 2418 first = "" 2419 else: 2420 first = "FIRST" if first else "LAST" 2421 2422 if not partition and not order and not spec and alias: 2423 return f"{this} {alias}" 2424 2425 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2426 return f"{this} ({args})" 2427 2428 def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str: 2429 partition = self.expressions(expression, key="partition_by", flat=True) 2430 return f"PARTITION BY {partition}" if partition else "" 2431 2432 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2433 kind = self.sql(expression, "kind") 2434 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2435 end = ( 2436 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2437 or "CURRENT ROW" 2438 ) 2439 return f"{kind} BETWEEN {start} AND {end}" 2440 2441 def withingroup_sql(self, expression: exp.WithinGroup) -> str: 2442 this = self.sql(expression, "this") 2443 expression_sql = self.sql(expression, "expression")[1:] # order has a leading space 2444 return f"{this} WITHIN GROUP ({expression_sql})" 2445 2446 def between_sql(self, expression: exp.Between) -> str: 2447 this = self.sql(expression, "this") 2448 low = self.sql(expression, "low") 2449 high = self.sql(expression, "high") 2450 return f"{this} BETWEEN {low} AND {high}" 2451 2452 def bracket_offset_expressions(self, expression: exp.Bracket) -> t.List[exp.Expression]: 2453 return apply_index_offset( 2454 expression.this, 2455 expression.expressions, 2456 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2457 ) 2458 2459 def bracket_sql(self, expression: exp.Bracket) -> str: 2460 expressions = self.bracket_offset_expressions(expression) 2461 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2462 return f"{self.sql(expression, 'this')}[{expressions_sql}]" 2463 2464 def all_sql(self, expression: exp.All) -> str: 2465 return f"ALL {self.wrap(expression)}" 2466 2467 def any_sql(self, expression: exp.Any) -> str: 2468 this = self.sql(expression, "this") 2469 if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)): 2470 if isinstance(expression.this, exp.UNWRAPPED_QUERIES): 2471 this = self.wrap(this) 2472 return f"ANY{this}" 2473 return f"ANY {this}" 2474 2475 def exists_sql(self, expression: exp.Exists) -> str: 2476 return f"EXISTS{self.wrap(expression)}" 2477 2478 def case_sql(self, expression: exp.Case) -> str: 2479 this = self.sql(expression, "this") 2480 statements = [f"CASE {this}" if this else "CASE"] 2481 2482 for e in expression.args["ifs"]: 2483 statements.append(f"WHEN {self.sql(e, 'this')}") 2484 statements.append(f"THEN {self.sql(e, 'true')}") 2485 2486 default = self.sql(expression, "default") 2487 2488 if default: 2489 statements.append(f"ELSE {default}") 2490 2491 statements.append("END") 2492 2493 if self.pretty and self.too_wide(statements): 2494 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2495 2496 return " ".join(statements) 2497 2498 def constraint_sql(self, expression: exp.Constraint) -> str: 2499 this = self.sql(expression, "this") 2500 expressions = self.expressions(expression, flat=True) 2501 return f"CONSTRAINT {this} {expressions}" 2502 2503 def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str: 2504 order = expression.args.get("order") 2505 order = f" OVER ({self.order_sql(order, flat=True)})" if order else "" 2506 return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}" 2507 2508 def extract_sql(self, expression: exp.Extract) -> str: 2509 this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name 2510 expression_sql = self.sql(expression, "expression") 2511 return f"EXTRACT({this} FROM {expression_sql})" 2512 2513 def trim_sql(self, expression: exp.Trim) -> str: 2514 trim_type = self.sql(expression, "position") 2515 2516 if trim_type == "LEADING": 2517 return self.func("LTRIM", expression.this) 2518 elif trim_type == "TRAILING": 2519 return self.func("RTRIM", expression.this) 2520 else: 2521 return self.func("TRIM", expression.this, expression.expression) 2522 2523 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2524 args = expression.expressions 2525 if isinstance(expression, exp.ConcatWs): 2526 args = args[1:] # Skip the delimiter 2527 2528 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2529 args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args] 2530 2531 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2532 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2533 2534 return args 2535 2536 def concat_sql(self, expression: exp.Concat) -> str: 2537 expressions = self.convert_concat_args(expression) 2538 2539 # Some dialects don't allow a single-argument CONCAT call 2540 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2541 return self.sql(expressions[0]) 2542 2543 return self.func("CONCAT", *expressions) 2544 2545 def concatws_sql(self, expression: exp.ConcatWs) -> str: 2546 return self.func( 2547 "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression) 2548 ) 2549 2550 def check_sql(self, expression: exp.Check) -> str: 2551 this = self.sql(expression, key="this") 2552 return f"CHECK ({this})" 2553 2554 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2555 expressions = self.expressions(expression, flat=True) 2556 reference = self.sql(expression, "reference") 2557 reference = f" {reference}" if reference else "" 2558 delete = self.sql(expression, "delete") 2559 delete = f" ON DELETE {delete}" if delete else "" 2560 update = self.sql(expression, "update") 2561 update = f" ON UPDATE {update}" if update else "" 2562 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}" 2563 2564 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2565 expressions = self.expressions(expression, flat=True) 2566 options = self.expressions(expression, key="options", flat=True, sep=" ") 2567 options = f" {options}" if options else "" 2568 return f"PRIMARY KEY ({expressions}){options}" 2569 2570 def if_sql(self, expression: exp.If) -> str: 2571 return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false"))) 2572 2573 def matchagainst_sql(self, expression: exp.MatchAgainst) -> str: 2574 modifier = expression.args.get("modifier") 2575 modifier = f" {modifier}" if modifier else "" 2576 return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})" 2577 2578 def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str: 2579 return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}" 2580 2581 def jsonpath_sql(self, expression: exp.JSONPath) -> str: 2582 path = self.expressions(expression, sep="", flat=True).lstrip(".") 2583 return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}" 2584 2585 def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str: 2586 if isinstance(expression, exp.JSONPathPart): 2587 transform = self.TRANSFORMS.get(expression.__class__) 2588 if not callable(transform): 2589 self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}") 2590 return "" 2591 2592 return transform(self, expression) 2593 2594 if isinstance(expression, int): 2595 return str(expression) 2596 2597 if self.JSON_PATH_SINGLE_QUOTE_ESCAPE: 2598 escaped = expression.replace("'", "\\'") 2599 escaped = f"\\'{expression}\\'" 2600 else: 2601 escaped = expression.replace('"', '\\"') 2602 escaped = f'"{escaped}"' 2603 2604 return escaped 2605 2606 def formatjson_sql(self, expression: exp.FormatJson) -> str: 2607 return f"{self.sql(expression, 'this')} FORMAT JSON" 2608 2609 def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str: 2610 null_handling = expression.args.get("null_handling") 2611 null_handling = f" {null_handling}" if null_handling else "" 2612 2613 unique_keys = expression.args.get("unique_keys") 2614 if unique_keys is not None: 2615 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2616 else: 2617 unique_keys = "" 2618 2619 return_type = self.sql(expression, "return_type") 2620 return_type = f" RETURNING {return_type}" if return_type else "" 2621 encoding = self.sql(expression, "encoding") 2622 encoding = f" ENCODING {encoding}" if encoding else "" 2623 2624 return self.func( 2625 "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG", 2626 *expression.expressions, 2627 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2628 ) 2629 2630 def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str: 2631 return self.jsonobject_sql(expression) 2632 2633 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2634 null_handling = expression.args.get("null_handling") 2635 null_handling = f" {null_handling}" if null_handling else "" 2636 return_type = self.sql(expression, "return_type") 2637 return_type = f" RETURNING {return_type}" if return_type else "" 2638 strict = " STRICT" if expression.args.get("strict") else "" 2639 return self.func( 2640 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2641 ) 2642 2643 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2644 this = self.sql(expression, "this") 2645 order = self.sql(expression, "order") 2646 null_handling = expression.args.get("null_handling") 2647 null_handling = f" {null_handling}" if null_handling else "" 2648 return_type = self.sql(expression, "return_type") 2649 return_type = f" RETURNING {return_type}" if return_type else "" 2650 strict = " STRICT" if expression.args.get("strict") else "" 2651 return self.func( 2652 "JSON_ARRAYAGG", 2653 this, 2654 suffix=f"{order}{null_handling}{return_type}{strict})", 2655 ) 2656 2657 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2658 path = self.sql(expression, "path") 2659 path = f" PATH {path}" if path else "" 2660 nested_schema = self.sql(expression, "nested_schema") 2661 2662 if nested_schema: 2663 return f"NESTED{path} {nested_schema}" 2664 2665 this = self.sql(expression, "this") 2666 kind = self.sql(expression, "kind") 2667 kind = f" {kind}" if kind else "" 2668 return f"{this}{kind}{path}" 2669 2670 def jsonschema_sql(self, expression: exp.JSONSchema) -> str: 2671 return self.func("COLUMNS", *expression.expressions) 2672 2673 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2674 this = self.sql(expression, "this") 2675 path = self.sql(expression, "path") 2676 path = f", {path}" if path else "" 2677 error_handling = expression.args.get("error_handling") 2678 error_handling = f" {error_handling}" if error_handling else "" 2679 empty_handling = expression.args.get("empty_handling") 2680 empty_handling = f" {empty_handling}" if empty_handling else "" 2681 schema = self.sql(expression, "schema") 2682 return self.func( 2683 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2684 ) 2685 2686 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2687 this = self.sql(expression, "this") 2688 kind = self.sql(expression, "kind") 2689 path = self.sql(expression, "path") 2690 path = f" {path}" if path else "" 2691 as_json = " AS JSON" if expression.args.get("as_json") else "" 2692 return f"{this} {kind}{path}{as_json}" 2693 2694 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2695 this = self.sql(expression, "this") 2696 path = self.sql(expression, "path") 2697 path = f", {path}" if path else "" 2698 expressions = self.expressions(expression) 2699 with_ = ( 2700 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2701 if expressions 2702 else "" 2703 ) 2704 return f"OPENJSON({this}{path}){with_}" 2705 2706 def in_sql(self, expression: exp.In) -> str: 2707 query = expression.args.get("query") 2708 unnest = expression.args.get("unnest") 2709 field = expression.args.get("field") 2710 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2711 2712 if query: 2713 in_sql = self.sql(query) 2714 elif unnest: 2715 in_sql = self.in_unnest_op(unnest) 2716 elif field: 2717 in_sql = self.sql(field) 2718 else: 2719 in_sql = f"({self.expressions(expression, flat=True)})" 2720 2721 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}" 2722 2723 def in_unnest_op(self, unnest: exp.Unnest) -> str: 2724 return f"(SELECT {self.sql(unnest)})" 2725 2726 def interval_sql(self, expression: exp.Interval) -> str: 2727 unit = self.sql(expression, "unit") 2728 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2729 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2730 unit = f" {unit}" if unit else "" 2731 2732 if self.SINGLE_STRING_INTERVAL: 2733 this = expression.this.name if expression.this else "" 2734 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2735 2736 this = self.sql(expression, "this") 2737 if this: 2738 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2739 this = f" {this}" if unwrapped else f" ({this})" 2740 2741 return f"INTERVAL{this}{unit}" 2742 2743 def return_sql(self, expression: exp.Return) -> str: 2744 return f"RETURN {self.sql(expression, 'this')}" 2745 2746 def reference_sql(self, expression: exp.Reference) -> str: 2747 this = self.sql(expression, "this") 2748 expressions = self.expressions(expression, flat=True) 2749 expressions = f"({expressions})" if expressions else "" 2750 options = self.expressions(expression, key="options", flat=True, sep=" ") 2751 options = f" {options}" if options else "" 2752 return f"REFERENCES {this}{expressions}{options}" 2753 2754 def anonymous_sql(self, expression: exp.Anonymous) -> str: 2755 return self.func(self.sql(expression, "this"), *expression.expressions) 2756 2757 def paren_sql(self, expression: exp.Paren) -> str: 2758 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2759 return f"({sql}{self.seg(')', sep='')}" 2760 2761 def neg_sql(self, expression: exp.Neg) -> str: 2762 # This makes sure we don't convert "- - 5" to "--5", which is a comment 2763 this_sql = self.sql(expression, "this") 2764 sep = " " if this_sql[0] == "-" else "" 2765 return f"-{sep}{this_sql}" 2766 2767 def not_sql(self, expression: exp.Not) -> str: 2768 return f"NOT {self.sql(expression, 'this')}" 2769 2770 def alias_sql(self, expression: exp.Alias) -> str: 2771 alias = self.sql(expression, "alias") 2772 alias = f" AS {alias}" if alias else "" 2773 return f"{self.sql(expression, 'this')}{alias}" 2774 2775 def pivotalias_sql(self, expression: exp.PivotAlias) -> str: 2776 alias = expression.args["alias"] 2777 identifier_alias = isinstance(alias, exp.Identifier) 2778 2779 if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2780 alias.replace(exp.Literal.string(alias.output_name)) 2781 elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2782 alias.replace(exp.to_identifier(alias.output_name)) 2783 2784 return self.alias_sql(expression) 2785 2786 def aliases_sql(self, expression: exp.Aliases) -> str: 2787 return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})" 2788 2789 def atindex_sql(self, expression: exp.AtTimeZone) -> str: 2790 this = self.sql(expression, "this") 2791 index = self.sql(expression, "expression") 2792 return f"{this} AT {index}" 2793 2794 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 2795 this = self.sql(expression, "this") 2796 zone = self.sql(expression, "zone") 2797 return f"{this} AT TIME ZONE {zone}" 2798 2799 def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str: 2800 this = self.sql(expression, "this") 2801 zone = self.sql(expression, "zone") 2802 return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'" 2803 2804 def add_sql(self, expression: exp.Add) -> str: 2805 return self.binary(expression, "+") 2806 2807 def and_sql( 2808 self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None 2809 ) -> str: 2810 return self.connector_sql(expression, "AND", stack) 2811 2812 def or_sql( 2813 self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None 2814 ) -> str: 2815 return self.connector_sql(expression, "OR", stack) 2816 2817 def xor_sql( 2818 self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None 2819 ) -> str: 2820 return self.connector_sql(expression, "XOR", stack) 2821 2822 def connector_sql( 2823 self, 2824 expression: exp.Connector, 2825 op: str, 2826 stack: t.Optional[t.List[str | exp.Expression]] = None, 2827 ) -> str: 2828 if stack is not None: 2829 if expression.expressions: 2830 stack.append(self.expressions(expression, sep=f" {op} ")) 2831 else: 2832 stack.append(expression.right) 2833 if expression.comments: 2834 for comment in expression.comments: 2835 op += f" /*{self.pad_comment(comment)}*/" 2836 stack.extend((op, expression.left)) 2837 return op 2838 2839 stack = [expression] 2840 sqls: t.List[str] = [] 2841 ops = set() 2842 2843 while stack: 2844 node = stack.pop() 2845 if isinstance(node, exp.Connector): 2846 ops.add(getattr(self, f"{node.key}_sql")(node, stack)) 2847 else: 2848 sql = self.sql(node) 2849 if sqls and sqls[-1] in ops: 2850 sqls[-1] += f" {sql}" 2851 else: 2852 sqls.append(sql) 2853 2854 sep = "\n" if self.pretty and self.too_wide(sqls) else " " 2855 return sep.join(sqls) 2856 2857 def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str: 2858 return self.binary(expression, "&") 2859 2860 def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str: 2861 return self.binary(expression, "<<") 2862 2863 def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str: 2864 return f"~{self.sql(expression, 'this')}" 2865 2866 def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str: 2867 return self.binary(expression, "|") 2868 2869 def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str: 2870 return self.binary(expression, ">>") 2871 2872 def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str: 2873 return self.binary(expression, "^") 2874 2875 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2876 format_sql = self.sql(expression, "format") 2877 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2878 to_sql = self.sql(expression, "to") 2879 to_sql = f" {to_sql}" if to_sql else "" 2880 action = self.sql(expression, "action") 2881 action = f" {action}" if action else "" 2882 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql}{action})" 2883 2884 def currentdate_sql(self, expression: exp.CurrentDate) -> str: 2885 zone = self.sql(expression, "this") 2886 return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE" 2887 2888 def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str: 2889 return self.func("CURRENT_TIMESTAMP", expression.this) 2890 2891 def collate_sql(self, expression: exp.Collate) -> str: 2892 if self.COLLATE_IS_FUNC: 2893 return self.function_fallback_sql(expression) 2894 return self.binary(expression, "COLLATE") 2895 2896 def command_sql(self, expression: exp.Command) -> str: 2897 return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}" 2898 2899 def comment_sql(self, expression: exp.Comment) -> str: 2900 this = self.sql(expression, "this") 2901 kind = expression.args["kind"] 2902 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 2903 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2904 expression_sql = self.sql(expression, "expression") 2905 return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}" 2906 2907 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2908 this = self.sql(expression, "this") 2909 delete = " DELETE" if expression.args.get("delete") else "" 2910 recompress = self.sql(expression, "recompress") 2911 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2912 to_disk = self.sql(expression, "to_disk") 2913 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2914 to_volume = self.sql(expression, "to_volume") 2915 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2916 return f"{this}{delete}{recompress}{to_disk}{to_volume}" 2917 2918 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2919 where = self.sql(expression, "where") 2920 group = self.sql(expression, "group") 2921 aggregates = self.expressions(expression, key="aggregates") 2922 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2923 2924 if not (where or group or aggregates) and len(expression.expressions) == 1: 2925 return f"TTL {self.expressions(expression, flat=True)}" 2926 2927 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}" 2928 2929 def transaction_sql(self, expression: exp.Transaction) -> str: 2930 return "BEGIN" 2931 2932 def commit_sql(self, expression: exp.Commit) -> str: 2933 chain = expression.args.get("chain") 2934 if chain is not None: 2935 chain = " AND CHAIN" if chain else " AND NO CHAIN" 2936 2937 return f"COMMIT{chain or ''}" 2938 2939 def rollback_sql(self, expression: exp.Rollback) -> str: 2940 savepoint = expression.args.get("savepoint") 2941 savepoint = f" TO {savepoint}" if savepoint else "" 2942 return f"ROLLBACK{savepoint}" 2943 2944 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2945 this = self.sql(expression, "this") 2946 2947 dtype = self.sql(expression, "dtype") 2948 if dtype: 2949 collate = self.sql(expression, "collate") 2950 collate = f" COLLATE {collate}" if collate else "" 2951 using = self.sql(expression, "using") 2952 using = f" USING {using}" if using else "" 2953 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2954 2955 default = self.sql(expression, "default") 2956 if default: 2957 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2958 2959 comment = self.sql(expression, "comment") 2960 if comment: 2961 return f"ALTER COLUMN {this} COMMENT {comment}" 2962 2963 if not expression.args.get("drop"): 2964 self.unsupported("Unsupported ALTER COLUMN syntax") 2965 2966 return f"ALTER COLUMN {this} DROP DEFAULT" 2967 2968 def renametable_sql(self, expression: exp.RenameTable) -> str: 2969 if not self.RENAME_TABLE_WITH_DB: 2970 # Remove db from tables 2971 expression = expression.transform( 2972 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 2973 ).assert_is(exp.RenameTable) 2974 this = self.sql(expression, "this") 2975 return f"RENAME TO {this}" 2976 2977 def renamecolumn_sql(self, expression: exp.RenameColumn) -> str: 2978 exists = " IF EXISTS" if expression.args.get("exists") else "" 2979 old_column = self.sql(expression, "this") 2980 new_column = self.sql(expression, "to") 2981 return f"RENAME COLUMN{exists} {old_column} TO {new_column}" 2982 2983 def altertable_sql(self, expression: exp.AlterTable) -> str: 2984 actions = expression.args["actions"] 2985 2986 if isinstance(actions[0], exp.ColumnDef): 2987 actions = self.add_column_sql(expression) 2988 elif isinstance(actions[0], exp.Schema): 2989 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 2990 elif isinstance(actions[0], exp.Delete): 2991 actions = self.expressions(expression, key="actions", flat=True) 2992 else: 2993 actions = self.expressions(expression, key="actions", flat=True) 2994 2995 exists = " IF EXISTS" if expression.args.get("exists") else "" 2996 only = " ONLY" if expression.args.get("only") else "" 2997 options = self.expressions(expression, key="options") 2998 options = f", {options}" if options else "" 2999 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}" 3000 3001 def add_column_sql(self, expression: exp.AlterTable) -> str: 3002 if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD: 3003 return self.expressions( 3004 expression, 3005 key="actions", 3006 prefix="ADD COLUMN ", 3007 ) 3008 return f"ADD {self.expressions(expression, key='actions', flat=True)}" 3009 3010 def droppartition_sql(self, expression: exp.DropPartition) -> str: 3011 expressions = self.expressions(expression) 3012 exists = " IF EXISTS " if expression.args.get("exists") else " " 3013 return f"DROP{exists}{expressions}" 3014 3015 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 3016 return f"ADD {self.expressions(expression)}" 3017 3018 def distinct_sql(self, expression: exp.Distinct) -> str: 3019 this = self.expressions(expression, flat=True) 3020 3021 if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1: 3022 case = exp.case() 3023 for arg in expression.expressions: 3024 case = case.when(arg.is_(exp.null()), exp.null()) 3025 this = self.sql(case.else_(f"({this})")) 3026 3027 this = f" {this}" if this else "" 3028 3029 on = self.sql(expression, "on") 3030 on = f" ON {on}" if on else "" 3031 return f"DISTINCT{this}{on}" 3032 3033 def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: 3034 return self._embed_ignore_nulls(expression, "IGNORE NULLS") 3035 3036 def respectnulls_sql(self, expression: exp.RespectNulls) -> str: 3037 return self._embed_ignore_nulls(expression, "RESPECT NULLS") 3038 3039 def havingmax_sql(self, expression: exp.HavingMax) -> str: 3040 this_sql = self.sql(expression, "this") 3041 expression_sql = self.sql(expression, "expression") 3042 kind = "MAX" if expression.args.get("max") else "MIN" 3043 return f"{this_sql} HAVING {kind} {expression_sql}" 3044 3045 def intdiv_sql(self, expression: exp.IntDiv) -> str: 3046 return self.sql( 3047 exp.Cast( 3048 this=exp.Div(this=expression.this, expression=expression.expression), 3049 to=exp.DataType(this=exp.DataType.Type.INT), 3050 ) 3051 ) 3052 3053 def dpipe_sql(self, expression: exp.DPipe) -> str: 3054 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 3055 return self.func( 3056 "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten()) 3057 ) 3058 return self.binary(expression, "||") 3059 3060 def div_sql(self, expression: exp.Div) -> str: 3061 l, r = expression.left, expression.right 3062 3063 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 3064 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 3065 3066 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 3067 if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES): 3068 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 3069 3070 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 3071 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 3072 return self.sql( 3073 exp.cast( 3074 l / r, 3075 to=exp.DataType.Type.BIGINT, 3076 ) 3077 ) 3078 3079 return self.binary(expression, "/") 3080 3081 def overlaps_sql(self, expression: exp.Overlaps) -> str: 3082 return self.binary(expression, "OVERLAPS") 3083 3084 def distance_sql(self, expression: exp.Distance) -> str: 3085 return self.binary(expression, "<->") 3086 3087 def dot_sql(self, expression: exp.Dot) -> str: 3088 return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}" 3089 3090 def eq_sql(self, expression: exp.EQ) -> str: 3091 return self.binary(expression, "=") 3092 3093 def propertyeq_sql(self, expression: exp.PropertyEQ) -> str: 3094 return self.binary(expression, ":=") 3095 3096 def escape_sql(self, expression: exp.Escape) -> str: 3097 return self.binary(expression, "ESCAPE") 3098 3099 def glob_sql(self, expression: exp.Glob) -> str: 3100 return self.binary(expression, "GLOB") 3101 3102 def gt_sql(self, expression: exp.GT) -> str: 3103 return self.binary(expression, ">") 3104 3105 def gte_sql(self, expression: exp.GTE) -> str: 3106 return self.binary(expression, ">=") 3107 3108 def ilike_sql(self, expression: exp.ILike) -> str: 3109 return self.binary(expression, "ILIKE") 3110 3111 def ilikeany_sql(self, expression: exp.ILikeAny) -> str: 3112 return self.binary(expression, "ILIKE ANY") 3113 3114 def is_sql(self, expression: exp.Is) -> str: 3115 if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean): 3116 return self.sql( 3117 expression.this if expression.expression.this else exp.not_(expression.this) 3118 ) 3119 return self.binary(expression, "IS") 3120 3121 def like_sql(self, expression: exp.Like) -> str: 3122 return self.binary(expression, "LIKE") 3123 3124 def likeany_sql(self, expression: exp.LikeAny) -> str: 3125 return self.binary(expression, "LIKE ANY") 3126 3127 def similarto_sql(self, expression: exp.SimilarTo) -> str: 3128 return self.binary(expression, "SIMILAR TO") 3129 3130 def lt_sql(self, expression: exp.LT) -> str: 3131 return self.binary(expression, "<") 3132 3133 def lte_sql(self, expression: exp.LTE) -> str: 3134 return self.binary(expression, "<=") 3135 3136 def mod_sql(self, expression: exp.Mod) -> str: 3137 return self.binary(expression, "%") 3138 3139 def mul_sql(self, expression: exp.Mul) -> str: 3140 return self.binary(expression, "*") 3141 3142 def neq_sql(self, expression: exp.NEQ) -> str: 3143 return self.binary(expression, "<>") 3144 3145 def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str: 3146 return self.binary(expression, "IS NOT DISTINCT FROM") 3147 3148 def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str: 3149 return self.binary(expression, "IS DISTINCT FROM") 3150 3151 def slice_sql(self, expression: exp.Slice) -> str: 3152 return self.binary(expression, ":") 3153 3154 def sub_sql(self, expression: exp.Sub) -> str: 3155 return self.binary(expression, "-") 3156 3157 def trycast_sql(self, expression: exp.TryCast) -> str: 3158 return self.cast_sql(expression, safe_prefix="TRY_") 3159 3160 def log_sql(self, expression: exp.Log) -> str: 3161 this = expression.this 3162 expr = expression.expression 3163 3164 if self.dialect.LOG_BASE_FIRST is False: 3165 this, expr = expr, this 3166 elif self.dialect.LOG_BASE_FIRST is None and expr: 3167 if this.name in ("2", "10"): 3168 return self.func(f"LOG{this.name}", expr) 3169 3170 self.unsupported(f"Unsupported logarithm with base {self.sql(this)}") 3171 3172 return self.func("LOG", this, expr) 3173 3174 def use_sql(self, expression: exp.Use) -> str: 3175 kind = self.sql(expression, "kind") 3176 kind = f" {kind}" if kind else "" 3177 this = self.sql(expression, "this") 3178 this = f" {this}" if this else "" 3179 return f"USE{kind}{this}" 3180 3181 def binary(self, expression: exp.Binary, op: str) -> str: 3182 op = self.maybe_comment(op, comments=expression.comments) 3183 return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}" 3184 3185 def function_fallback_sql(self, expression: exp.Func) -> str: 3186 args = [] 3187 3188 for key in expression.arg_types: 3189 arg_value = expression.args.get(key) 3190 3191 if isinstance(arg_value, list): 3192 for value in arg_value: 3193 args.append(value) 3194 elif arg_value is not None: 3195 args.append(arg_value) 3196 3197 if self.normalize_functions: 3198 name = expression.sql_name() 3199 else: 3200 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 3201 3202 return self.func(name, *args) 3203 3204 def func( 3205 self, 3206 name: str, 3207 *args: t.Optional[exp.Expression | str], 3208 prefix: str = "(", 3209 suffix: str = ")", 3210 ) -> str: 3211 return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}" 3212 3213 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 3214 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 3215 if self.pretty and self.too_wide(arg_sqls): 3216 return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 3217 return ", ".join(arg_sqls) 3218 3219 def too_wide(self, args: t.Iterable) -> bool: 3220 return sum(len(arg) for arg in args) > self.max_text_width 3221 3222 def format_time( 3223 self, 3224 expression: exp.Expression, 3225 inverse_time_mapping: t.Optional[t.Dict[str, str]] = None, 3226 inverse_time_trie: t.Optional[t.Dict] = None, 3227 ) -> t.Optional[str]: 3228 return format_time( 3229 self.sql(expression, "format"), 3230 inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING, 3231 inverse_time_trie or self.dialect.INVERSE_TIME_TRIE, 3232 ) 3233 3234 def expressions( 3235 self, 3236 expression: t.Optional[exp.Expression] = None, 3237 key: t.Optional[str] = None, 3238 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 3239 flat: bool = False, 3240 indent: bool = True, 3241 skip_first: bool = False, 3242 skip_last: bool = False, 3243 sep: str = ", ", 3244 prefix: str = "", 3245 dynamic: bool = False, 3246 new_line: bool = False, 3247 ) -> str: 3248 expressions = expression.args.get(key or "expressions") if expression else sqls 3249 3250 if not expressions: 3251 return "" 3252 3253 if flat: 3254 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 3255 3256 num_sqls = len(expressions) 3257 3258 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 3259 if self.pretty and not self.leading_comma: 3260 stripped_sep = sep.strip() 3261 3262 result_sqls = [] 3263 for i, e in enumerate(expressions): 3264 sql = self.sql(e, comment=False) 3265 if not sql: 3266 continue 3267 3268 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 3269 3270 if self.pretty: 3271 if self.leading_comma: 3272 result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}") 3273 else: 3274 result_sqls.append( 3275 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 3276 ) 3277 else: 3278 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 3279 3280 if self.pretty and (not dynamic or self.too_wide(result_sqls)): 3281 if new_line: 3282 result_sqls.insert(0, "") 3283 result_sqls.append("") 3284 result_sql = "\n".join(result_sqls) 3285 else: 3286 result_sql = "".join(result_sqls) 3287 return ( 3288 self.indent(result_sql, skip_first=skip_first, skip_last=skip_last) 3289 if indent 3290 else result_sql 3291 ) 3292 3293 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 3294 flat = flat or isinstance(expression.parent, exp.Properties) 3295 expressions_sql = self.expressions(expression, flat=flat) 3296 if flat: 3297 return f"{op} {expressions_sql}" 3298 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}" 3299 3300 def naked_property(self, expression: exp.Property) -> str: 3301 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 3302 if not property_name: 3303 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 3304 return f"{property_name} {self.sql(expression, 'this')}" 3305 3306 def tag_sql(self, expression: exp.Tag) -> str: 3307 return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}" 3308 3309 def token_sql(self, token_type: TokenType) -> str: 3310 return self.TOKEN_MAPPING.get(token_type, token_type.name) 3311 3312 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 3313 this = self.sql(expression, "this") 3314 expressions = self.no_identify(self.expressions, expression) 3315 expressions = ( 3316 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 3317 ) 3318 return f"{this}{expressions}" 3319 3320 def joinhint_sql(self, expression: exp.JoinHint) -> str: 3321 this = self.sql(expression, "this") 3322 expressions = self.expressions(expression, flat=True) 3323 return f"{this}({expressions})" 3324 3325 def kwarg_sql(self, expression: exp.Kwarg) -> str: 3326 return self.binary(expression, "=>") 3327 3328 def when_sql(self, expression: exp.When) -> str: 3329 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 3330 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 3331 condition = self.sql(expression, "condition") 3332 condition = f" AND {condition}" if condition else "" 3333 3334 then_expression = expression.args.get("then") 3335 if isinstance(then_expression, exp.Insert): 3336 then = f"INSERT {self.sql(then_expression, 'this')}" 3337 if "expression" in then_expression.args: 3338 then += f" VALUES {self.sql(then_expression, 'expression')}" 3339 elif isinstance(then_expression, exp.Update): 3340 if isinstance(then_expression.args.get("expressions"), exp.Star): 3341 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 3342 else: 3343 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 3344 else: 3345 then = self.sql(then_expression) 3346 return f"WHEN {matched}{source}{condition} THEN {then}" 3347 3348 def merge_sql(self, expression: exp.Merge) -> str: 3349 table = expression.this 3350 table_alias = "" 3351 3352 hints = table.args.get("hints") 3353 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 3354 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 3355 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 3356 3357 this = self.sql(table) 3358 using = f"USING {self.sql(expression, 'using')}" 3359 on = f"ON {self.sql(expression, 'on')}" 3360 expressions = self.expressions(expression, sep=" ") 3361 3362 return self.prepend_ctes( 3363 expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}" 3364 ) 3365 3366 def tochar_sql(self, expression: exp.ToChar) -> str: 3367 if expression.args.get("format"): 3368 self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function") 3369 3370 return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT)) 3371 3372 def tonumber_sql(self, expression: exp.ToNumber) -> str: 3373 if not self.SUPPORTS_TO_NUMBER: 3374 self.unsupported("Unsupported TO_NUMBER function") 3375 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3376 3377 fmt = expression.args.get("format") 3378 if not fmt: 3379 self.unsupported("Conversion format is required for TO_NUMBER") 3380 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3381 3382 return self.func("TO_NUMBER", expression.this, fmt) 3383 3384 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 3385 this = self.sql(expression, "this") 3386 kind = self.sql(expression, "kind") 3387 settings_sql = self.expressions(expression, key="settings", sep=" ") 3388 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 3389 return f"{this}({kind}{args})" 3390 3391 def dictrange_sql(self, expression: exp.DictRange) -> str: 3392 this = self.sql(expression, "this") 3393 max = self.sql(expression, "max") 3394 min = self.sql(expression, "min") 3395 return f"{this}(MIN {min} MAX {max})" 3396 3397 def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str: 3398 return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}" 3399 3400 def oncluster_sql(self, expression: exp.OnCluster) -> str: 3401 return "" 3402 3403 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3404 expressions = self.expressions(expression, key="expressions", flat=True) 3405 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3406 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3407 buckets = self.sql(expression, "buckets") 3408 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS" 3409 3410 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3411 this = self.sql(expression, "this") 3412 having = self.sql(expression, "having") 3413 3414 if having: 3415 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3416 3417 return self.func("ANY_VALUE", this) 3418 3419 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3420 transform = self.func("TRANSFORM", *expression.expressions) 3421 row_format_before = self.sql(expression, "row_format_before") 3422 row_format_before = f" {row_format_before}" if row_format_before else "" 3423 record_writer = self.sql(expression, "record_writer") 3424 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3425 using = f" USING {self.sql(expression, 'command_script')}" 3426 schema = self.sql(expression, "schema") 3427 schema = f" AS {schema}" if schema else "" 3428 row_format_after = self.sql(expression, "row_format_after") 3429 row_format_after = f" {row_format_after}" if row_format_after else "" 3430 record_reader = self.sql(expression, "record_reader") 3431 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3432 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}" 3433 3434 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3435 key_block_size = self.sql(expression, "key_block_size") 3436 if key_block_size: 3437 return f"KEY_BLOCK_SIZE = {key_block_size}" 3438 3439 using = self.sql(expression, "using") 3440 if using: 3441 return f"USING {using}" 3442 3443 parser = self.sql(expression, "parser") 3444 if parser: 3445 return f"WITH PARSER {parser}" 3446 3447 comment = self.sql(expression, "comment") 3448 if comment: 3449 return f"COMMENT {comment}" 3450 3451 visible = expression.args.get("visible") 3452 if visible is not None: 3453 return "VISIBLE" if visible else "INVISIBLE" 3454 3455 engine_attr = self.sql(expression, "engine_attr") 3456 if engine_attr: 3457 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3458 3459 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3460 if secondary_engine_attr: 3461 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3462 3463 self.unsupported("Unsupported index constraint option.") 3464 return "" 3465 3466 def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str: 3467 enforced = " ENFORCED" if expression.args.get("enforced") else "" 3468 return f"CHECK ({self.sql(expression, 'this')}){enforced}" 3469 3470 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3471 kind = self.sql(expression, "kind") 3472 kind = f"{kind} INDEX" if kind else "INDEX" 3473 this = self.sql(expression, "this") 3474 this = f" {this}" if this else "" 3475 index_type = self.sql(expression, "index_type") 3476 index_type = f" USING {index_type}" if index_type else "" 3477 expressions = self.expressions(expression, flat=True) 3478 expressions = f" ({expressions})" if expressions else "" 3479 options = self.expressions(expression, key="options", sep=" ") 3480 options = f" {options}" if options else "" 3481 return f"{kind}{this}{index_type}{expressions}{options}" 3482 3483 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3484 if self.NVL2_SUPPORTED: 3485 return self.function_fallback_sql(expression) 3486 3487 case = exp.Case().when( 3488 expression.this.is_(exp.null()).not_(copy=False), 3489 expression.args["true"], 3490 copy=False, 3491 ) 3492 else_cond = expression.args.get("false") 3493 if else_cond: 3494 case.else_(else_cond, copy=False) 3495 3496 return self.sql(case) 3497 3498 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3499 this = self.sql(expression, "this") 3500 expr = self.sql(expression, "expression") 3501 iterator = self.sql(expression, "iterator") 3502 condition = self.sql(expression, "condition") 3503 condition = f" IF {condition}" if condition else "" 3504 return f"{this} FOR {expr} IN {iterator}{condition}" 3505 3506 def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str: 3507 return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})" 3508 3509 def opclass_sql(self, expression: exp.Opclass) -> str: 3510 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 3511 3512 def predict_sql(self, expression: exp.Predict) -> str: 3513 model = self.sql(expression, "this") 3514 model = f"MODEL {model}" 3515 table = self.sql(expression, "expression") 3516 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3517 parameters = self.sql(expression, "params_struct") 3518 return self.func("PREDICT", model, table, parameters or None) 3519 3520 def forin_sql(self, expression: exp.ForIn) -> str: 3521 this = self.sql(expression, "this") 3522 expression_sql = self.sql(expression, "expression") 3523 return f"FOR {this} DO {expression_sql}" 3524 3525 def refresh_sql(self, expression: exp.Refresh) -> str: 3526 this = self.sql(expression, "this") 3527 table = "" if isinstance(expression.this, exp.Literal) else "TABLE " 3528 return f"REFRESH {table}{this}" 3529 3530 def operator_sql(self, expression: exp.Operator) -> str: 3531 return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})") 3532 3533 def toarray_sql(self, expression: exp.ToArray) -> str: 3534 arg = expression.this 3535 if not arg.type: 3536 from sqlglot.optimizer.annotate_types import annotate_types 3537 3538 arg = annotate_types(arg) 3539 3540 if arg.is_type(exp.DataType.Type.ARRAY): 3541 return self.sql(arg) 3542 3543 cond_for_null = arg.is_(exp.null()) 3544 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False))) 3545 3546 def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str: 3547 this = expression.this 3548 if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME): 3549 return self.sql(this) 3550 3551 return self.sql(exp.cast(this, exp.DataType.Type.TIME)) 3552 3553 def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str: 3554 this = expression.this 3555 if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP): 3556 return self.sql(this) 3557 3558 return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP)) 3559 3560 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3561 this = expression.this 3562 time_format = self.format_time(expression) 3563 3564 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3565 return self.sql( 3566 exp.cast( 3567 exp.StrToTime(this=this, format=expression.args["format"]), 3568 exp.DataType.Type.DATE, 3569 ) 3570 ) 3571 3572 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3573 return self.sql(this) 3574 3575 return self.sql(exp.cast(this, exp.DataType.Type.DATE)) 3576 3577 def unixdate_sql(self, expression: exp.UnixDate) -> str: 3578 return self.sql( 3579 exp.func( 3580 "DATEDIFF", 3581 expression.this, 3582 exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE), 3583 "day", 3584 ) 3585 ) 3586 3587 def lastday_sql(self, expression: exp.LastDay) -> str: 3588 if self.LAST_DAY_SUPPORTS_DATE_PART: 3589 return self.function_fallback_sql(expression) 3590 3591 unit = expression.text("unit") 3592 if unit and unit != "MONTH": 3593 self.unsupported("Date parts are not supported in LAST_DAY.") 3594 3595 return self.func("LAST_DAY", expression.this) 3596 3597 def dateadd_sql(self, expression: exp.DateAdd) -> str: 3598 from sqlglot.dialects.dialect import unit_to_str 3599 3600 return self.func( 3601 "DATE_ADD", expression.this, expression.expression, unit_to_str(expression) 3602 ) 3603 3604 def arrayany_sql(self, expression: exp.ArrayAny) -> str: 3605 if self.CAN_IMPLEMENT_ARRAY_ANY: 3606 filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression) 3607 filtered_not_empty = exp.ArraySize(this=filtered).neq(0) 3608 original_is_empty = exp.ArraySize(this=expression.this).eq(0) 3609 return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty))) 3610 3611 from sqlglot.dialects import Dialect 3612 3613 # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect 3614 if self.dialect.__class__ != Dialect: 3615 self.unsupported("ARRAY_ANY is unsupported") 3616 3617 return self.function_fallback_sql(expression) 3618 3619 def generateseries_sql(self, expression: exp.GenerateSeries) -> str: 3620 expression.set("is_end_exclusive", None) 3621 return self.function_fallback_sql(expression) 3622 3623 def struct_sql(self, expression: exp.Struct) -> str: 3624 expression.set( 3625 "expressions", 3626 [ 3627 exp.alias_(e.expression, e.name if e.this.is_string else e.this) 3628 if isinstance(e, exp.PropertyEQ) 3629 else e 3630 for e in expression.expressions 3631 ], 3632 ) 3633 3634 return self.function_fallback_sql(expression) 3635 3636 def partitionrange_sql(self, expression: exp.PartitionRange) -> str: 3637 low = self.sql(expression, "this") 3638 high = self.sql(expression, "expression") 3639 3640 return f"{low} TO {high}" 3641 3642 def truncatetable_sql(self, expression: exp.TruncateTable) -> str: 3643 target = "DATABASE" if expression.args.get("is_database") else "TABLE" 3644 tables = f" {self.expressions(expression)}" 3645 3646 exists = " IF EXISTS" if expression.args.get("exists") else "" 3647 3648 on_cluster = self.sql(expression, "cluster") 3649 on_cluster = f" {on_cluster}" if on_cluster else "" 3650 3651 identity = self.sql(expression, "identity") 3652 identity = f" {identity} IDENTITY" if identity else "" 3653 3654 option = self.sql(expression, "option") 3655 option = f" {option}" if option else "" 3656 3657 partition = self.sql(expression, "partition") 3658 partition = f" {partition}" if partition else "" 3659 3660 return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}" 3661 3662 # This transpiles T-SQL's CONVERT function 3663 # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16 3664 def convert_sql(self, expression: exp.Convert) -> str: 3665 to = expression.this 3666 value = expression.expression 3667 style = expression.args.get("style") 3668 safe = expression.args.get("safe") 3669 strict = expression.args.get("strict") 3670 3671 if not to or not value: 3672 return "" 3673 3674 # Retrieve length of datatype and override to default if not specified 3675 if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3676 to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False) 3677 3678 transformed: t.Optional[exp.Expression] = None 3679 cast = exp.Cast if strict else exp.TryCast 3680 3681 # Check whether a conversion with format (T-SQL calls this 'style') is applicable 3682 if isinstance(style, exp.Literal) and style.is_int: 3683 from sqlglot.dialects.tsql import TSQL 3684 3685 style_value = style.name 3686 converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value) 3687 if not converted_style: 3688 self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}") 3689 3690 fmt = exp.Literal.string(converted_style) 3691 3692 if to.this == exp.DataType.Type.DATE: 3693 transformed = exp.StrToDate(this=value, format=fmt) 3694 elif to.this == exp.DataType.Type.DATETIME: 3695 transformed = exp.StrToTime(this=value, format=fmt) 3696 elif to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3697 transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe) 3698 elif to.this == exp.DataType.Type.TEXT: 3699 transformed = exp.TimeToStr(this=value, format=fmt) 3700 3701 if not transformed: 3702 transformed = cast(this=value, to=to, safe=safe) 3703 3704 return self.sql(transformed) 3705 3706 def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str: 3707 this = expression.this 3708 if isinstance(this, exp.JSONPathWildcard): 3709 this = self.json_path_part(this) 3710 return f".{this}" if this else "" 3711 3712 if exp.SAFE_IDENTIFIER_RE.match(this): 3713 return f".{this}" 3714 3715 this = self.json_path_part(this) 3716 return f"[{this}]" if self.JSON_PATH_BRACKETED_KEY_SUPPORTED else f".{this}" 3717 3718 def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str: 3719 this = self.json_path_part(expression.this) 3720 return f"[{this}]" if this else "" 3721 3722 def _simplify_unless_literal(self, expression: E) -> E: 3723 if not isinstance(expression, exp.Literal): 3724 from sqlglot.optimizer.simplify import simplify 3725 3726 expression = simplify(expression, dialect=self.dialect) 3727 3728 return expression 3729 3730 def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str: 3731 if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"): 3732 # The first modifier here will be the one closest to the AggFunc's arg 3733 mods = sorted( 3734 expression.find_all(exp.HavingMax, exp.Order, exp.Limit), 3735 key=lambda x: 0 3736 if isinstance(x, exp.HavingMax) 3737 else (1 if isinstance(x, exp.Order) else 2), 3738 ) 3739 3740 if mods: 3741 mod = mods[0] 3742 this = expression.__class__(this=mod.this.copy()) 3743 this.meta["inline"] = True 3744 mod.this.replace(this) 3745 return self.sql(expression.this) 3746 3747 agg_func = expression.find(exp.AggFunc) 3748 3749 if agg_func: 3750 return self.sql(agg_func)[:-1] + f" {text})" 3751 3752 return f"{self.sql(expression, 'this')} {text}" 3753 3754 def _replace_line_breaks(self, string: str) -> str: 3755 """We don't want to extra indent line breaks so we temporarily replace them with sentinels.""" 3756 if self.pretty: 3757 return string.replace("\n", self.SENTINEL_LINE_BREAK) 3758 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 } 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
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)
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 )
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.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'>, <class 'sqlglot.expressions.JSONPathScript'>, <class 'sqlglot.expressions.JSONPathRoot'>}
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'}
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.NVARCHAR: 'NVARCHAR'>, <Type.NCHAR: 'NCHAR'>, <Type.VARCHAR: 'VARCHAR'>, <Type.CHAR: 'CHAR'>}
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
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:
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
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:
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}"
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='')}"
def
indent( self, sql: str, level: int = 0, pad: Optional[int] = None, skip_first: bool = False, skip_last: bool = False) -> str:
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 )
def
sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str:
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
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)
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')}"
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}"
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}"
def
computedcolumnconstraint_sql(self, expression: sqlglot.expressions.ComputedColumnConstraint) -> str:
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}"
def
compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str:
def
generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str:
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}"
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:
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}"
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)
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()
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}"
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)}"
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}"
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}"
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
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}"
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}")
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}"
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}"
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)
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}"
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}"
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
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))
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))
def
properties( self, properties: sqlglot.expressions.Properties, prefix: str = '', sep: str = ', ', suffix: str = '', wrapped: bool = True) -> str:
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 ""
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
def
property_name( self, expression: sqlglot.expressions.Property, string_key: bool = False) -> str:
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')}"
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)}"
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"
def
mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str:
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}"
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}"
def
blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str:
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}"
def
isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str:
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}"
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})"
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}"
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}"
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}"
def
withsystemversioningproperty_sql( self, expression: sqlglot.expressions.WithSystemVersioningProperty) -> str:
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})"
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)
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}"
def
rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str:
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}"
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}"
def
tablesample_sql( self, expression: sqlglot.expressions.TableSample, sep: str = ' AS ', tablesample_keyword: Optional[str] = None) -> str:
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 )
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}"
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)
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}"
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}"
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
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}"
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"
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}"
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}"
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}"
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}"
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}"
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 ''}"
def
escape_str(self, text: str, escape_backslash: bool = True) -> str:
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 )
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}"
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}"
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}"
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}"
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}"
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 )
def
offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]:
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 ]
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
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}"
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)
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)
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}"
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})"
def
partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
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}"
def
bracket_offset_expressions( self, expression: sqlglot.expressions.Bracket) -> List[sqlglot.expressions.Expression]:
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}"
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)
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)
def
convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]:
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
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)
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}"
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}"
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
def
jsonobject_sql( self, expression: sqlglot.expressions.JSONObject | sqlglot.expressions.JSONObjectAgg) -> str:
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 )
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 )
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 )
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}"
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 )
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}"
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_}"
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}"
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}"
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}"
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)
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:
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)
def
cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str:
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})"
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}"
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}"
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}"
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"
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}"
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}"
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}"
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, "/")
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)
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)
def
func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')') -> str:
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)
def
format_time( self, expression: sqlglot.expressions.Expression, inverse_time_mapping: Optional[Dict[str, str]] = None, inverse_time_trie: Optional[Dict] = None) -> Optional[str]:
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 )
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:
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 )
def
op_expressions( self, op: str, expression: sqlglot.expressions.Expression, flat: bool = False) -> str:
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}"
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')}"
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}"
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}"
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 )
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)
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})"
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"
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)
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}"
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 ""
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}"
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)
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}"
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)
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)))
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))
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)
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)
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)
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}"
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)