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, name_sequence, 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.AllowedValuesProperty: lambda self, 78 e: f"ALLOWED_VALUES {self.expressions(e, flat=True)}", 79 exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}", 80 exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}", 81 exp.CaseSpecificColumnConstraint: lambda _, 82 e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC", 83 exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}", 84 exp.CharacterSetProperty: lambda self, 85 e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}", 86 exp.ClusteredColumnConstraint: lambda self, 87 e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})", 88 exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}", 89 exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}", 90 exp.CopyGrantsProperty: lambda *_: "COPY GRANTS", 91 exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}", 92 exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}", 93 exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}", 94 exp.EphemeralColumnConstraint: lambda self, 95 e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}", 96 exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}", 97 exp.ExecuteAsProperty: lambda self, e: self.naked_property(e), 98 exp.ExternalProperty: lambda *_: "EXTERNAL", 99 exp.GlobalProperty: lambda *_: "GLOBAL", 100 exp.HeapProperty: lambda *_: "HEAP", 101 exp.IcebergProperty: lambda *_: "ICEBERG", 102 exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})", 103 exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}", 104 exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}", 105 exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}", 106 exp.JSONExtract: lambda self, e: self.func( 107 "JSON_EXTRACT", e.this, e.expression, *e.expressions 108 ), 109 exp.JSONExtractScalar: lambda self, e: self.func( 110 "JSON_EXTRACT_SCALAR", e.this, e.expression, *e.expressions 111 ), 112 exp.LanguageProperty: lambda self, e: self.naked_property(e), 113 exp.LocationProperty: lambda self, e: self.naked_property(e), 114 exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG", 115 exp.MaterializedProperty: lambda *_: "MATERIALIZED", 116 exp.NonClusteredColumnConstraint: lambda self, 117 e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})", 118 exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX", 119 exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION", 120 exp.OnCommitProperty: lambda _, 121 e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS", 122 exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}", 123 exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}", 124 exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}", 125 exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}", 126 exp.RemoteWithConnectionModelProperty: lambda self, 127 e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}", 128 exp.ReturnsProperty: lambda self, e: self.naked_property(e), 129 exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}", 130 exp.SetConfigProperty: lambda self, e: self.sql(e, "this"), 131 exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET", 132 exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}", 133 exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}", 134 exp.SqlReadWriteProperty: lambda _, e: e.name, 135 exp.SqlSecurityProperty: lambda _, 136 e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}", 137 exp.StabilityProperty: lambda _, e: e.name, 138 exp.TemporaryProperty: lambda *_: "TEMPORARY", 139 exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}", 140 exp.Timestamp: lambda self, e: self.func("TIMESTAMP", e.this, e.expression), 141 exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}", 142 exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}", 143 exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions), 144 exp.TransientProperty: lambda *_: "TRANSIENT", 145 exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE", 146 exp.UnloggedProperty: lambda *_: "UNLOGGED", 147 exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]), 148 exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}", 149 exp.VolatileProperty: lambda *_: "VOLATILE", 150 exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}", 151 exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}", 152 } 153 154 # Whether null ordering is supported in order by 155 # True: Full Support, None: No support, False: No support in window specifications 156 NULL_ORDERING_SUPPORTED: t.Optional[bool] = True 157 158 # Whether ignore nulls is inside the agg or outside. 159 # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER 160 IGNORE_NULLS_IN_FUNC = False 161 162 # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported 163 LOCKING_READS_SUPPORTED = False 164 165 # Always do union distinct or union all 166 EXPLICIT_UNION = False 167 168 # Wrap derived values in parens, usually standard but spark doesn't support it 169 WRAP_DERIVED_VALUES = True 170 171 # Whether create function uses an AS before the RETURN 172 CREATE_FUNCTION_RETURN_AS = True 173 174 # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed 175 MATCHED_BY_SOURCE = True 176 177 # Whether the INTERVAL expression works only with values like '1 day' 178 SINGLE_STRING_INTERVAL = False 179 180 # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs 181 INTERVAL_ALLOWS_PLURAL_FORM = True 182 183 # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH") 184 LIMIT_FETCH = "ALL" 185 186 # Whether limit and fetch allows expresions or just limits 187 LIMIT_ONLY_LITERALS = False 188 189 # Whether a table is allowed to be renamed with a db 190 RENAME_TABLE_WITH_DB = True 191 192 # The separator for grouping sets and rollups 193 GROUPINGS_SEP = "," 194 195 # The string used for creating an index on a table 196 INDEX_ON = "ON" 197 198 # Whether join hints should be generated 199 JOIN_HINTS = True 200 201 # Whether table hints should be generated 202 TABLE_HINTS = True 203 204 # Whether query hints should be generated 205 QUERY_HINTS = True 206 207 # What kind of separator to use for query hints 208 QUERY_HINT_SEP = ", " 209 210 # Whether comparing against booleans (e.g. x IS TRUE) is supported 211 IS_BOOL_ALLOWED = True 212 213 # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement 214 DUPLICATE_KEY_UPDATE_WITH_SET = True 215 216 # Whether to generate the limit as TOP <value> instead of LIMIT <value> 217 LIMIT_IS_TOP = False 218 219 # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ... 220 RETURNING_END = True 221 222 # Whether to generate the (+) suffix for columns used in old-style join conditions 223 COLUMN_JOIN_MARKS_SUPPORTED = False 224 225 # Whether to generate an unquoted value for EXTRACT's date part argument 226 EXTRACT_ALLOWS_QUOTES = True 227 228 # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax 229 TZ_TO_WITH_TIME_ZONE = False 230 231 # Whether the NVL2 function is supported 232 NVL2_SUPPORTED = True 233 234 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax 235 SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE") 236 237 # Whether VALUES statements can be used as derived tables. 238 # MySQL 5 and Redshift do not allow this, so when False, it will convert 239 # SELECT * VALUES into SELECT UNION 240 VALUES_AS_TABLE = True 241 242 # Whether the word COLUMN is included when adding a column with ALTER TABLE 243 ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True 244 245 # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery) 246 UNNEST_WITH_ORDINALITY = True 247 248 # Whether FILTER (WHERE cond) can be used for conditional aggregation 249 AGGREGATE_FILTER_SUPPORTED = True 250 251 # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds 252 SEMI_ANTI_JOIN_WITH_SIDE = True 253 254 # Whether to include the type of a computed column in the CREATE DDL 255 COMPUTED_COLUMN_WITH_TYPE = True 256 257 # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY 258 SUPPORTS_TABLE_COPY = True 259 260 # Whether parentheses are required around the table sample's expression 261 TABLESAMPLE_REQUIRES_PARENS = True 262 263 # Whether a table sample clause's size needs to be followed by the ROWS keyword 264 TABLESAMPLE_SIZE_IS_ROWS = True 265 266 # The keyword(s) to use when generating a sample clause 267 TABLESAMPLE_KEYWORDS = "TABLESAMPLE" 268 269 # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI 270 TABLESAMPLE_WITH_METHOD = True 271 272 # The keyword to use when specifying the seed of a sample clause 273 TABLESAMPLE_SEED_KEYWORD = "SEED" 274 275 # Whether COLLATE is a function instead of a binary operator 276 COLLATE_IS_FUNC = False 277 278 # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle) 279 DATA_TYPE_SPECIFIERS_ALLOWED = False 280 281 # Whether conditions require booleans WHERE x = 0 vs WHERE x 282 ENSURE_BOOLS = False 283 284 # Whether the "RECURSIVE" keyword is required when defining recursive CTEs 285 CTE_RECURSIVE_KEYWORD_REQUIRED = True 286 287 # Whether CONCAT requires >1 arguments 288 SUPPORTS_SINGLE_ARG_CONCAT = True 289 290 # Whether LAST_DAY function supports a date part argument 291 LAST_DAY_SUPPORTS_DATE_PART = True 292 293 # Whether named columns are allowed in table aliases 294 SUPPORTS_TABLE_ALIAS_COLUMNS = True 295 296 # Whether UNPIVOT aliases are Identifiers (False means they're Literals) 297 UNPIVOT_ALIASES_ARE_IDENTIFIERS = True 298 299 # What delimiter to use for separating JSON key/value pairs 300 JSON_KEY_VALUE_PAIR_SEP = ":" 301 302 # INSERT OVERWRITE TABLE x override 303 INSERT_OVERWRITE = " OVERWRITE TABLE" 304 305 # Whether the SELECT .. INTO syntax is used instead of CTAS 306 SUPPORTS_SELECT_INTO = False 307 308 # Whether UNLOGGED tables can be created 309 SUPPORTS_UNLOGGED_TABLES = False 310 311 # Whether the CREATE TABLE LIKE statement is supported 312 SUPPORTS_CREATE_TABLE_LIKE = True 313 314 # Whether the LikeProperty needs to be specified inside of the schema clause 315 LIKE_PROPERTY_INSIDE_SCHEMA = False 316 317 # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be 318 # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args 319 MULTI_ARG_DISTINCT = True 320 321 # Whether the JSON extraction operators expect a value of type JSON 322 JSON_TYPE_REQUIRED_FOR_EXTRACTION = False 323 324 # Whether bracketed keys like ["foo"] are supported in JSON paths 325 JSON_PATH_BRACKETED_KEY_SUPPORTED = True 326 327 # Whether to escape keys using single quotes in JSON paths 328 JSON_PATH_SINGLE_QUOTE_ESCAPE = False 329 330 # The JSONPathPart expressions supported by this dialect 331 SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy() 332 333 # Whether any(f(x) for x in array) can be implemented by this dialect 334 CAN_IMPLEMENT_ARRAY_ANY = False 335 336 # Whether the function TO_NUMBER is supported 337 SUPPORTS_TO_NUMBER = True 338 339 # Whether or not union modifiers apply to the outer union or select. 340 # SELECT * FROM x UNION SELECT * FROM y LIMIT 1 341 # True means limit 1 happens after the union, False means it it happens on y. 342 OUTER_UNION_MODIFIERS = True 343 344 # Whether parameters from COPY statement are wrapped in parentheses 345 COPY_PARAMS_ARE_WRAPPED = True 346 347 # Whether values of params are set with "=" token or empty space 348 COPY_PARAMS_EQ_REQUIRED = False 349 350 # Whether COPY statement has INTO keyword 351 COPY_HAS_INTO_KEYWORD = True 352 353 # Whether the conditional TRY(expression) function is supported 354 TRY_SUPPORTED = True 355 356 # The keyword to use when generating a star projection with excluded columns 357 STAR_EXCEPT = "EXCEPT" 358 359 # The HEX function name 360 HEX_FUNC = "HEX" 361 362 TYPE_MAPPING = { 363 exp.DataType.Type.NCHAR: "CHAR", 364 exp.DataType.Type.NVARCHAR: "VARCHAR", 365 exp.DataType.Type.MEDIUMTEXT: "TEXT", 366 exp.DataType.Type.LONGTEXT: "TEXT", 367 exp.DataType.Type.TINYTEXT: "TEXT", 368 exp.DataType.Type.MEDIUMBLOB: "BLOB", 369 exp.DataType.Type.LONGBLOB: "BLOB", 370 exp.DataType.Type.TINYBLOB: "BLOB", 371 exp.DataType.Type.INET: "INET", 372 exp.DataType.Type.ROWVERSION: "VARBINARY", 373 } 374 375 TIME_PART_SINGULARS = { 376 "MICROSECONDS": "MICROSECOND", 377 "SECONDS": "SECOND", 378 "MINUTES": "MINUTE", 379 "HOURS": "HOUR", 380 "DAYS": "DAY", 381 "WEEKS": "WEEK", 382 "MONTHS": "MONTH", 383 "QUARTERS": "QUARTER", 384 "YEARS": "YEAR", 385 } 386 387 AFTER_HAVING_MODIFIER_TRANSFORMS = { 388 "cluster": lambda self, e: self.sql(e, "cluster"), 389 "distribute": lambda self, e: self.sql(e, "distribute"), 390 "qualify": lambda self, e: self.sql(e, "qualify"), 391 "sort": lambda self, e: self.sql(e, "sort"), 392 "windows": lambda self, e: ( 393 self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True) 394 if e.args.get("windows") 395 else "" 396 ), 397 } 398 399 TOKEN_MAPPING: t.Dict[TokenType, str] = {} 400 401 STRUCT_DELIMITER = ("<", ">") 402 403 PARAMETER_TOKEN = "@" 404 NAMED_PLACEHOLDER_TOKEN = ":" 405 406 PROPERTIES_LOCATION = { 407 exp.AllowedValuesProperty: exp.Properties.Location.POST_SCHEMA, 408 exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE, 409 exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA, 410 exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA, 411 exp.BackupProperty: exp.Properties.Location.POST_SCHEMA, 412 exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME, 413 exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA, 414 exp.ChecksumProperty: exp.Properties.Location.POST_NAME, 415 exp.CollateProperty: exp.Properties.Location.POST_SCHEMA, 416 exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA, 417 exp.Cluster: exp.Properties.Location.POST_SCHEMA, 418 exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA, 419 exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME, 420 exp.DefinerProperty: exp.Properties.Location.POST_CREATE, 421 exp.DictRange: exp.Properties.Location.POST_SCHEMA, 422 exp.DictProperty: exp.Properties.Location.POST_SCHEMA, 423 exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA, 424 exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA, 425 exp.EngineProperty: exp.Properties.Location.POST_SCHEMA, 426 exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA, 427 exp.ExternalProperty: exp.Properties.Location.POST_CREATE, 428 exp.FallbackProperty: exp.Properties.Location.POST_NAME, 429 exp.FileFormatProperty: exp.Properties.Location.POST_WITH, 430 exp.FreespaceProperty: exp.Properties.Location.POST_NAME, 431 exp.GlobalProperty: exp.Properties.Location.POST_CREATE, 432 exp.HeapProperty: exp.Properties.Location.POST_WITH, 433 exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA, 434 exp.IcebergProperty: exp.Properties.Location.POST_CREATE, 435 exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA, 436 exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME, 437 exp.JournalProperty: exp.Properties.Location.POST_NAME, 438 exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA, 439 exp.LikeProperty: exp.Properties.Location.POST_SCHEMA, 440 exp.LocationProperty: exp.Properties.Location.POST_SCHEMA, 441 exp.LockProperty: exp.Properties.Location.POST_SCHEMA, 442 exp.LockingProperty: exp.Properties.Location.POST_ALIAS, 443 exp.LogProperty: exp.Properties.Location.POST_NAME, 444 exp.MaterializedProperty: exp.Properties.Location.POST_CREATE, 445 exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME, 446 exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION, 447 exp.OnProperty: exp.Properties.Location.POST_SCHEMA, 448 exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION, 449 exp.Order: exp.Properties.Location.POST_SCHEMA, 450 exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA, 451 exp.PartitionedByProperty: exp.Properties.Location.POST_WITH, 452 exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA, 453 exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA, 454 exp.Property: exp.Properties.Location.POST_WITH, 455 exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA, 456 exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA, 457 exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA, 458 exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA, 459 exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA, 460 exp.SampleProperty: exp.Properties.Location.POST_SCHEMA, 461 exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA, 462 exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA, 463 exp.Set: exp.Properties.Location.POST_SCHEMA, 464 exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA, 465 exp.SetProperty: exp.Properties.Location.POST_CREATE, 466 exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA, 467 exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION, 468 exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION, 469 exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA, 470 exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA, 471 exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE, 472 exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA, 473 exp.TemporaryProperty: exp.Properties.Location.POST_CREATE, 474 exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA, 475 exp.TransientProperty: exp.Properties.Location.POST_CREATE, 476 exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA, 477 exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA, 478 exp.UnloggedProperty: exp.Properties.Location.POST_CREATE, 479 exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA, 480 exp.VolatileProperty: exp.Properties.Location.POST_CREATE, 481 exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION, 482 exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME, 483 exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA, 484 } 485 486 # Keywords that can't be used as unquoted identifier names 487 RESERVED_KEYWORDS: t.Set[str] = set() 488 489 # Expressions whose comments are separated from them for better formatting 490 WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 491 exp.Create, 492 exp.Delete, 493 exp.Drop, 494 exp.From, 495 exp.Insert, 496 exp.Join, 497 exp.Select, 498 exp.Union, 499 exp.Update, 500 exp.Where, 501 exp.With, 502 ) 503 504 # Expressions that should not have their comments generated in maybe_comment 505 EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 506 exp.Binary, 507 exp.Union, 508 ) 509 510 # Expressions that can remain unwrapped when appearing in the context of an INTERVAL 511 UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = ( 512 exp.Column, 513 exp.Literal, 514 exp.Neg, 515 exp.Paren, 516 ) 517 518 PARAMETERIZABLE_TEXT_TYPES = { 519 exp.DataType.Type.NVARCHAR, 520 exp.DataType.Type.VARCHAR, 521 exp.DataType.Type.CHAR, 522 exp.DataType.Type.NCHAR, 523 } 524 525 # Expressions that need to have all CTEs under them bubbled up to them 526 EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set() 527 528 SENTINEL_LINE_BREAK = "__SQLGLOT__LB__" 529 530 __slots__ = ( 531 "pretty", 532 "identify", 533 "normalize", 534 "pad", 535 "_indent", 536 "normalize_functions", 537 "unsupported_level", 538 "max_unsupported", 539 "leading_comma", 540 "max_text_width", 541 "comments", 542 "dialect", 543 "unsupported_messages", 544 "_escaped_quote_end", 545 "_escaped_identifier_end", 546 "_next_name", 547 ) 548 549 def __init__( 550 self, 551 pretty: t.Optional[bool] = None, 552 identify: str | bool = False, 553 normalize: bool = False, 554 pad: int = 2, 555 indent: int = 2, 556 normalize_functions: t.Optional[str | bool] = None, 557 unsupported_level: ErrorLevel = ErrorLevel.WARN, 558 max_unsupported: int = 3, 559 leading_comma: bool = False, 560 max_text_width: int = 80, 561 comments: bool = True, 562 dialect: DialectType = None, 563 ): 564 import sqlglot 565 from sqlglot.dialects import Dialect 566 567 self.pretty = pretty if pretty is not None else sqlglot.pretty 568 self.identify = identify 569 self.normalize = normalize 570 self.pad = pad 571 self._indent = indent 572 self.unsupported_level = unsupported_level 573 self.max_unsupported = max_unsupported 574 self.leading_comma = leading_comma 575 self.max_text_width = max_text_width 576 self.comments = comments 577 self.dialect = Dialect.get_or_raise(dialect) 578 579 # This is both a Dialect property and a Generator argument, so we prioritize the latter 580 self.normalize_functions = ( 581 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 582 ) 583 584 self.unsupported_messages: t.List[str] = [] 585 self._escaped_quote_end: str = ( 586 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 587 ) 588 self._escaped_identifier_end: str = ( 589 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 590 ) 591 592 self._next_name = name_sequence("_t") 593 594 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 595 """ 596 Generates the SQL string corresponding to the given syntax tree. 597 598 Args: 599 expression: The syntax tree. 600 copy: Whether to copy the expression. The generator performs mutations so 601 it is safer to copy. 602 603 Returns: 604 The SQL string corresponding to `expression`. 605 """ 606 if copy: 607 expression = expression.copy() 608 609 expression = self.preprocess(expression) 610 611 self.unsupported_messages = [] 612 sql = self.sql(expression).strip() 613 614 if self.pretty: 615 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 616 617 if self.unsupported_level == ErrorLevel.IGNORE: 618 return sql 619 620 if self.unsupported_level == ErrorLevel.WARN: 621 for msg in self.unsupported_messages: 622 logger.warning(msg) 623 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 624 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 625 626 return sql 627 628 def preprocess(self, expression: exp.Expression) -> exp.Expression: 629 """Apply generic preprocessing transformations to a given expression.""" 630 if ( 631 not expression.parent 632 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 633 and any(node.parent is not expression for node in expression.find_all(exp.With)) 634 ): 635 from sqlglot.transforms import move_ctes_to_top_level 636 637 expression = move_ctes_to_top_level(expression) 638 639 if self.ENSURE_BOOLS: 640 from sqlglot.transforms import ensure_bools 641 642 expression = ensure_bools(expression) 643 644 return expression 645 646 def unsupported(self, message: str) -> None: 647 if self.unsupported_level == ErrorLevel.IMMEDIATE: 648 raise UnsupportedError(message) 649 self.unsupported_messages.append(message) 650 651 def sep(self, sep: str = " ") -> str: 652 return f"{sep.strip()}\n" if self.pretty else sep 653 654 def seg(self, sql: str, sep: str = " ") -> str: 655 return f"{self.sep(sep)}{sql}" 656 657 def pad_comment(self, comment: str) -> str: 658 comment = " " + comment if comment[0].strip() else comment 659 comment = comment + " " if comment[-1].strip() else comment 660 return comment 661 662 def maybe_comment( 663 self, 664 sql: str, 665 expression: t.Optional[exp.Expression] = None, 666 comments: t.Optional[t.List[str]] = None, 667 separated: bool = False, 668 ) -> str: 669 comments = ( 670 ((expression and expression.comments) if comments is None else comments) # type: ignore 671 if self.comments 672 else None 673 ) 674 675 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 676 return sql 677 678 comments_sql = " ".join( 679 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 680 ) 681 682 if not comments_sql: 683 return sql 684 685 comments_sql = self._replace_line_breaks(comments_sql) 686 687 if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS): 688 return ( 689 f"{self.sep()}{comments_sql}{sql}" 690 if not sql or sql[0].isspace() 691 else f"{comments_sql}{self.sep()}{sql}" 692 ) 693 694 return f"{sql} {comments_sql}" 695 696 def wrap(self, expression: exp.Expression | str) -> str: 697 this_sql = ( 698 self.sql(expression) 699 if isinstance(expression, exp.UNWRAPPED_QUERIES) 700 else self.sql(expression, "this") 701 ) 702 if not this_sql: 703 return "()" 704 705 this_sql = self.indent(this_sql, level=1, pad=0) 706 return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}" 707 708 def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str: 709 original = self.identify 710 self.identify = False 711 result = func(*args, **kwargs) 712 self.identify = original 713 return result 714 715 def normalize_func(self, name: str) -> str: 716 if self.normalize_functions == "upper" or self.normalize_functions is True: 717 return name.upper() 718 if self.normalize_functions == "lower": 719 return name.lower() 720 return name 721 722 def indent( 723 self, 724 sql: str, 725 level: int = 0, 726 pad: t.Optional[int] = None, 727 skip_first: bool = False, 728 skip_last: bool = False, 729 ) -> str: 730 if not self.pretty or not sql: 731 return sql 732 733 pad = self.pad if pad is None else pad 734 lines = sql.split("\n") 735 736 return "\n".join( 737 ( 738 line 739 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 740 else f"{' ' * (level * self._indent + pad)}{line}" 741 ) 742 for i, line in enumerate(lines) 743 ) 744 745 def sql( 746 self, 747 expression: t.Optional[str | exp.Expression], 748 key: t.Optional[str] = None, 749 comment: bool = True, 750 ) -> str: 751 if not expression: 752 return "" 753 754 if isinstance(expression, str): 755 return expression 756 757 if key: 758 value = expression.args.get(key) 759 if value: 760 return self.sql(value) 761 return "" 762 763 transform = self.TRANSFORMS.get(expression.__class__) 764 765 if callable(transform): 766 sql = transform(self, expression) 767 elif isinstance(expression, exp.Expression): 768 exp_handler_name = f"{expression.key}_sql" 769 770 if hasattr(self, exp_handler_name): 771 sql = getattr(self, exp_handler_name)(expression) 772 elif isinstance(expression, exp.Func): 773 sql = self.function_fallback_sql(expression) 774 elif isinstance(expression, exp.Property): 775 sql = self.property_sql(expression) 776 else: 777 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 778 else: 779 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 780 781 return self.maybe_comment(sql, expression) if self.comments and comment else sql 782 783 def uncache_sql(self, expression: exp.Uncache) -> str: 784 table = self.sql(expression, "this") 785 exists_sql = " IF EXISTS" if expression.args.get("exists") else "" 786 return f"UNCACHE TABLE{exists_sql} {table}" 787 788 def cache_sql(self, expression: exp.Cache) -> str: 789 lazy = " LAZY" if expression.args.get("lazy") else "" 790 table = self.sql(expression, "this") 791 options = expression.args.get("options") 792 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 793 sql = self.sql(expression, "expression") 794 sql = f" AS{self.sep()}{sql}" if sql else "" 795 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 796 return self.prepend_ctes(expression, sql) 797 798 def characterset_sql(self, expression: exp.CharacterSet) -> str: 799 if isinstance(expression.parent, exp.Cast): 800 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 801 default = "DEFAULT " if expression.args.get("default") else "" 802 return f"{default}CHARACTER SET={self.sql(expression, 'this')}" 803 804 def column_parts(self, expression: exp.Column) -> str: 805 return ".".join( 806 self.sql(part) 807 for part in ( 808 expression.args.get("catalog"), 809 expression.args.get("db"), 810 expression.args.get("table"), 811 expression.args.get("this"), 812 ) 813 if part 814 ) 815 816 def column_sql(self, expression: exp.Column) -> str: 817 join_mark = " (+)" if expression.args.get("join_mark") else "" 818 819 if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED: 820 join_mark = "" 821 self.unsupported("Outer join syntax using the (+) operator is not supported.") 822 823 return f"{self.column_parts(expression)}{join_mark}" 824 825 def columnposition_sql(self, expression: exp.ColumnPosition) -> str: 826 this = self.sql(expression, "this") 827 this = f" {this}" if this else "" 828 position = self.sql(expression, "position") 829 return f"{position}{this}" 830 831 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 832 column = self.sql(expression, "this") 833 kind = self.sql(expression, "kind") 834 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 835 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 836 kind = f"{sep}{kind}" if kind else "" 837 constraints = f" {constraints}" if constraints else "" 838 position = self.sql(expression, "position") 839 position = f" {position}" if position else "" 840 841 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 842 kind = "" 843 844 return f"{exists}{column}{kind}{constraints}{position}" 845 846 def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str: 847 this = self.sql(expression, "this") 848 kind_sql = self.sql(expression, "kind").strip() 849 return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql 850 851 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 852 this = self.sql(expression, "this") 853 if expression.args.get("not_null"): 854 persisted = " PERSISTED NOT NULL" 855 elif expression.args.get("persisted"): 856 persisted = " PERSISTED" 857 else: 858 persisted = "" 859 return f"AS {this}{persisted}" 860 861 def autoincrementcolumnconstraint_sql(self, _) -> str: 862 return self.token_sql(TokenType.AUTO_INCREMENT) 863 864 def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str: 865 if isinstance(expression.this, list): 866 this = self.wrap(self.expressions(expression, key="this", flat=True)) 867 else: 868 this = self.sql(expression, "this") 869 870 return f"COMPRESS {this}" 871 872 def generatedasidentitycolumnconstraint_sql( 873 self, expression: exp.GeneratedAsIdentityColumnConstraint 874 ) -> str: 875 this = "" 876 if expression.this is not None: 877 on_null = " ON NULL" if expression.args.get("on_null") else "" 878 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 879 880 start = expression.args.get("start") 881 start = f"START WITH {start}" if start else "" 882 increment = expression.args.get("increment") 883 increment = f" INCREMENT BY {increment}" if increment else "" 884 minvalue = expression.args.get("minvalue") 885 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 886 maxvalue = expression.args.get("maxvalue") 887 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 888 cycle = expression.args.get("cycle") 889 cycle_sql = "" 890 891 if cycle is not None: 892 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 893 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 894 895 sequence_opts = "" 896 if start or increment or cycle_sql: 897 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 898 sequence_opts = f" ({sequence_opts.strip()})" 899 900 expr = self.sql(expression, "expression") 901 expr = f"({expr})" if expr else "IDENTITY" 902 903 return f"GENERATED{this} AS {expr}{sequence_opts}" 904 905 def generatedasrowcolumnconstraint_sql( 906 self, expression: exp.GeneratedAsRowColumnConstraint 907 ) -> str: 908 start = "START" if expression.args.get("start") else "END" 909 hidden = " HIDDEN" if expression.args.get("hidden") else "" 910 return f"GENERATED ALWAYS AS ROW {start}{hidden}" 911 912 def periodforsystemtimeconstraint_sql( 913 self, expression: exp.PeriodForSystemTimeConstraint 914 ) -> str: 915 return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})" 916 917 def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str: 918 return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL" 919 920 def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str: 921 return f"AS {self.sql(expression, 'this')}" 922 923 def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str: 924 desc = expression.args.get("desc") 925 if desc is not None: 926 return f"PRIMARY KEY{' DESC' if desc else ' ASC'}" 927 return "PRIMARY KEY" 928 929 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 930 this = self.sql(expression, "this") 931 this = f" {this}" if this else "" 932 index_type = expression.args.get("index_type") 933 index_type = f" USING {index_type}" if index_type else "" 934 on_conflict = self.sql(expression, "on_conflict") 935 on_conflict = f" {on_conflict}" if on_conflict else "" 936 return f"UNIQUE{this}{index_type}{on_conflict}" 937 938 def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str: 939 return self.sql(expression, "this") 940 941 def create_sql(self, expression: exp.Create) -> str: 942 kind = self.sql(expression, "kind") 943 properties = expression.args.get("properties") 944 properties_locs = self.locate_properties(properties) if properties else defaultdict() 945 946 this = self.createable_sql(expression, properties_locs) 947 948 properties_sql = "" 949 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 950 exp.Properties.Location.POST_WITH 951 ): 952 properties_sql = self.sql( 953 exp.Properties( 954 expressions=[ 955 *properties_locs[exp.Properties.Location.POST_SCHEMA], 956 *properties_locs[exp.Properties.Location.POST_WITH], 957 ] 958 ) 959 ) 960 961 begin = " BEGIN" if expression.args.get("begin") else "" 962 end = " END" if expression.args.get("end") else "" 963 964 expression_sql = self.sql(expression, "expression") 965 if expression_sql: 966 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 967 968 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 969 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 970 postalias_props_sql = self.properties( 971 exp.Properties( 972 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 973 ), 974 wrapped=False, 975 ) 976 expression_sql = f" AS {postalias_props_sql}{expression_sql}" 977 else: 978 expression_sql = f" AS{expression_sql}" 979 980 postindex_props_sql = "" 981 if properties_locs.get(exp.Properties.Location.POST_INDEX): 982 postindex_props_sql = self.properties( 983 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 984 wrapped=False, 985 prefix=" ", 986 ) 987 988 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 989 indexes = f" {indexes}" if indexes else "" 990 index_sql = indexes + postindex_props_sql 991 992 replace = " OR REPLACE" if expression.args.get("replace") else "" 993 unique = " UNIQUE" if expression.args.get("unique") else "" 994 995 postcreate_props_sql = "" 996 if properties_locs.get(exp.Properties.Location.POST_CREATE): 997 postcreate_props_sql = self.properties( 998 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 999 sep=" ", 1000 prefix=" ", 1001 wrapped=False, 1002 ) 1003 1004 modifiers = "".join((replace, unique, postcreate_props_sql)) 1005 1006 postexpression_props_sql = "" 1007 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 1008 postexpression_props_sql = self.properties( 1009 exp.Properties( 1010 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 1011 ), 1012 sep=" ", 1013 prefix=" ", 1014 wrapped=False, 1015 ) 1016 1017 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 1018 no_schema_binding = ( 1019 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 1020 ) 1021 1022 clone = self.sql(expression, "clone") 1023 clone = f" {clone}" if clone else "" 1024 1025 expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 1026 return self.prepend_ctes(expression, expression_sql) 1027 1028 def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str: 1029 start = self.sql(expression, "start") 1030 start = f"START WITH {start}" if start else "" 1031 increment = self.sql(expression, "increment") 1032 increment = f" INCREMENT BY {increment}" if increment else "" 1033 minvalue = self.sql(expression, "minvalue") 1034 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 1035 maxvalue = self.sql(expression, "maxvalue") 1036 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 1037 owned = self.sql(expression, "owned") 1038 owned = f" OWNED BY {owned}" if owned else "" 1039 1040 cache = expression.args.get("cache") 1041 if cache is None: 1042 cache_str = "" 1043 elif cache is True: 1044 cache_str = " CACHE" 1045 else: 1046 cache_str = f" CACHE {cache}" 1047 1048 options = self.expressions(expression, key="options", flat=True, sep=" ") 1049 options = f" {options}" if options else "" 1050 1051 return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip() 1052 1053 def clone_sql(self, expression: exp.Clone) -> str: 1054 this = self.sql(expression, "this") 1055 shallow = "SHALLOW " if expression.args.get("shallow") else "" 1056 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 1057 return f"{shallow}{keyword} {this}" 1058 1059 def describe_sql(self, expression: exp.Describe) -> str: 1060 style = expression.args.get("style") 1061 style = f" {style}" if style else "" 1062 return f"DESCRIBE{style} {self.sql(expression, 'this')}" 1063 1064 def heredoc_sql(self, expression: exp.Heredoc) -> str: 1065 tag = self.sql(expression, "tag") 1066 return f"${tag}${self.sql(expression, 'this')}${tag}$" 1067 1068 def prepend_ctes(self, expression: exp.Expression, sql: str) -> str: 1069 with_ = self.sql(expression, "with") 1070 if with_: 1071 sql = f"{with_}{self.sep()}{sql}" 1072 return sql 1073 1074 def with_sql(self, expression: exp.With) -> str: 1075 sql = self.expressions(expression, flat=True) 1076 recursive = ( 1077 "RECURSIVE " 1078 if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive") 1079 else "" 1080 ) 1081 1082 return f"WITH {recursive}{sql}" 1083 1084 def cte_sql(self, expression: exp.CTE) -> str: 1085 alias = self.sql(expression, "alias") 1086 1087 materialized = expression.args.get("materialized") 1088 if materialized is False: 1089 materialized = "NOT MATERIALIZED " 1090 elif materialized: 1091 materialized = "MATERIALIZED " 1092 1093 return f"{alias} AS {materialized or ''}{self.wrap(expression)}" 1094 1095 def tablealias_sql(self, expression: exp.TableAlias) -> str: 1096 alias = self.sql(expression, "this") 1097 columns = self.expressions(expression, key="columns", flat=True) 1098 columns = f"({columns})" if columns else "" 1099 1100 if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS: 1101 columns = "" 1102 self.unsupported("Named columns are not supported in table alias.") 1103 1104 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 1105 alias = self._next_name() 1106 1107 return f"{alias}{columns}" 1108 1109 def bitstring_sql(self, expression: exp.BitString) -> str: 1110 this = self.sql(expression, "this") 1111 if self.dialect.BIT_START: 1112 return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}" 1113 return f"{int(this, 2)}" 1114 1115 def hexstring_sql(self, expression: exp.HexString) -> str: 1116 this = self.sql(expression, "this") 1117 if self.dialect.HEX_START: 1118 return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}" 1119 return f"{int(this, 16)}" 1120 1121 def bytestring_sql(self, expression: exp.ByteString) -> str: 1122 this = self.sql(expression, "this") 1123 if self.dialect.BYTE_START: 1124 return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}" 1125 return this 1126 1127 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 1128 this = self.sql(expression, "this") 1129 escape = expression.args.get("escape") 1130 1131 if self.dialect.UNICODE_START: 1132 escape = f" UESCAPE {self.sql(escape)}" if escape else "" 1133 return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}" 1134 1135 if escape: 1136 pattern = re.compile(rf"{escape.name}(\d+)") 1137 else: 1138 pattern = ESCAPED_UNICODE_RE 1139 1140 this = pattern.sub(r"\\u\1", this) 1141 return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}" 1142 1143 def rawstring_sql(self, expression: exp.RawString) -> str: 1144 string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False) 1145 return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}" 1146 1147 def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str: 1148 this = self.sql(expression, "this") 1149 specifier = self.sql(expression, "expression") 1150 specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else "" 1151 return f"{this}{specifier}" 1152 1153 def datatype_sql(self, expression: exp.DataType) -> str: 1154 type_value = expression.this 1155 1156 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 1157 type_sql = self.sql(expression, "kind") 1158 else: 1159 type_sql = ( 1160 self.TYPE_MAPPING.get(type_value, type_value.value) 1161 if isinstance(type_value, exp.DataType.Type) 1162 else type_value 1163 ) 1164 1165 nested = "" 1166 interior = self.expressions(expression, flat=True) 1167 values = "" 1168 1169 if interior: 1170 if expression.args.get("nested"): 1171 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 1172 if expression.args.get("values") is not None: 1173 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 1174 values = self.expressions(expression, key="values", flat=True) 1175 values = f"{delimiters[0]}{values}{delimiters[1]}" 1176 elif type_value == exp.DataType.Type.INTERVAL: 1177 nested = f" {interior}" 1178 else: 1179 nested = f"({interior})" 1180 1181 type_sql = f"{type_sql}{nested}{values}" 1182 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 1183 exp.DataType.Type.TIMETZ, 1184 exp.DataType.Type.TIMESTAMPTZ, 1185 ): 1186 type_sql = f"{type_sql} WITH TIME ZONE" 1187 1188 return type_sql 1189 1190 def directory_sql(self, expression: exp.Directory) -> str: 1191 local = "LOCAL " if expression.args.get("local") else "" 1192 row_format = self.sql(expression, "row_format") 1193 row_format = f" {row_format}" if row_format else "" 1194 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}" 1195 1196 def delete_sql(self, expression: exp.Delete) -> str: 1197 this = self.sql(expression, "this") 1198 this = f" FROM {this}" if this else "" 1199 using = self.sql(expression, "using") 1200 using = f" USING {using}" if using else "" 1201 where = self.sql(expression, "where") 1202 returning = self.sql(expression, "returning") 1203 limit = self.sql(expression, "limit") 1204 tables = self.expressions(expression, key="tables") 1205 tables = f" {tables}" if tables else "" 1206 if self.RETURNING_END: 1207 expression_sql = f"{this}{using}{where}{returning}{limit}" 1208 else: 1209 expression_sql = f"{returning}{this}{using}{where}{limit}" 1210 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}") 1211 1212 def drop_sql(self, expression: exp.Drop) -> str: 1213 this = self.sql(expression, "this") 1214 expressions = self.expressions(expression, flat=True) 1215 expressions = f" ({expressions})" if expressions else "" 1216 kind = expression.args["kind"] 1217 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1218 on_cluster = self.sql(expression, "cluster") 1219 on_cluster = f" {on_cluster}" if on_cluster else "" 1220 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1221 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1222 cascade = " CASCADE" if expression.args.get("cascade") else "" 1223 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1224 purge = " PURGE" if expression.args.get("purge") else "" 1225 return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}" 1226 1227 def except_sql(self, expression: exp.Except) -> str: 1228 return self.set_operations(expression) 1229 1230 def except_op(self, expression: exp.Except) -> str: 1231 return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}" 1232 1233 def fetch_sql(self, expression: exp.Fetch) -> str: 1234 direction = expression.args.get("direction") 1235 direction = f" {direction}" if direction else "" 1236 count = expression.args.get("count") 1237 count = f" {count}" if count else "" 1238 if expression.args.get("percent"): 1239 count = f"{count} PERCENT" 1240 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1241 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}" 1242 1243 def filter_sql(self, expression: exp.Filter) -> str: 1244 if self.AGGREGATE_FILTER_SUPPORTED: 1245 this = self.sql(expression, "this") 1246 where = self.sql(expression, "expression").strip() 1247 return f"{this} FILTER({where})" 1248 1249 agg = expression.this 1250 agg_arg = agg.this 1251 cond = expression.expression.this 1252 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1253 return self.sql(agg) 1254 1255 def hint_sql(self, expression: exp.Hint) -> str: 1256 if not self.QUERY_HINTS: 1257 self.unsupported("Hints are not supported") 1258 return "" 1259 1260 return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */" 1261 1262 def indexparameters_sql(self, expression: exp.IndexParameters) -> str: 1263 using = self.sql(expression, "using") 1264 using = f" USING {using}" if using else "" 1265 columns = self.expressions(expression, key="columns", flat=True) 1266 columns = f"({columns})" if columns else "" 1267 partition_by = self.expressions(expression, key="partition_by", flat=True) 1268 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1269 where = self.sql(expression, "where") 1270 include = self.expressions(expression, key="include", flat=True) 1271 if include: 1272 include = f" INCLUDE ({include})" 1273 with_storage = self.expressions(expression, key="with_storage", flat=True) 1274 with_storage = f" WITH ({with_storage})" if with_storage else "" 1275 tablespace = self.sql(expression, "tablespace") 1276 tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else "" 1277 1278 return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}" 1279 1280 def index_sql(self, expression: exp.Index) -> str: 1281 unique = "UNIQUE " if expression.args.get("unique") else "" 1282 primary = "PRIMARY " if expression.args.get("primary") else "" 1283 amp = "AMP " if expression.args.get("amp") else "" 1284 name = self.sql(expression, "this") 1285 name = f"{name} " if name else "" 1286 table = self.sql(expression, "table") 1287 table = f"{self.INDEX_ON} {table}" if table else "" 1288 1289 index = "INDEX " if not table else "" 1290 1291 params = self.sql(expression, "params") 1292 return f"{unique}{primary}{amp}{index}{name}{table}{params}" 1293 1294 def identifier_sql(self, expression: exp.Identifier) -> str: 1295 text = expression.name 1296 lower = text.lower() 1297 text = lower if self.normalize and not expression.quoted else text 1298 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1299 if ( 1300 expression.quoted 1301 or self.dialect.can_identify(text, self.identify) 1302 or lower in self.RESERVED_KEYWORDS 1303 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1304 ): 1305 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1306 return text 1307 1308 def hex_sql(self, expression: exp.Hex) -> str: 1309 text = self.func(self.HEX_FUNC, self.sql(expression, "this")) 1310 if self.dialect.HEX_LOWERCASE: 1311 text = self.func("LOWER", text) 1312 1313 return text 1314 1315 def lowerhex_sql(self, expression: exp.LowerHex) -> str: 1316 text = self.func(self.HEX_FUNC, self.sql(expression, "this")) 1317 if not self.dialect.HEX_LOWERCASE: 1318 text = self.func("LOWER", text) 1319 return text 1320 1321 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1322 input_format = self.sql(expression, "input_format") 1323 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1324 output_format = self.sql(expression, "output_format") 1325 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1326 return self.sep().join((input_format, output_format)) 1327 1328 def national_sql(self, expression: exp.National, prefix: str = "N") -> str: 1329 string = self.sql(exp.Literal.string(expression.name)) 1330 return f"{prefix}{string}" 1331 1332 def partition_sql(self, expression: exp.Partition) -> str: 1333 return f"PARTITION({self.expressions(expression, flat=True)})" 1334 1335 def properties_sql(self, expression: exp.Properties) -> str: 1336 root_properties = [] 1337 with_properties = [] 1338 1339 for p in expression.expressions: 1340 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1341 if p_loc == exp.Properties.Location.POST_WITH: 1342 with_properties.append(p) 1343 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1344 root_properties.append(p) 1345 1346 return self.root_properties( 1347 exp.Properties(expressions=root_properties) 1348 ) + self.with_properties(exp.Properties(expressions=with_properties)) 1349 1350 def root_properties(self, properties: exp.Properties) -> str: 1351 if properties.expressions: 1352 return self.sep() + self.expressions(properties, indent=False, sep=" ") 1353 return "" 1354 1355 def properties( 1356 self, 1357 properties: exp.Properties, 1358 prefix: str = "", 1359 sep: str = ", ", 1360 suffix: str = "", 1361 wrapped: bool = True, 1362 ) -> str: 1363 if properties.expressions: 1364 expressions = self.expressions(properties, sep=sep, indent=False) 1365 if expressions: 1366 expressions = self.wrap(expressions) if wrapped else expressions 1367 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1368 return "" 1369 1370 def with_properties(self, properties: exp.Properties) -> str: 1371 return self.properties(properties, prefix=self.seg("WITH")) 1372 1373 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1374 properties_locs = defaultdict(list) 1375 for p in properties.expressions: 1376 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1377 if p_loc != exp.Properties.Location.UNSUPPORTED: 1378 properties_locs[p_loc].append(p) 1379 else: 1380 self.unsupported(f"Unsupported property {p.key}") 1381 1382 return properties_locs 1383 1384 def property_name(self, expression: exp.Property, string_key: bool = False) -> str: 1385 if isinstance(expression.this, exp.Dot): 1386 return self.sql(expression, "this") 1387 return f"'{expression.name}'" if string_key else expression.name 1388 1389 def property_sql(self, expression: exp.Property) -> str: 1390 property_cls = expression.__class__ 1391 if property_cls == exp.Property: 1392 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1393 1394 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1395 if not property_name: 1396 self.unsupported(f"Unsupported property {expression.key}") 1397 1398 return f"{property_name}={self.sql(expression, 'this')}" 1399 1400 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1401 if self.SUPPORTS_CREATE_TABLE_LIKE: 1402 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1403 options = f" {options}" if options else "" 1404 1405 like = f"LIKE {self.sql(expression, 'this')}{options}" 1406 if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema): 1407 like = f"({like})" 1408 1409 return like 1410 1411 if expression.expressions: 1412 self.unsupported("Transpilation of LIKE property options is unsupported") 1413 1414 select = exp.select("*").from_(expression.this).limit(0) 1415 return f"AS {self.sql(select)}" 1416 1417 def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str: 1418 no = "NO " if expression.args.get("no") else "" 1419 protection = " PROTECTION" if expression.args.get("protection") else "" 1420 return f"{no}FALLBACK{protection}" 1421 1422 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1423 no = "NO " if expression.args.get("no") else "" 1424 local = expression.args.get("local") 1425 local = f"{local} " if local else "" 1426 dual = "DUAL " if expression.args.get("dual") else "" 1427 before = "BEFORE " if expression.args.get("before") else "" 1428 after = "AFTER " if expression.args.get("after") else "" 1429 return f"{no}{local}{dual}{before}{after}JOURNAL" 1430 1431 def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str: 1432 freespace = self.sql(expression, "this") 1433 percent = " PERCENT" if expression.args.get("percent") else "" 1434 return f"FREESPACE={freespace}{percent}" 1435 1436 def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str: 1437 if expression.args.get("default"): 1438 property = "DEFAULT" 1439 elif expression.args.get("on"): 1440 property = "ON" 1441 else: 1442 property = "OFF" 1443 return f"CHECKSUM={property}" 1444 1445 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1446 if expression.args.get("no"): 1447 return "NO MERGEBLOCKRATIO" 1448 if expression.args.get("default"): 1449 return "DEFAULT MERGEBLOCKRATIO" 1450 1451 percent = " PERCENT" if expression.args.get("percent") else "" 1452 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}" 1453 1454 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1455 default = expression.args.get("default") 1456 minimum = expression.args.get("minimum") 1457 maximum = expression.args.get("maximum") 1458 if default or minimum or maximum: 1459 if default: 1460 prop = "DEFAULT" 1461 elif minimum: 1462 prop = "MINIMUM" 1463 else: 1464 prop = "MAXIMUM" 1465 return f"{prop} DATABLOCKSIZE" 1466 units = expression.args.get("units") 1467 units = f" {units}" if units else "" 1468 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}" 1469 1470 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1471 autotemp = expression.args.get("autotemp") 1472 always = expression.args.get("always") 1473 default = expression.args.get("default") 1474 manual = expression.args.get("manual") 1475 never = expression.args.get("never") 1476 1477 if autotemp is not None: 1478 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1479 elif always: 1480 prop = "ALWAYS" 1481 elif default: 1482 prop = "DEFAULT" 1483 elif manual: 1484 prop = "MANUAL" 1485 elif never: 1486 prop = "NEVER" 1487 return f"BLOCKCOMPRESSION={prop}" 1488 1489 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1490 no = expression.args.get("no") 1491 no = " NO" if no else "" 1492 concurrent = expression.args.get("concurrent") 1493 concurrent = " CONCURRENT" if concurrent else "" 1494 target = self.sql(expression, "target") 1495 target = f" {target}" if target else "" 1496 return f"WITH{no}{concurrent} ISOLATED LOADING{target}" 1497 1498 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1499 if isinstance(expression.this, list): 1500 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1501 if expression.this: 1502 modulus = self.sql(expression, "this") 1503 remainder = self.sql(expression, "expression") 1504 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1505 1506 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1507 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1508 return f"FROM ({from_expressions}) TO ({to_expressions})" 1509 1510 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1511 this = self.sql(expression, "this") 1512 1513 for_values_or_default = expression.expression 1514 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1515 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1516 else: 1517 for_values_or_default = " DEFAULT" 1518 1519 return f"PARTITION OF {this}{for_values_or_default}" 1520 1521 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1522 kind = expression.args.get("kind") 1523 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1524 for_or_in = expression.args.get("for_or_in") 1525 for_or_in = f" {for_or_in}" if for_or_in else "" 1526 lock_type = expression.args.get("lock_type") 1527 override = " OVERRIDE" if expression.args.get("override") else "" 1528 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}" 1529 1530 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1531 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1532 statistics = expression.args.get("statistics") 1533 statistics_sql = "" 1534 if statistics is not None: 1535 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1536 return f"{data_sql}{statistics_sql}" 1537 1538 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1539 sql = "WITH(SYSTEM_VERSIONING=ON" 1540 1541 if expression.this: 1542 history_table = self.sql(expression, "this") 1543 sql = f"{sql}(HISTORY_TABLE={history_table}" 1544 1545 if expression.expression: 1546 data_consistency_check = self.sql(expression, "expression") 1547 sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}" 1548 1549 sql = f"{sql})" 1550 1551 return f"{sql})" 1552 1553 def insert_sql(self, expression: exp.Insert) -> str: 1554 hint = self.sql(expression, "hint") 1555 overwrite = expression.args.get("overwrite") 1556 1557 if isinstance(expression.this, exp.Directory): 1558 this = " OVERWRITE" if overwrite else " INTO" 1559 else: 1560 this = self.INSERT_OVERWRITE if overwrite else " INTO" 1561 1562 stored = self.sql(expression, "stored") 1563 stored = f" {stored}" if stored else "" 1564 alternative = expression.args.get("alternative") 1565 alternative = f" OR {alternative}" if alternative else "" 1566 ignore = " IGNORE" if expression.args.get("ignore") else "" 1567 is_function = expression.args.get("is_function") 1568 if is_function: 1569 this = f"{this} FUNCTION" 1570 this = f"{this} {self.sql(expression, 'this')}" 1571 1572 exists = " IF EXISTS" if expression.args.get("exists") else "" 1573 where = self.sql(expression, "where") 1574 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1575 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1576 on_conflict = self.sql(expression, "conflict") 1577 on_conflict = f" {on_conflict}" if on_conflict else "" 1578 by_name = " BY NAME" if expression.args.get("by_name") else "" 1579 returning = self.sql(expression, "returning") 1580 1581 if self.RETURNING_END: 1582 expression_sql = f"{expression_sql}{on_conflict}{returning}" 1583 else: 1584 expression_sql = f"{returning}{expression_sql}{on_conflict}" 1585 1586 sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{where}{expression_sql}" 1587 return self.prepend_ctes(expression, sql) 1588 1589 def intersect_sql(self, expression: exp.Intersect) -> str: 1590 return self.set_operations(expression) 1591 1592 def intersect_op(self, expression: exp.Intersect) -> str: 1593 return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}" 1594 1595 def introducer_sql(self, expression: exp.Introducer) -> str: 1596 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 1597 1598 def kill_sql(self, expression: exp.Kill) -> str: 1599 kind = self.sql(expression, "kind") 1600 kind = f" {kind}" if kind else "" 1601 this = self.sql(expression, "this") 1602 this = f" {this}" if this else "" 1603 return f"KILL{kind}{this}" 1604 1605 def pseudotype_sql(self, expression: exp.PseudoType) -> str: 1606 return expression.name 1607 1608 def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str: 1609 return expression.name 1610 1611 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1612 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1613 1614 constraint = self.sql(expression, "constraint") 1615 constraint = f" ON CONSTRAINT {constraint}" if constraint else "" 1616 1617 conflict_keys = self.expressions(expression, key="conflict_keys", flat=True) 1618 conflict_keys = f"({conflict_keys}) " if conflict_keys else " " 1619 action = self.sql(expression, "action") 1620 1621 expressions = self.expressions(expression, flat=True) 1622 if expressions: 1623 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1624 expressions = f" {set_keyword}{expressions}" 1625 1626 return f"{conflict}{constraint}{conflict_keys}{action}{expressions}" 1627 1628 def returning_sql(self, expression: exp.Returning) -> str: 1629 return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}" 1630 1631 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1632 fields = self.sql(expression, "fields") 1633 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1634 escaped = self.sql(expression, "escaped") 1635 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1636 items = self.sql(expression, "collection_items") 1637 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1638 keys = self.sql(expression, "map_keys") 1639 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1640 lines = self.sql(expression, "lines") 1641 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1642 null = self.sql(expression, "null") 1643 null = f" NULL DEFINED AS {null}" if null else "" 1644 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}" 1645 1646 def withtablehint_sql(self, expression: exp.WithTableHint) -> str: 1647 return f"WITH ({self.expressions(expression, flat=True)})" 1648 1649 def indextablehint_sql(self, expression: exp.IndexTableHint) -> str: 1650 this = f"{self.sql(expression, 'this')} INDEX" 1651 target = self.sql(expression, "target") 1652 target = f" FOR {target}" if target else "" 1653 return f"{this}{target} ({self.expressions(expression, flat=True)})" 1654 1655 def historicaldata_sql(self, expression: exp.HistoricalData) -> str: 1656 this = self.sql(expression, "this") 1657 kind = self.sql(expression, "kind") 1658 expr = self.sql(expression, "expression") 1659 return f"{this} ({kind} => {expr})" 1660 1661 def table_parts(self, expression: exp.Table) -> str: 1662 return ".".join( 1663 self.sql(part) 1664 for part in ( 1665 expression.args.get("catalog"), 1666 expression.args.get("db"), 1667 expression.args.get("this"), 1668 ) 1669 if part is not None 1670 ) 1671 1672 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1673 table = self.table_parts(expression) 1674 only = "ONLY " if expression.args.get("only") else "" 1675 partition = self.sql(expression, "partition") 1676 partition = f" {partition}" if partition else "" 1677 version = self.sql(expression, "version") 1678 version = f" {version}" if version else "" 1679 alias = self.sql(expression, "alias") 1680 alias = f"{sep}{alias}" if alias else "" 1681 hints = self.expressions(expression, key="hints", sep=" ") 1682 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1683 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 1684 pivots = f" {pivots}" if pivots else "" 1685 joins = self.indent( 1686 self.expressions(expression, key="joins", sep="", flat=True), skip_first=True 1687 ) 1688 laterals = self.expressions(expression, key="laterals", sep="") 1689 1690 file_format = self.sql(expression, "format") 1691 if file_format: 1692 pattern = self.sql(expression, "pattern") 1693 pattern = f", PATTERN => {pattern}" if pattern else "" 1694 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1695 1696 ordinality = expression.args.get("ordinality") or "" 1697 if ordinality: 1698 ordinality = f" WITH ORDINALITY{alias}" 1699 alias = "" 1700 1701 when = self.sql(expression, "when") 1702 if when: 1703 table = f"{table} {when}" 1704 1705 return f"{only}{table}{partition}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}" 1706 1707 def tablesample_sql( 1708 self, 1709 expression: exp.TableSample, 1710 sep: str = " AS ", 1711 tablesample_keyword: t.Optional[str] = None, 1712 ) -> str: 1713 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1714 table = expression.this.copy() 1715 table.set("alias", None) 1716 this = self.sql(table) 1717 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1718 else: 1719 this = self.sql(expression, "this") 1720 alias = "" 1721 1722 method = self.sql(expression, "method") 1723 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1724 numerator = self.sql(expression, "bucket_numerator") 1725 denominator = self.sql(expression, "bucket_denominator") 1726 field = self.sql(expression, "bucket_field") 1727 field = f" ON {field}" if field else "" 1728 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1729 seed = self.sql(expression, "seed") 1730 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1731 1732 size = self.sql(expression, "size") 1733 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1734 size = f"{size} ROWS" 1735 1736 percent = self.sql(expression, "percent") 1737 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1738 percent = f"{percent} PERCENT" 1739 1740 expr = f"{bucket}{percent}{size}" 1741 if self.TABLESAMPLE_REQUIRES_PARENS: 1742 expr = f"({expr})" 1743 1744 return ( 1745 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1746 ) 1747 1748 def pivot_sql(self, expression: exp.Pivot) -> str: 1749 expressions = self.expressions(expression, flat=True) 1750 1751 if expression.this: 1752 this = self.sql(expression, "this") 1753 if not expressions: 1754 return f"UNPIVOT {this}" 1755 1756 on = f"{self.seg('ON')} {expressions}" 1757 using = self.expressions(expression, key="using", flat=True) 1758 using = f"{self.seg('USING')} {using}" if using else "" 1759 group = self.sql(expression, "group") 1760 return f"PIVOT {this}{on}{using}{group}" 1761 1762 alias = self.sql(expression, "alias") 1763 alias = f" AS {alias}" if alias else "" 1764 direction = "UNPIVOT" if expression.unpivot else "PIVOT" 1765 field = self.sql(expression, "field") 1766 include_nulls = expression.args.get("include_nulls") 1767 if include_nulls is not None: 1768 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1769 else: 1770 nulls = "" 1771 return f"{direction}{nulls}({expressions} FOR {field}){alias}" 1772 1773 def version_sql(self, expression: exp.Version) -> str: 1774 this = f"FOR {expression.name}" 1775 kind = expression.text("kind") 1776 expr = self.sql(expression, "expression") 1777 return f"{this} {kind} {expr}" 1778 1779 def tuple_sql(self, expression: exp.Tuple) -> str: 1780 return f"({self.expressions(expression, flat=True)})" 1781 1782 def update_sql(self, expression: exp.Update) -> str: 1783 this = self.sql(expression, "this") 1784 set_sql = self.expressions(expression, flat=True) 1785 from_sql = self.sql(expression, "from") 1786 where_sql = self.sql(expression, "where") 1787 returning = self.sql(expression, "returning") 1788 order = self.sql(expression, "order") 1789 limit = self.sql(expression, "limit") 1790 if self.RETURNING_END: 1791 expression_sql = f"{from_sql}{where_sql}{returning}" 1792 else: 1793 expression_sql = f"{returning}{from_sql}{where_sql}" 1794 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1795 return self.prepend_ctes(expression, sql) 1796 1797 def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str: 1798 values_as_table = values_as_table and self.VALUES_AS_TABLE 1799 1800 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1801 if values_as_table or not expression.find_ancestor(exp.From, exp.Join): 1802 args = self.expressions(expression) 1803 alias = self.sql(expression, "alias") 1804 values = f"VALUES{self.seg('')}{args}" 1805 values = ( 1806 f"({values})" 1807 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1808 else values 1809 ) 1810 return f"{values} AS {alias}" if alias else values 1811 1812 # Converts `VALUES...` expression into a series of select unions. 1813 alias_node = expression.args.get("alias") 1814 column_names = alias_node and alias_node.columns 1815 1816 selects: t.List[exp.Query] = [] 1817 1818 for i, tup in enumerate(expression.expressions): 1819 row = tup.expressions 1820 1821 if i == 0 and column_names: 1822 row = [ 1823 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1824 ] 1825 1826 selects.append(exp.Select(expressions=row)) 1827 1828 if self.pretty: 1829 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1830 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1831 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1832 query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1833 return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False)) 1834 1835 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1836 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1837 return f"({unions}){alias}" 1838 1839 def var_sql(self, expression: exp.Var) -> str: 1840 return self.sql(expression, "this") 1841 1842 def into_sql(self, expression: exp.Into) -> str: 1843 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1844 unlogged = " UNLOGGED" if expression.args.get("unlogged") else "" 1845 return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}" 1846 1847 def from_sql(self, expression: exp.From) -> str: 1848 return f"{self.seg('FROM')} {self.sql(expression, 'this')}" 1849 1850 def group_sql(self, expression: exp.Group) -> str: 1851 group_by_all = expression.args.get("all") 1852 if group_by_all is True: 1853 modifier = " ALL" 1854 elif group_by_all is False: 1855 modifier = " DISTINCT" 1856 else: 1857 modifier = "" 1858 1859 group_by = self.op_expressions(f"GROUP BY{modifier}", expression) 1860 1861 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1862 grouping_sets = ( 1863 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1864 ) 1865 1866 cube = expression.args.get("cube", []) 1867 if seq_get(cube, 0) is True: 1868 return f"{group_by}{self.seg('WITH CUBE')}" 1869 else: 1870 cube_sql = self.expressions(expression, key="cube", indent=False) 1871 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1872 1873 rollup = expression.args.get("rollup", []) 1874 if seq_get(rollup, 0) is True: 1875 return f"{group_by}{self.seg('WITH ROLLUP')}" 1876 else: 1877 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1878 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1879 1880 groupings = csv( 1881 grouping_sets, 1882 cube_sql, 1883 rollup_sql, 1884 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1885 sep=self.GROUPINGS_SEP, 1886 ) 1887 1888 if expression.args.get("expressions") and groupings: 1889 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1890 1891 return f"{group_by}{groupings}" 1892 1893 def having_sql(self, expression: exp.Having) -> str: 1894 this = self.indent(self.sql(expression, "this")) 1895 return f"{self.seg('HAVING')}{self.sep()}{this}" 1896 1897 def connect_sql(self, expression: exp.Connect) -> str: 1898 start = self.sql(expression, "start") 1899 start = self.seg(f"START WITH {start}") if start else "" 1900 nocycle = " NOCYCLE" if expression.args.get("nocycle") else "" 1901 connect = self.sql(expression, "connect") 1902 connect = self.seg(f"CONNECT BY{nocycle} {connect}") 1903 return start + connect 1904 1905 def prior_sql(self, expression: exp.Prior) -> str: 1906 return f"PRIOR {self.sql(expression, 'this')}" 1907 1908 def join_sql(self, expression: exp.Join) -> str: 1909 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1910 side = None 1911 else: 1912 side = expression.side 1913 1914 op_sql = " ".join( 1915 op 1916 for op in ( 1917 expression.method, 1918 "GLOBAL" if expression.args.get("global") else None, 1919 side, 1920 expression.kind, 1921 expression.hint if self.JOIN_HINTS else None, 1922 ) 1923 if op 1924 ) 1925 match_cond = self.sql(expression, "match_condition") 1926 match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else "" 1927 on_sql = self.sql(expression, "on") 1928 using = expression.args.get("using") 1929 1930 if not on_sql and using: 1931 on_sql = csv(*(self.sql(column) for column in using)) 1932 1933 this = expression.this 1934 this_sql = self.sql(this) 1935 1936 if on_sql: 1937 on_sql = self.indent(on_sql, skip_first=True) 1938 space = self.seg(" " * self.pad) if self.pretty else " " 1939 if using: 1940 on_sql = f"{space}USING ({on_sql})" 1941 else: 1942 on_sql = f"{space}ON {on_sql}" 1943 elif not op_sql: 1944 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 1945 return f" {this_sql}" 1946 1947 return f", {this_sql}" 1948 1949 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1950 return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}" 1951 1952 def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str: 1953 args = self.expressions(expression, flat=True) 1954 args = f"({args})" if len(args.split(",")) > 1 else args 1955 return f"{args} {arrow_sep} {self.sql(expression, 'this')}" 1956 1957 def lateral_op(self, expression: exp.Lateral) -> str: 1958 cross_apply = expression.args.get("cross_apply") 1959 1960 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 1961 if cross_apply is True: 1962 op = "INNER JOIN " 1963 elif cross_apply is False: 1964 op = "LEFT JOIN " 1965 else: 1966 op = "" 1967 1968 return f"{op}LATERAL" 1969 1970 def lateral_sql(self, expression: exp.Lateral) -> str: 1971 this = self.sql(expression, "this") 1972 1973 if expression.args.get("view"): 1974 alias = expression.args["alias"] 1975 columns = self.expressions(alias, key="columns", flat=True) 1976 table = f" {alias.name}" if alias.name else "" 1977 columns = f" AS {columns}" if columns else "" 1978 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1979 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1980 1981 alias = self.sql(expression, "alias") 1982 alias = f" AS {alias}" if alias else "" 1983 return f"{self.lateral_op(expression)} {this}{alias}" 1984 1985 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1986 this = self.sql(expression, "this") 1987 1988 args = [ 1989 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1990 for e in (expression.args.get(k) for k in ("offset", "expression")) 1991 if e 1992 ] 1993 1994 args_sql = ", ".join(self.sql(e) for e in args) 1995 args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql 1996 expressions = self.expressions(expression, flat=True) 1997 expressions = f" BY {expressions}" if expressions else "" 1998 1999 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}" 2000 2001 def offset_sql(self, expression: exp.Offset) -> str: 2002 this = self.sql(expression, "this") 2003 value = expression.expression 2004 value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value 2005 expressions = self.expressions(expression, flat=True) 2006 expressions = f" BY {expressions}" if expressions else "" 2007 return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}" 2008 2009 def setitem_sql(self, expression: exp.SetItem) -> str: 2010 kind = self.sql(expression, "kind") 2011 kind = f"{kind} " if kind else "" 2012 this = self.sql(expression, "this") 2013 expressions = self.expressions(expression) 2014 collate = self.sql(expression, "collate") 2015 collate = f" COLLATE {collate}" if collate else "" 2016 global_ = "GLOBAL " if expression.args.get("global") else "" 2017 return f"{global_}{kind}{this}{expressions}{collate}" 2018 2019 def set_sql(self, expression: exp.Set) -> str: 2020 expressions = ( 2021 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 2022 ) 2023 tag = " TAG" if expression.args.get("tag") else "" 2024 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}" 2025 2026 def pragma_sql(self, expression: exp.Pragma) -> str: 2027 return f"PRAGMA {self.sql(expression, 'this')}" 2028 2029 def lock_sql(self, expression: exp.Lock) -> str: 2030 if not self.LOCKING_READS_SUPPORTED: 2031 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 2032 return "" 2033 2034 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 2035 expressions = self.expressions(expression, flat=True) 2036 expressions = f" OF {expressions}" if expressions else "" 2037 wait = expression.args.get("wait") 2038 2039 if wait is not None: 2040 if isinstance(wait, exp.Literal): 2041 wait = f" WAIT {self.sql(wait)}" 2042 else: 2043 wait = " NOWAIT" if wait else " SKIP LOCKED" 2044 2045 return f"{lock_type}{expressions}{wait or ''}" 2046 2047 def literal_sql(self, expression: exp.Literal) -> str: 2048 text = expression.this or "" 2049 if expression.is_string: 2050 text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}" 2051 return text 2052 2053 def escape_str(self, text: str, escape_backslash: bool = True) -> str: 2054 if self.dialect.ESCAPED_SEQUENCES: 2055 to_escaped = self.dialect.ESCAPED_SEQUENCES 2056 text = "".join( 2057 to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text 2058 ) 2059 2060 return self._replace_line_breaks(text).replace( 2061 self.dialect.QUOTE_END, self._escaped_quote_end 2062 ) 2063 2064 def loaddata_sql(self, expression: exp.LoadData) -> str: 2065 local = " LOCAL" if expression.args.get("local") else "" 2066 inpath = f" INPATH {self.sql(expression, 'inpath')}" 2067 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 2068 this = f" INTO TABLE {self.sql(expression, 'this')}" 2069 partition = self.sql(expression, "partition") 2070 partition = f" {partition}" if partition else "" 2071 input_format = self.sql(expression, "input_format") 2072 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 2073 serde = self.sql(expression, "serde") 2074 serde = f" SERDE {serde}" if serde else "" 2075 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}" 2076 2077 def null_sql(self, *_) -> str: 2078 return "NULL" 2079 2080 def boolean_sql(self, expression: exp.Boolean) -> str: 2081 return "TRUE" if expression.this else "FALSE" 2082 2083 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 2084 this = self.sql(expression, "this") 2085 this = f"{this} " if this else this 2086 siblings = "SIBLINGS " if expression.args.get("siblings") else "" 2087 order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore 2088 interpolated_values = [ 2089 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 2090 for named_expression in expression.args.get("interpolate") or [] 2091 ] 2092 interpolate = ( 2093 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 2094 ) 2095 return f"{order}{interpolate}" 2096 2097 def withfill_sql(self, expression: exp.WithFill) -> str: 2098 from_sql = self.sql(expression, "from") 2099 from_sql = f" FROM {from_sql}" if from_sql else "" 2100 to_sql = self.sql(expression, "to") 2101 to_sql = f" TO {to_sql}" if to_sql else "" 2102 step_sql = self.sql(expression, "step") 2103 step_sql = f" STEP {step_sql}" if step_sql else "" 2104 return f"WITH FILL{from_sql}{to_sql}{step_sql}" 2105 2106 def cluster_sql(self, expression: exp.Cluster) -> str: 2107 return self.op_expressions("CLUSTER BY", expression) 2108 2109 def distribute_sql(self, expression: exp.Distribute) -> str: 2110 return self.op_expressions("DISTRIBUTE BY", expression) 2111 2112 def sort_sql(self, expression: exp.Sort) -> str: 2113 return self.op_expressions("SORT BY", expression) 2114 2115 def ordered_sql(self, expression: exp.Ordered) -> str: 2116 desc = expression.args.get("desc") 2117 asc = not desc 2118 2119 nulls_first = expression.args.get("nulls_first") 2120 nulls_last = not nulls_first 2121 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 2122 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 2123 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 2124 2125 this = self.sql(expression, "this") 2126 2127 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 2128 nulls_sort_change = "" 2129 if nulls_first and ( 2130 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 2131 ): 2132 nulls_sort_change = " NULLS FIRST" 2133 elif ( 2134 nulls_last 2135 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 2136 and not nulls_are_last 2137 ): 2138 nulls_sort_change = " NULLS LAST" 2139 2140 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 2141 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 2142 window = expression.find_ancestor(exp.Window, exp.Select) 2143 if isinstance(window, exp.Window) and window.args.get("spec"): 2144 self.unsupported( 2145 f"'{nulls_sort_change.strip()}' translation not supported in window functions" 2146 ) 2147 nulls_sort_change = "" 2148 elif self.NULL_ORDERING_SUPPORTED is None: 2149 if expression.this.is_int: 2150 self.unsupported( 2151 f"'{nulls_sort_change.strip()}' translation not supported with positional ordering" 2152 ) 2153 elif not isinstance(expression.this, exp.Rand): 2154 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 2155 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 2156 nulls_sort_change = "" 2157 2158 with_fill = self.sql(expression, "with_fill") 2159 with_fill = f" {with_fill}" if with_fill else "" 2160 2161 return f"{this}{sort_order}{nulls_sort_change}{with_fill}" 2162 2163 def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str: 2164 window_frame = self.sql(expression, "window_frame") 2165 window_frame = f"{window_frame} " if window_frame else "" 2166 2167 this = self.sql(expression, "this") 2168 2169 return f"{window_frame}{this}" 2170 2171 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 2172 partition = self.partition_by_sql(expression) 2173 order = self.sql(expression, "order") 2174 measures = self.expressions(expression, key="measures") 2175 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 2176 rows = self.sql(expression, "rows") 2177 rows = self.seg(rows) if rows else "" 2178 after = self.sql(expression, "after") 2179 after = self.seg(after) if after else "" 2180 pattern = self.sql(expression, "pattern") 2181 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 2182 definition_sqls = [ 2183 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 2184 for definition in expression.args.get("define", []) 2185 ] 2186 definitions = self.expressions(sqls=definition_sqls) 2187 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 2188 body = "".join( 2189 ( 2190 partition, 2191 order, 2192 measures, 2193 rows, 2194 after, 2195 pattern, 2196 define, 2197 ) 2198 ) 2199 alias = self.sql(expression, "alias") 2200 alias = f" {alias}" if alias else "" 2201 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}" 2202 2203 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 2204 limit = expression.args.get("limit") 2205 2206 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 2207 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 2208 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 2209 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 2210 2211 options = self.expressions(expression, key="options") 2212 if options: 2213 options = f" OPTION{self.wrap(options)}" 2214 2215 return csv( 2216 *sqls, 2217 *[self.sql(join) for join in expression.args.get("joins") or []], 2218 self.sql(expression, "connect"), 2219 self.sql(expression, "match"), 2220 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 2221 self.sql(expression, "prewhere"), 2222 self.sql(expression, "where"), 2223 self.sql(expression, "group"), 2224 self.sql(expression, "having"), 2225 *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()], 2226 self.sql(expression, "order"), 2227 *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit), 2228 *self.after_limit_modifiers(expression), 2229 options, 2230 sep="", 2231 ) 2232 2233 def queryoption_sql(self, expression: exp.QueryOption) -> str: 2234 return "" 2235 2236 def offset_limit_modifiers( 2237 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 2238 ) -> t.List[str]: 2239 return [ 2240 self.sql(expression, "offset") if fetch else self.sql(limit), 2241 self.sql(limit) if fetch else self.sql(expression, "offset"), 2242 ] 2243 2244 def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]: 2245 locks = self.expressions(expression, key="locks", sep=" ") 2246 locks = f" {locks}" if locks else "" 2247 return [locks, self.sql(expression, "sample")] 2248 2249 def select_sql(self, expression: exp.Select) -> str: 2250 into = expression.args.get("into") 2251 if not self.SUPPORTS_SELECT_INTO and into: 2252 into.pop() 2253 2254 hint = self.sql(expression, "hint") 2255 distinct = self.sql(expression, "distinct") 2256 distinct = f" {distinct}" if distinct else "" 2257 kind = self.sql(expression, "kind") 2258 2259 limit = expression.args.get("limit") 2260 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP: 2261 top = self.limit_sql(limit, top=True) 2262 limit.pop() 2263 else: 2264 top = "" 2265 2266 expressions = self.expressions(expression) 2267 2268 if kind: 2269 if kind in self.SELECT_KINDS: 2270 kind = f" AS {kind}" 2271 else: 2272 if kind == "STRUCT": 2273 expressions = self.expressions( 2274 sqls=[ 2275 self.sql( 2276 exp.Struct( 2277 expressions=[ 2278 exp.PropertyEQ(this=e.args.get("alias"), expression=e.this) 2279 if isinstance(e, exp.Alias) 2280 else e 2281 for e in expression.expressions 2282 ] 2283 ) 2284 ) 2285 ] 2286 ) 2287 kind = "" 2288 2289 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2290 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2291 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2292 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2293 sql = self.query_modifiers( 2294 expression, 2295 f"SELECT{top_distinct}{kind}{expressions}", 2296 self.sql(expression, "into", comment=False), 2297 self.sql(expression, "from", comment=False), 2298 ) 2299 2300 sql = self.prepend_ctes(expression, sql) 2301 2302 if not self.SUPPORTS_SELECT_INTO and into: 2303 if into.args.get("temporary"): 2304 table_kind = " TEMPORARY" 2305 elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"): 2306 table_kind = " UNLOGGED" 2307 else: 2308 table_kind = "" 2309 sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}" 2310 2311 return sql 2312 2313 def schema_sql(self, expression: exp.Schema) -> str: 2314 this = self.sql(expression, "this") 2315 sql = self.schema_columns_sql(expression) 2316 return f"{this} {sql}" if this and sql else this or sql 2317 2318 def schema_columns_sql(self, expression: exp.Schema) -> str: 2319 if expression.expressions: 2320 return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}" 2321 return "" 2322 2323 def star_sql(self, expression: exp.Star) -> str: 2324 except_ = self.expressions(expression, key="except", flat=True) 2325 except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else "" 2326 replace = self.expressions(expression, key="replace", flat=True) 2327 replace = f"{self.seg('REPLACE')} ({replace})" if replace else "" 2328 rename = self.expressions(expression, key="rename", flat=True) 2329 rename = f"{self.seg('RENAME')} ({rename})" if rename else "" 2330 return f"*{except_}{replace}{rename}" 2331 2332 def parameter_sql(self, expression: exp.Parameter) -> str: 2333 this = self.sql(expression, "this") 2334 return f"{self.PARAMETER_TOKEN}{this}" 2335 2336 def sessionparameter_sql(self, expression: exp.SessionParameter) -> str: 2337 this = self.sql(expression, "this") 2338 kind = expression.text("kind") 2339 if kind: 2340 kind = f"{kind}." 2341 return f"@@{kind}{this}" 2342 2343 def placeholder_sql(self, expression: exp.Placeholder) -> str: 2344 return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?" 2345 2346 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2347 alias = self.sql(expression, "alias") 2348 alias = f"{sep}{alias}" if alias else "" 2349 2350 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2351 pivots = f" {pivots}" if pivots else "" 2352 2353 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2354 return self.prepend_ctes(expression, sql) 2355 2356 def qualify_sql(self, expression: exp.Qualify) -> str: 2357 this = self.indent(self.sql(expression, "this")) 2358 return f"{self.seg('QUALIFY')}{self.sep()}{this}" 2359 2360 def set_operations(self, expression: exp.Union) -> str: 2361 if not self.OUTER_UNION_MODIFIERS: 2362 limit = expression.args.get("limit") 2363 order = expression.args.get("order") 2364 2365 if limit or order: 2366 select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False) 2367 2368 if limit: 2369 select = select.limit(limit.pop(), copy=False) 2370 if order: 2371 select = select.order_by(order.pop(), copy=False) 2372 return self.sql(select) 2373 2374 sqls: t.List[str] = [] 2375 stack: t.List[t.Union[str, exp.Expression]] = [expression] 2376 2377 while stack: 2378 node = stack.pop() 2379 2380 if isinstance(node, exp.Union): 2381 stack.append(node.expression) 2382 stack.append( 2383 self.maybe_comment( 2384 getattr(self, f"{node.key}_op")(node), 2385 comments=node.comments, 2386 separated=True, 2387 ) 2388 ) 2389 stack.append(node.this) 2390 else: 2391 sqls.append(self.sql(node)) 2392 2393 this = self.sep().join(sqls) 2394 this = self.query_modifiers(expression, this) 2395 return self.prepend_ctes(expression, this) 2396 2397 def union_sql(self, expression: exp.Union) -> str: 2398 return self.set_operations(expression) 2399 2400 def union_op(self, expression: exp.Union) -> str: 2401 kind = " DISTINCT" if self.EXPLICIT_UNION else "" 2402 kind = kind if expression.args.get("distinct") else " ALL" 2403 by_name = " BY NAME" if expression.args.get("by_name") else "" 2404 return f"UNION{kind}{by_name}" 2405 2406 def unnest_sql(self, expression: exp.Unnest) -> str: 2407 args = self.expressions(expression, flat=True) 2408 2409 alias = expression.args.get("alias") 2410 offset = expression.args.get("offset") 2411 2412 if self.UNNEST_WITH_ORDINALITY: 2413 if alias and isinstance(offset, exp.Expression): 2414 alias.append("columns", offset) 2415 2416 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2417 columns = alias.columns 2418 alias = self.sql(columns[0]) if columns else "" 2419 else: 2420 alias = self.sql(alias) 2421 2422 alias = f" AS {alias}" if alias else alias 2423 if self.UNNEST_WITH_ORDINALITY: 2424 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2425 else: 2426 if isinstance(offset, exp.Expression): 2427 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2428 elif offset: 2429 suffix = f"{alias} WITH OFFSET" 2430 else: 2431 suffix = alias 2432 2433 return f"UNNEST({args}){suffix}" 2434 2435 def prewhere_sql(self, expression: exp.PreWhere) -> str: 2436 return "" 2437 2438 def where_sql(self, expression: exp.Where) -> str: 2439 this = self.indent(self.sql(expression, "this")) 2440 return f"{self.seg('WHERE')}{self.sep()}{this}" 2441 2442 def window_sql(self, expression: exp.Window) -> str: 2443 this = self.sql(expression, "this") 2444 partition = self.partition_by_sql(expression) 2445 order = expression.args.get("order") 2446 order = self.order_sql(order, flat=True) if order else "" 2447 spec = self.sql(expression, "spec") 2448 alias = self.sql(expression, "alias") 2449 over = self.sql(expression, "over") or "OVER" 2450 2451 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2452 2453 first = expression.args.get("first") 2454 if first is None: 2455 first = "" 2456 else: 2457 first = "FIRST" if first else "LAST" 2458 2459 if not partition and not order and not spec and alias: 2460 return f"{this} {alias}" 2461 2462 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2463 return f"{this} ({args})" 2464 2465 def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str: 2466 partition = self.expressions(expression, key="partition_by", flat=True) 2467 return f"PARTITION BY {partition}" if partition else "" 2468 2469 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2470 kind = self.sql(expression, "kind") 2471 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2472 end = ( 2473 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2474 or "CURRENT ROW" 2475 ) 2476 return f"{kind} BETWEEN {start} AND {end}" 2477 2478 def withingroup_sql(self, expression: exp.WithinGroup) -> str: 2479 this = self.sql(expression, "this") 2480 expression_sql = self.sql(expression, "expression")[1:] # order has a leading space 2481 return f"{this} WITHIN GROUP ({expression_sql})" 2482 2483 def between_sql(self, expression: exp.Between) -> str: 2484 this = self.sql(expression, "this") 2485 low = self.sql(expression, "low") 2486 high = self.sql(expression, "high") 2487 return f"{this} BETWEEN {low} AND {high}" 2488 2489 def bracket_offset_expressions(self, expression: exp.Bracket) -> t.List[exp.Expression]: 2490 return apply_index_offset( 2491 expression.this, 2492 expression.expressions, 2493 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2494 ) 2495 2496 def bracket_sql(self, expression: exp.Bracket) -> str: 2497 expressions = self.bracket_offset_expressions(expression) 2498 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2499 return f"{self.sql(expression, 'this')}[{expressions_sql}]" 2500 2501 def all_sql(self, expression: exp.All) -> str: 2502 return f"ALL {self.wrap(expression)}" 2503 2504 def any_sql(self, expression: exp.Any) -> str: 2505 this = self.sql(expression, "this") 2506 if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)): 2507 if isinstance(expression.this, exp.UNWRAPPED_QUERIES): 2508 this = self.wrap(this) 2509 return f"ANY{this}" 2510 return f"ANY {this}" 2511 2512 def exists_sql(self, expression: exp.Exists) -> str: 2513 return f"EXISTS{self.wrap(expression)}" 2514 2515 def case_sql(self, expression: exp.Case) -> str: 2516 this = self.sql(expression, "this") 2517 statements = [f"CASE {this}" if this else "CASE"] 2518 2519 for e in expression.args["ifs"]: 2520 statements.append(f"WHEN {self.sql(e, 'this')}") 2521 statements.append(f"THEN {self.sql(e, 'true')}") 2522 2523 default = self.sql(expression, "default") 2524 2525 if default: 2526 statements.append(f"ELSE {default}") 2527 2528 statements.append("END") 2529 2530 if self.pretty and self.too_wide(statements): 2531 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2532 2533 return " ".join(statements) 2534 2535 def constraint_sql(self, expression: exp.Constraint) -> str: 2536 this = self.sql(expression, "this") 2537 expressions = self.expressions(expression, flat=True) 2538 return f"CONSTRAINT {this} {expressions}" 2539 2540 def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str: 2541 order = expression.args.get("order") 2542 order = f" OVER ({self.order_sql(order, flat=True)})" if order else "" 2543 return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}" 2544 2545 def extract_sql(self, expression: exp.Extract) -> str: 2546 this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name 2547 expression_sql = self.sql(expression, "expression") 2548 return f"EXTRACT({this} FROM {expression_sql})" 2549 2550 def trim_sql(self, expression: exp.Trim) -> str: 2551 trim_type = self.sql(expression, "position") 2552 2553 if trim_type == "LEADING": 2554 return self.func("LTRIM", expression.this) 2555 elif trim_type == "TRAILING": 2556 return self.func("RTRIM", expression.this) 2557 else: 2558 return self.func("TRIM", expression.this, expression.expression) 2559 2560 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2561 args = expression.expressions 2562 if isinstance(expression, exp.ConcatWs): 2563 args = args[1:] # Skip the delimiter 2564 2565 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2566 args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args] 2567 2568 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2569 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2570 2571 return args 2572 2573 def concat_sql(self, expression: exp.Concat) -> str: 2574 expressions = self.convert_concat_args(expression) 2575 2576 # Some dialects don't allow a single-argument CONCAT call 2577 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2578 return self.sql(expressions[0]) 2579 2580 return self.func("CONCAT", *expressions) 2581 2582 def concatws_sql(self, expression: exp.ConcatWs) -> str: 2583 return self.func( 2584 "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression) 2585 ) 2586 2587 def check_sql(self, expression: exp.Check) -> str: 2588 this = self.sql(expression, key="this") 2589 return f"CHECK ({this})" 2590 2591 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2592 expressions = self.expressions(expression, flat=True) 2593 reference = self.sql(expression, "reference") 2594 reference = f" {reference}" if reference else "" 2595 delete = self.sql(expression, "delete") 2596 delete = f" ON DELETE {delete}" if delete else "" 2597 update = self.sql(expression, "update") 2598 update = f" ON UPDATE {update}" if update else "" 2599 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}" 2600 2601 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2602 expressions = self.expressions(expression, flat=True) 2603 options = self.expressions(expression, key="options", flat=True, sep=" ") 2604 options = f" {options}" if options else "" 2605 return f"PRIMARY KEY ({expressions}){options}" 2606 2607 def if_sql(self, expression: exp.If) -> str: 2608 return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false"))) 2609 2610 def matchagainst_sql(self, expression: exp.MatchAgainst) -> str: 2611 modifier = expression.args.get("modifier") 2612 modifier = f" {modifier}" if modifier else "" 2613 return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})" 2614 2615 def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str: 2616 return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}" 2617 2618 def jsonpath_sql(self, expression: exp.JSONPath) -> str: 2619 path = self.expressions(expression, sep="", flat=True).lstrip(".") 2620 return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}" 2621 2622 def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str: 2623 if isinstance(expression, exp.JSONPathPart): 2624 transform = self.TRANSFORMS.get(expression.__class__) 2625 if not callable(transform): 2626 self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}") 2627 return "" 2628 2629 return transform(self, expression) 2630 2631 if isinstance(expression, int): 2632 return str(expression) 2633 2634 if self.JSON_PATH_SINGLE_QUOTE_ESCAPE: 2635 escaped = expression.replace("'", "\\'") 2636 escaped = f"\\'{expression}\\'" 2637 else: 2638 escaped = expression.replace('"', '\\"') 2639 escaped = f'"{escaped}"' 2640 2641 return escaped 2642 2643 def formatjson_sql(self, expression: exp.FormatJson) -> str: 2644 return f"{self.sql(expression, 'this')} FORMAT JSON" 2645 2646 def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str: 2647 null_handling = expression.args.get("null_handling") 2648 null_handling = f" {null_handling}" if null_handling else "" 2649 2650 unique_keys = expression.args.get("unique_keys") 2651 if unique_keys is not None: 2652 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2653 else: 2654 unique_keys = "" 2655 2656 return_type = self.sql(expression, "return_type") 2657 return_type = f" RETURNING {return_type}" if return_type else "" 2658 encoding = self.sql(expression, "encoding") 2659 encoding = f" ENCODING {encoding}" if encoding else "" 2660 2661 return self.func( 2662 "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG", 2663 *expression.expressions, 2664 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2665 ) 2666 2667 def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str: 2668 return self.jsonobject_sql(expression) 2669 2670 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2671 null_handling = expression.args.get("null_handling") 2672 null_handling = f" {null_handling}" if null_handling else "" 2673 return_type = self.sql(expression, "return_type") 2674 return_type = f" RETURNING {return_type}" if return_type else "" 2675 strict = " STRICT" if expression.args.get("strict") else "" 2676 return self.func( 2677 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2678 ) 2679 2680 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2681 this = self.sql(expression, "this") 2682 order = self.sql(expression, "order") 2683 null_handling = expression.args.get("null_handling") 2684 null_handling = f" {null_handling}" if null_handling else "" 2685 return_type = self.sql(expression, "return_type") 2686 return_type = f" RETURNING {return_type}" if return_type else "" 2687 strict = " STRICT" if expression.args.get("strict") else "" 2688 return self.func( 2689 "JSON_ARRAYAGG", 2690 this, 2691 suffix=f"{order}{null_handling}{return_type}{strict})", 2692 ) 2693 2694 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2695 path = self.sql(expression, "path") 2696 path = f" PATH {path}" if path else "" 2697 nested_schema = self.sql(expression, "nested_schema") 2698 2699 if nested_schema: 2700 return f"NESTED{path} {nested_schema}" 2701 2702 this = self.sql(expression, "this") 2703 kind = self.sql(expression, "kind") 2704 kind = f" {kind}" if kind else "" 2705 return f"{this}{kind}{path}" 2706 2707 def jsonschema_sql(self, expression: exp.JSONSchema) -> str: 2708 return self.func("COLUMNS", *expression.expressions) 2709 2710 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2711 this = self.sql(expression, "this") 2712 path = self.sql(expression, "path") 2713 path = f", {path}" if path else "" 2714 error_handling = expression.args.get("error_handling") 2715 error_handling = f" {error_handling}" if error_handling else "" 2716 empty_handling = expression.args.get("empty_handling") 2717 empty_handling = f" {empty_handling}" if empty_handling else "" 2718 schema = self.sql(expression, "schema") 2719 return self.func( 2720 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2721 ) 2722 2723 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2724 this = self.sql(expression, "this") 2725 kind = self.sql(expression, "kind") 2726 path = self.sql(expression, "path") 2727 path = f" {path}" if path else "" 2728 as_json = " AS JSON" if expression.args.get("as_json") else "" 2729 return f"{this} {kind}{path}{as_json}" 2730 2731 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2732 this = self.sql(expression, "this") 2733 path = self.sql(expression, "path") 2734 path = f", {path}" if path else "" 2735 expressions = self.expressions(expression) 2736 with_ = ( 2737 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2738 if expressions 2739 else "" 2740 ) 2741 return f"OPENJSON({this}{path}){with_}" 2742 2743 def in_sql(self, expression: exp.In) -> str: 2744 query = expression.args.get("query") 2745 unnest = expression.args.get("unnest") 2746 field = expression.args.get("field") 2747 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2748 2749 if query: 2750 in_sql = self.sql(query) 2751 elif unnest: 2752 in_sql = self.in_unnest_op(unnest) 2753 elif field: 2754 in_sql = self.sql(field) 2755 else: 2756 in_sql = f"({self.expressions(expression, flat=True)})" 2757 2758 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}" 2759 2760 def in_unnest_op(self, unnest: exp.Unnest) -> str: 2761 return f"(SELECT {self.sql(unnest)})" 2762 2763 def interval_sql(self, expression: exp.Interval) -> str: 2764 unit = self.sql(expression, "unit") 2765 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2766 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2767 unit = f" {unit}" if unit else "" 2768 2769 if self.SINGLE_STRING_INTERVAL: 2770 this = expression.this.name if expression.this else "" 2771 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2772 2773 this = self.sql(expression, "this") 2774 if this: 2775 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2776 this = f" {this}" if unwrapped else f" ({this})" 2777 2778 return f"INTERVAL{this}{unit}" 2779 2780 def return_sql(self, expression: exp.Return) -> str: 2781 return f"RETURN {self.sql(expression, 'this')}" 2782 2783 def reference_sql(self, expression: exp.Reference) -> str: 2784 this = self.sql(expression, "this") 2785 expressions = self.expressions(expression, flat=True) 2786 expressions = f"({expressions})" if expressions else "" 2787 options = self.expressions(expression, key="options", flat=True, sep=" ") 2788 options = f" {options}" if options else "" 2789 return f"REFERENCES {this}{expressions}{options}" 2790 2791 def anonymous_sql(self, expression: exp.Anonymous) -> str: 2792 return self.func(self.sql(expression, "this"), *expression.expressions) 2793 2794 def paren_sql(self, expression: exp.Paren) -> str: 2795 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2796 return f"({sql}{self.seg(')', sep='')}" 2797 2798 def neg_sql(self, expression: exp.Neg) -> str: 2799 # This makes sure we don't convert "- - 5" to "--5", which is a comment 2800 this_sql = self.sql(expression, "this") 2801 sep = " " if this_sql[0] == "-" else "" 2802 return f"-{sep}{this_sql}" 2803 2804 def not_sql(self, expression: exp.Not) -> str: 2805 return f"NOT {self.sql(expression, 'this')}" 2806 2807 def alias_sql(self, expression: exp.Alias) -> str: 2808 alias = self.sql(expression, "alias") 2809 alias = f" AS {alias}" if alias else "" 2810 return f"{self.sql(expression, 'this')}{alias}" 2811 2812 def pivotalias_sql(self, expression: exp.PivotAlias) -> str: 2813 alias = expression.args["alias"] 2814 identifier_alias = isinstance(alias, exp.Identifier) 2815 2816 if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2817 alias.replace(exp.Literal.string(alias.output_name)) 2818 elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2819 alias.replace(exp.to_identifier(alias.output_name)) 2820 2821 return self.alias_sql(expression) 2822 2823 def aliases_sql(self, expression: exp.Aliases) -> str: 2824 return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})" 2825 2826 def atindex_sql(self, expression: exp.AtTimeZone) -> str: 2827 this = self.sql(expression, "this") 2828 index = self.sql(expression, "expression") 2829 return f"{this} AT {index}" 2830 2831 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 2832 this = self.sql(expression, "this") 2833 zone = self.sql(expression, "zone") 2834 return f"{this} AT TIME ZONE {zone}" 2835 2836 def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str: 2837 this = self.sql(expression, "this") 2838 zone = self.sql(expression, "zone") 2839 return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'" 2840 2841 def add_sql(self, expression: exp.Add) -> str: 2842 return self.binary(expression, "+") 2843 2844 def and_sql( 2845 self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None 2846 ) -> str: 2847 return self.connector_sql(expression, "AND", stack) 2848 2849 def or_sql( 2850 self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None 2851 ) -> str: 2852 return self.connector_sql(expression, "OR", stack) 2853 2854 def xor_sql( 2855 self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None 2856 ) -> str: 2857 return self.connector_sql(expression, "XOR", stack) 2858 2859 def connector_sql( 2860 self, 2861 expression: exp.Connector, 2862 op: str, 2863 stack: t.Optional[t.List[str | exp.Expression]] = None, 2864 ) -> str: 2865 if stack is not None: 2866 if expression.expressions: 2867 stack.append(self.expressions(expression, sep=f" {op} ")) 2868 else: 2869 stack.append(expression.right) 2870 if expression.comments and self.comments: 2871 for comment in expression.comments: 2872 if comment: 2873 op += f" /*{self.pad_comment(comment)}*/" 2874 stack.extend((op, expression.left)) 2875 return op 2876 2877 stack = [expression] 2878 sqls: t.List[str] = [] 2879 ops = set() 2880 2881 while stack: 2882 node = stack.pop() 2883 if isinstance(node, exp.Connector): 2884 ops.add(getattr(self, f"{node.key}_sql")(node, stack)) 2885 else: 2886 sql = self.sql(node) 2887 if sqls and sqls[-1] in ops: 2888 sqls[-1] += f" {sql}" 2889 else: 2890 sqls.append(sql) 2891 2892 sep = "\n" if self.pretty and self.too_wide(sqls) else " " 2893 return sep.join(sqls) 2894 2895 def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str: 2896 return self.binary(expression, "&") 2897 2898 def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str: 2899 return self.binary(expression, "<<") 2900 2901 def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str: 2902 return f"~{self.sql(expression, 'this')}" 2903 2904 def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str: 2905 return self.binary(expression, "|") 2906 2907 def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str: 2908 return self.binary(expression, ">>") 2909 2910 def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str: 2911 return self.binary(expression, "^") 2912 2913 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2914 format_sql = self.sql(expression, "format") 2915 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2916 to_sql = self.sql(expression, "to") 2917 to_sql = f" {to_sql}" if to_sql else "" 2918 action = self.sql(expression, "action") 2919 action = f" {action}" if action else "" 2920 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql}{action})" 2921 2922 def currentdate_sql(self, expression: exp.CurrentDate) -> str: 2923 zone = self.sql(expression, "this") 2924 return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE" 2925 2926 def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str: 2927 return self.func("CURRENT_TIMESTAMP", expression.this) 2928 2929 def collate_sql(self, expression: exp.Collate) -> str: 2930 if self.COLLATE_IS_FUNC: 2931 return self.function_fallback_sql(expression) 2932 return self.binary(expression, "COLLATE") 2933 2934 def command_sql(self, expression: exp.Command) -> str: 2935 return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}" 2936 2937 def comment_sql(self, expression: exp.Comment) -> str: 2938 this = self.sql(expression, "this") 2939 kind = expression.args["kind"] 2940 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 2941 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2942 expression_sql = self.sql(expression, "expression") 2943 return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}" 2944 2945 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2946 this = self.sql(expression, "this") 2947 delete = " DELETE" if expression.args.get("delete") else "" 2948 recompress = self.sql(expression, "recompress") 2949 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2950 to_disk = self.sql(expression, "to_disk") 2951 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2952 to_volume = self.sql(expression, "to_volume") 2953 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2954 return f"{this}{delete}{recompress}{to_disk}{to_volume}" 2955 2956 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2957 where = self.sql(expression, "where") 2958 group = self.sql(expression, "group") 2959 aggregates = self.expressions(expression, key="aggregates") 2960 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2961 2962 if not (where or group or aggregates) and len(expression.expressions) == 1: 2963 return f"TTL {self.expressions(expression, flat=True)}" 2964 2965 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}" 2966 2967 def transaction_sql(self, expression: exp.Transaction) -> str: 2968 return "BEGIN" 2969 2970 def commit_sql(self, expression: exp.Commit) -> str: 2971 chain = expression.args.get("chain") 2972 if chain is not None: 2973 chain = " AND CHAIN" if chain else " AND NO CHAIN" 2974 2975 return f"COMMIT{chain or ''}" 2976 2977 def rollback_sql(self, expression: exp.Rollback) -> str: 2978 savepoint = expression.args.get("savepoint") 2979 savepoint = f" TO {savepoint}" if savepoint else "" 2980 return f"ROLLBACK{savepoint}" 2981 2982 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2983 this = self.sql(expression, "this") 2984 2985 dtype = self.sql(expression, "dtype") 2986 if dtype: 2987 collate = self.sql(expression, "collate") 2988 collate = f" COLLATE {collate}" if collate else "" 2989 using = self.sql(expression, "using") 2990 using = f" USING {using}" if using else "" 2991 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2992 2993 default = self.sql(expression, "default") 2994 if default: 2995 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2996 2997 comment = self.sql(expression, "comment") 2998 if comment: 2999 return f"ALTER COLUMN {this} COMMENT {comment}" 3000 3001 if not expression.args.get("drop"): 3002 self.unsupported("Unsupported ALTER COLUMN syntax") 3003 3004 return f"ALTER COLUMN {this} DROP DEFAULT" 3005 3006 def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str: 3007 this = self.sql(expression, "this") 3008 if not isinstance(expression.this, exp.Var): 3009 this = f"KEY DISTKEY {this}" 3010 return f"ALTER DISTSTYLE {this}" 3011 3012 def altersortkey_sql(self, expression: exp.AlterSortKey) -> str: 3013 compound = " COMPOUND" if expression.args.get("compound") else "" 3014 this = self.sql(expression, "this") 3015 expressions = self.expressions(expression, flat=True) 3016 expressions = f"({expressions})" if expressions else "" 3017 return f"ALTER{compound} SORTKEY {this or expressions}" 3018 3019 def renametable_sql(self, expression: exp.RenameTable) -> str: 3020 if not self.RENAME_TABLE_WITH_DB: 3021 # Remove db from tables 3022 expression = expression.transform( 3023 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 3024 ).assert_is(exp.RenameTable) 3025 this = self.sql(expression, "this") 3026 return f"RENAME TO {this}" 3027 3028 def renamecolumn_sql(self, expression: exp.RenameColumn) -> str: 3029 exists = " IF EXISTS" if expression.args.get("exists") else "" 3030 old_column = self.sql(expression, "this") 3031 new_column = self.sql(expression, "to") 3032 return f"RENAME COLUMN{exists} {old_column} TO {new_column}" 3033 3034 def altertable_sql(self, expression: exp.AlterTable) -> str: 3035 actions = expression.args["actions"] 3036 3037 if isinstance(actions[0], exp.ColumnDef): 3038 actions = self.add_column_sql(expression) 3039 elif isinstance(actions[0], exp.Schema): 3040 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 3041 elif isinstance(actions[0], exp.Delete): 3042 actions = self.expressions(expression, key="actions", flat=True) 3043 else: 3044 actions = self.expressions(expression, key="actions", flat=True) 3045 3046 exists = " IF EXISTS" if expression.args.get("exists") else "" 3047 on_cluster = self.sql(expression, "cluster") 3048 on_cluster = f" {on_cluster}" if on_cluster else "" 3049 only = " ONLY" if expression.args.get("only") else "" 3050 options = self.expressions(expression, key="options") 3051 options = f", {options}" if options else "" 3052 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{options}" 3053 3054 def add_column_sql(self, expression: exp.AlterTable) -> str: 3055 if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD: 3056 return self.expressions( 3057 expression, 3058 key="actions", 3059 prefix="ADD COLUMN ", 3060 ) 3061 return f"ADD {self.expressions(expression, key='actions', flat=True)}" 3062 3063 def droppartition_sql(self, expression: exp.DropPartition) -> str: 3064 expressions = self.expressions(expression) 3065 exists = " IF EXISTS " if expression.args.get("exists") else " " 3066 return f"DROP{exists}{expressions}" 3067 3068 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 3069 return f"ADD {self.expressions(expression)}" 3070 3071 def distinct_sql(self, expression: exp.Distinct) -> str: 3072 this = self.expressions(expression, flat=True) 3073 3074 if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1: 3075 case = exp.case() 3076 for arg in expression.expressions: 3077 case = case.when(arg.is_(exp.null()), exp.null()) 3078 this = self.sql(case.else_(f"({this})")) 3079 3080 this = f" {this}" if this else "" 3081 3082 on = self.sql(expression, "on") 3083 on = f" ON {on}" if on else "" 3084 return f"DISTINCT{this}{on}" 3085 3086 def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: 3087 return self._embed_ignore_nulls(expression, "IGNORE NULLS") 3088 3089 def respectnulls_sql(self, expression: exp.RespectNulls) -> str: 3090 return self._embed_ignore_nulls(expression, "RESPECT NULLS") 3091 3092 def havingmax_sql(self, expression: exp.HavingMax) -> str: 3093 this_sql = self.sql(expression, "this") 3094 expression_sql = self.sql(expression, "expression") 3095 kind = "MAX" if expression.args.get("max") else "MIN" 3096 return f"{this_sql} HAVING {kind} {expression_sql}" 3097 3098 def intdiv_sql(self, expression: exp.IntDiv) -> str: 3099 return self.sql( 3100 exp.Cast( 3101 this=exp.Div(this=expression.this, expression=expression.expression), 3102 to=exp.DataType(this=exp.DataType.Type.INT), 3103 ) 3104 ) 3105 3106 def dpipe_sql(self, expression: exp.DPipe) -> str: 3107 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 3108 return self.func( 3109 "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten()) 3110 ) 3111 return self.binary(expression, "||") 3112 3113 def div_sql(self, expression: exp.Div) -> str: 3114 l, r = expression.left, expression.right 3115 3116 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 3117 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 3118 3119 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 3120 if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES): 3121 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 3122 3123 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 3124 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 3125 return self.sql( 3126 exp.cast( 3127 l / r, 3128 to=exp.DataType.Type.BIGINT, 3129 ) 3130 ) 3131 3132 return self.binary(expression, "/") 3133 3134 def overlaps_sql(self, expression: exp.Overlaps) -> str: 3135 return self.binary(expression, "OVERLAPS") 3136 3137 def distance_sql(self, expression: exp.Distance) -> str: 3138 return self.binary(expression, "<->") 3139 3140 def dot_sql(self, expression: exp.Dot) -> str: 3141 return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}" 3142 3143 def eq_sql(self, expression: exp.EQ) -> str: 3144 return self.binary(expression, "=") 3145 3146 def propertyeq_sql(self, expression: exp.PropertyEQ) -> str: 3147 return self.binary(expression, ":=") 3148 3149 def escape_sql(self, expression: exp.Escape) -> str: 3150 return self.binary(expression, "ESCAPE") 3151 3152 def glob_sql(self, expression: exp.Glob) -> str: 3153 return self.binary(expression, "GLOB") 3154 3155 def gt_sql(self, expression: exp.GT) -> str: 3156 return self.binary(expression, ">") 3157 3158 def gte_sql(self, expression: exp.GTE) -> str: 3159 return self.binary(expression, ">=") 3160 3161 def ilike_sql(self, expression: exp.ILike) -> str: 3162 return self.binary(expression, "ILIKE") 3163 3164 def ilikeany_sql(self, expression: exp.ILikeAny) -> str: 3165 return self.binary(expression, "ILIKE ANY") 3166 3167 def is_sql(self, expression: exp.Is) -> str: 3168 if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean): 3169 return self.sql( 3170 expression.this if expression.expression.this else exp.not_(expression.this) 3171 ) 3172 return self.binary(expression, "IS") 3173 3174 def like_sql(self, expression: exp.Like) -> str: 3175 return self.binary(expression, "LIKE") 3176 3177 def likeany_sql(self, expression: exp.LikeAny) -> str: 3178 return self.binary(expression, "LIKE ANY") 3179 3180 def similarto_sql(self, expression: exp.SimilarTo) -> str: 3181 return self.binary(expression, "SIMILAR TO") 3182 3183 def lt_sql(self, expression: exp.LT) -> str: 3184 return self.binary(expression, "<") 3185 3186 def lte_sql(self, expression: exp.LTE) -> str: 3187 return self.binary(expression, "<=") 3188 3189 def mod_sql(self, expression: exp.Mod) -> str: 3190 return self.binary(expression, "%") 3191 3192 def mul_sql(self, expression: exp.Mul) -> str: 3193 return self.binary(expression, "*") 3194 3195 def neq_sql(self, expression: exp.NEQ) -> str: 3196 return self.binary(expression, "<>") 3197 3198 def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str: 3199 return self.binary(expression, "IS NOT DISTINCT FROM") 3200 3201 def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str: 3202 return self.binary(expression, "IS DISTINCT FROM") 3203 3204 def slice_sql(self, expression: exp.Slice) -> str: 3205 return self.binary(expression, ":") 3206 3207 def sub_sql(self, expression: exp.Sub) -> str: 3208 return self.binary(expression, "-") 3209 3210 def trycast_sql(self, expression: exp.TryCast) -> str: 3211 return self.cast_sql(expression, safe_prefix="TRY_") 3212 3213 def try_sql(self, expression: exp.Try) -> str: 3214 if not self.TRY_SUPPORTED: 3215 self.unsupported("Unsupported TRY function") 3216 return self.sql(expression, "this") 3217 3218 return self.func("TRY", expression.this) 3219 3220 def log_sql(self, expression: exp.Log) -> str: 3221 this = expression.this 3222 expr = expression.expression 3223 3224 if self.dialect.LOG_BASE_FIRST is False: 3225 this, expr = expr, this 3226 elif self.dialect.LOG_BASE_FIRST is None and expr: 3227 if this.name in ("2", "10"): 3228 return self.func(f"LOG{this.name}", expr) 3229 3230 self.unsupported(f"Unsupported logarithm with base {self.sql(this)}") 3231 3232 return self.func("LOG", this, expr) 3233 3234 def use_sql(self, expression: exp.Use) -> str: 3235 kind = self.sql(expression, "kind") 3236 kind = f" {kind}" if kind else "" 3237 this = self.sql(expression, "this") 3238 this = f" {this}" if this else "" 3239 return f"USE{kind}{this}" 3240 3241 def binary(self, expression: exp.Binary, op: str) -> str: 3242 op = self.maybe_comment(op, comments=expression.comments) 3243 return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}" 3244 3245 def function_fallback_sql(self, expression: exp.Func) -> str: 3246 args = [] 3247 3248 for key in expression.arg_types: 3249 arg_value = expression.args.get(key) 3250 3251 if isinstance(arg_value, list): 3252 for value in arg_value: 3253 args.append(value) 3254 elif arg_value is not None: 3255 args.append(arg_value) 3256 3257 if self.normalize_functions: 3258 name = expression.sql_name() 3259 else: 3260 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 3261 3262 return self.func(name, *args) 3263 3264 def func( 3265 self, 3266 name: str, 3267 *args: t.Optional[exp.Expression | str], 3268 prefix: str = "(", 3269 suffix: str = ")", 3270 ) -> str: 3271 return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}" 3272 3273 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 3274 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 3275 if self.pretty and self.too_wide(arg_sqls): 3276 return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 3277 return ", ".join(arg_sqls) 3278 3279 def too_wide(self, args: t.Iterable) -> bool: 3280 return sum(len(arg) for arg in args) > self.max_text_width 3281 3282 def format_time( 3283 self, 3284 expression: exp.Expression, 3285 inverse_time_mapping: t.Optional[t.Dict[str, str]] = None, 3286 inverse_time_trie: t.Optional[t.Dict] = None, 3287 ) -> t.Optional[str]: 3288 return format_time( 3289 self.sql(expression, "format"), 3290 inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING, 3291 inverse_time_trie or self.dialect.INVERSE_TIME_TRIE, 3292 ) 3293 3294 def expressions( 3295 self, 3296 expression: t.Optional[exp.Expression] = None, 3297 key: t.Optional[str] = None, 3298 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 3299 flat: bool = False, 3300 indent: bool = True, 3301 skip_first: bool = False, 3302 skip_last: bool = False, 3303 sep: str = ", ", 3304 prefix: str = "", 3305 dynamic: bool = False, 3306 new_line: bool = False, 3307 ) -> str: 3308 expressions = expression.args.get(key or "expressions") if expression else sqls 3309 3310 if not expressions: 3311 return "" 3312 3313 if flat: 3314 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 3315 3316 num_sqls = len(expressions) 3317 3318 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 3319 if self.pretty and not self.leading_comma: 3320 stripped_sep = sep.strip() 3321 3322 result_sqls = [] 3323 for i, e in enumerate(expressions): 3324 sql = self.sql(e, comment=False) 3325 if not sql: 3326 continue 3327 3328 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 3329 3330 if self.pretty: 3331 if self.leading_comma: 3332 result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}") 3333 else: 3334 result_sqls.append( 3335 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 3336 ) 3337 else: 3338 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 3339 3340 if self.pretty and (not dynamic or self.too_wide(result_sqls)): 3341 if new_line: 3342 result_sqls.insert(0, "") 3343 result_sqls.append("") 3344 result_sql = "\n".join(result_sqls) 3345 else: 3346 result_sql = "".join(result_sqls) 3347 return ( 3348 self.indent(result_sql, skip_first=skip_first, skip_last=skip_last) 3349 if indent 3350 else result_sql 3351 ) 3352 3353 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 3354 flat = flat or isinstance(expression.parent, exp.Properties) 3355 expressions_sql = self.expressions(expression, flat=flat) 3356 if flat: 3357 return f"{op} {expressions_sql}" 3358 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}" 3359 3360 def naked_property(self, expression: exp.Property) -> str: 3361 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 3362 if not property_name: 3363 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 3364 return f"{property_name} {self.sql(expression, 'this')}" 3365 3366 def tag_sql(self, expression: exp.Tag) -> str: 3367 return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}" 3368 3369 def token_sql(self, token_type: TokenType) -> str: 3370 return self.TOKEN_MAPPING.get(token_type, token_type.name) 3371 3372 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 3373 this = self.sql(expression, "this") 3374 expressions = self.no_identify(self.expressions, expression) 3375 expressions = ( 3376 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 3377 ) 3378 return f"{this}{expressions}" 3379 3380 def joinhint_sql(self, expression: exp.JoinHint) -> str: 3381 this = self.sql(expression, "this") 3382 expressions = self.expressions(expression, flat=True) 3383 return f"{this}({expressions})" 3384 3385 def kwarg_sql(self, expression: exp.Kwarg) -> str: 3386 return self.binary(expression, "=>") 3387 3388 def when_sql(self, expression: exp.When) -> str: 3389 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 3390 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 3391 condition = self.sql(expression, "condition") 3392 condition = f" AND {condition}" if condition else "" 3393 3394 then_expression = expression.args.get("then") 3395 if isinstance(then_expression, exp.Insert): 3396 this = self.sql(then_expression, "this") 3397 this = f"INSERT {this}" if this else "INSERT" 3398 then = self.sql(then_expression, "expression") 3399 then = f"{this} VALUES {then}" if then else this 3400 elif isinstance(then_expression, exp.Update): 3401 if isinstance(then_expression.args.get("expressions"), exp.Star): 3402 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 3403 else: 3404 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 3405 else: 3406 then = self.sql(then_expression) 3407 return f"WHEN {matched}{source}{condition} THEN {then}" 3408 3409 def merge_sql(self, expression: exp.Merge) -> str: 3410 table = expression.this 3411 table_alias = "" 3412 3413 hints = table.args.get("hints") 3414 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 3415 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 3416 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 3417 3418 this = self.sql(table) 3419 using = f"USING {self.sql(expression, 'using')}" 3420 on = f"ON {self.sql(expression, 'on')}" 3421 expressions = self.expressions(expression, sep=" ", indent=False) 3422 sep = self.sep() 3423 3424 return self.prepend_ctes( 3425 expression, f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{expressions}" 3426 ) 3427 3428 def tochar_sql(self, expression: exp.ToChar) -> str: 3429 if expression.args.get("format"): 3430 self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function") 3431 3432 return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT)) 3433 3434 def tonumber_sql(self, expression: exp.ToNumber) -> str: 3435 if not self.SUPPORTS_TO_NUMBER: 3436 self.unsupported("Unsupported TO_NUMBER function") 3437 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3438 3439 fmt = expression.args.get("format") 3440 if not fmt: 3441 self.unsupported("Conversion format is required for TO_NUMBER") 3442 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3443 3444 return self.func("TO_NUMBER", expression.this, fmt) 3445 3446 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 3447 this = self.sql(expression, "this") 3448 kind = self.sql(expression, "kind") 3449 settings_sql = self.expressions(expression, key="settings", sep=" ") 3450 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 3451 return f"{this}({kind}{args})" 3452 3453 def dictrange_sql(self, expression: exp.DictRange) -> str: 3454 this = self.sql(expression, "this") 3455 max = self.sql(expression, "max") 3456 min = self.sql(expression, "min") 3457 return f"{this}(MIN {min} MAX {max})" 3458 3459 def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str: 3460 return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}" 3461 3462 def oncluster_sql(self, expression: exp.OnCluster) -> str: 3463 return "" 3464 3465 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3466 expressions = self.expressions(expression, key="expressions", flat=True) 3467 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3468 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3469 buckets = self.sql(expression, "buckets") 3470 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS" 3471 3472 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3473 this = self.sql(expression, "this") 3474 having = self.sql(expression, "having") 3475 3476 if having: 3477 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3478 3479 return self.func("ANY_VALUE", this) 3480 3481 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3482 transform = self.func("TRANSFORM", *expression.expressions) 3483 row_format_before = self.sql(expression, "row_format_before") 3484 row_format_before = f" {row_format_before}" if row_format_before else "" 3485 record_writer = self.sql(expression, "record_writer") 3486 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3487 using = f" USING {self.sql(expression, 'command_script')}" 3488 schema = self.sql(expression, "schema") 3489 schema = f" AS {schema}" if schema else "" 3490 row_format_after = self.sql(expression, "row_format_after") 3491 row_format_after = f" {row_format_after}" if row_format_after else "" 3492 record_reader = self.sql(expression, "record_reader") 3493 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3494 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}" 3495 3496 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3497 key_block_size = self.sql(expression, "key_block_size") 3498 if key_block_size: 3499 return f"KEY_BLOCK_SIZE = {key_block_size}" 3500 3501 using = self.sql(expression, "using") 3502 if using: 3503 return f"USING {using}" 3504 3505 parser = self.sql(expression, "parser") 3506 if parser: 3507 return f"WITH PARSER {parser}" 3508 3509 comment = self.sql(expression, "comment") 3510 if comment: 3511 return f"COMMENT {comment}" 3512 3513 visible = expression.args.get("visible") 3514 if visible is not None: 3515 return "VISIBLE" if visible else "INVISIBLE" 3516 3517 engine_attr = self.sql(expression, "engine_attr") 3518 if engine_attr: 3519 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3520 3521 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3522 if secondary_engine_attr: 3523 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3524 3525 self.unsupported("Unsupported index constraint option.") 3526 return "" 3527 3528 def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str: 3529 enforced = " ENFORCED" if expression.args.get("enforced") else "" 3530 return f"CHECK ({self.sql(expression, 'this')}){enforced}" 3531 3532 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3533 kind = self.sql(expression, "kind") 3534 kind = f"{kind} INDEX" if kind else "INDEX" 3535 this = self.sql(expression, "this") 3536 this = f" {this}" if this else "" 3537 index_type = self.sql(expression, "index_type") 3538 index_type = f" USING {index_type}" if index_type else "" 3539 expressions = self.expressions(expression, flat=True) 3540 expressions = f" ({expressions})" if expressions else "" 3541 options = self.expressions(expression, key="options", sep=" ") 3542 options = f" {options}" if options else "" 3543 return f"{kind}{this}{index_type}{expressions}{options}" 3544 3545 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3546 if self.NVL2_SUPPORTED: 3547 return self.function_fallback_sql(expression) 3548 3549 case = exp.Case().when( 3550 expression.this.is_(exp.null()).not_(copy=False), 3551 expression.args["true"], 3552 copy=False, 3553 ) 3554 else_cond = expression.args.get("false") 3555 if else_cond: 3556 case.else_(else_cond, copy=False) 3557 3558 return self.sql(case) 3559 3560 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3561 this = self.sql(expression, "this") 3562 expr = self.sql(expression, "expression") 3563 iterator = self.sql(expression, "iterator") 3564 condition = self.sql(expression, "condition") 3565 condition = f" IF {condition}" if condition else "" 3566 return f"{this} FOR {expr} IN {iterator}{condition}" 3567 3568 def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str: 3569 return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})" 3570 3571 def opclass_sql(self, expression: exp.Opclass) -> str: 3572 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 3573 3574 def predict_sql(self, expression: exp.Predict) -> str: 3575 model = self.sql(expression, "this") 3576 model = f"MODEL {model}" 3577 table = self.sql(expression, "expression") 3578 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3579 parameters = self.sql(expression, "params_struct") 3580 return self.func("PREDICT", model, table, parameters or None) 3581 3582 def forin_sql(self, expression: exp.ForIn) -> str: 3583 this = self.sql(expression, "this") 3584 expression_sql = self.sql(expression, "expression") 3585 return f"FOR {this} DO {expression_sql}" 3586 3587 def refresh_sql(self, expression: exp.Refresh) -> str: 3588 this = self.sql(expression, "this") 3589 table = "" if isinstance(expression.this, exp.Literal) else "TABLE " 3590 return f"REFRESH {table}{this}" 3591 3592 def operator_sql(self, expression: exp.Operator) -> str: 3593 return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})") 3594 3595 def toarray_sql(self, expression: exp.ToArray) -> str: 3596 arg = expression.this 3597 if not arg.type: 3598 from sqlglot.optimizer.annotate_types import annotate_types 3599 3600 arg = annotate_types(arg) 3601 3602 if arg.is_type(exp.DataType.Type.ARRAY): 3603 return self.sql(arg) 3604 3605 cond_for_null = arg.is_(exp.null()) 3606 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False))) 3607 3608 def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str: 3609 this = expression.this 3610 if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME): 3611 return self.sql(this) 3612 3613 return self.sql(exp.cast(this, exp.DataType.Type.TIME)) 3614 3615 def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str: 3616 this = expression.this 3617 if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP): 3618 return self.sql(this) 3619 3620 return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP)) 3621 3622 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3623 this = expression.this 3624 time_format = self.format_time(expression) 3625 3626 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3627 return self.sql( 3628 exp.cast( 3629 exp.StrToTime(this=this, format=expression.args["format"]), 3630 exp.DataType.Type.DATE, 3631 ) 3632 ) 3633 3634 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3635 return self.sql(this) 3636 3637 return self.sql(exp.cast(this, exp.DataType.Type.DATE)) 3638 3639 def unixdate_sql(self, expression: exp.UnixDate) -> str: 3640 return self.sql( 3641 exp.func( 3642 "DATEDIFF", 3643 expression.this, 3644 exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE), 3645 "day", 3646 ) 3647 ) 3648 3649 def lastday_sql(self, expression: exp.LastDay) -> str: 3650 if self.LAST_DAY_SUPPORTS_DATE_PART: 3651 return self.function_fallback_sql(expression) 3652 3653 unit = expression.text("unit") 3654 if unit and unit != "MONTH": 3655 self.unsupported("Date parts are not supported in LAST_DAY.") 3656 3657 return self.func("LAST_DAY", expression.this) 3658 3659 def dateadd_sql(self, expression: exp.DateAdd) -> str: 3660 from sqlglot.dialects.dialect import unit_to_str 3661 3662 return self.func( 3663 "DATE_ADD", expression.this, expression.expression, unit_to_str(expression) 3664 ) 3665 3666 def arrayany_sql(self, expression: exp.ArrayAny) -> str: 3667 if self.CAN_IMPLEMENT_ARRAY_ANY: 3668 filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression) 3669 filtered_not_empty = exp.ArraySize(this=filtered).neq(0) 3670 original_is_empty = exp.ArraySize(this=expression.this).eq(0) 3671 return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty))) 3672 3673 from sqlglot.dialects import Dialect 3674 3675 # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect 3676 if self.dialect.__class__ != Dialect: 3677 self.unsupported("ARRAY_ANY is unsupported") 3678 3679 return self.function_fallback_sql(expression) 3680 3681 def generateseries_sql(self, expression: exp.GenerateSeries) -> str: 3682 expression.set("is_end_exclusive", None) 3683 return self.function_fallback_sql(expression) 3684 3685 def struct_sql(self, expression: exp.Struct) -> str: 3686 expression.set( 3687 "expressions", 3688 [ 3689 exp.alias_(e.expression, e.name if e.this.is_string else e.this) 3690 if isinstance(e, exp.PropertyEQ) 3691 else e 3692 for e in expression.expressions 3693 ], 3694 ) 3695 3696 return self.function_fallback_sql(expression) 3697 3698 def partitionrange_sql(self, expression: exp.PartitionRange) -> str: 3699 low = self.sql(expression, "this") 3700 high = self.sql(expression, "expression") 3701 3702 return f"{low} TO {high}" 3703 3704 def truncatetable_sql(self, expression: exp.TruncateTable) -> str: 3705 target = "DATABASE" if expression.args.get("is_database") else "TABLE" 3706 tables = f" {self.expressions(expression)}" 3707 3708 exists = " IF EXISTS" if expression.args.get("exists") else "" 3709 3710 on_cluster = self.sql(expression, "cluster") 3711 on_cluster = f" {on_cluster}" if on_cluster else "" 3712 3713 identity = self.sql(expression, "identity") 3714 identity = f" {identity} IDENTITY" if identity else "" 3715 3716 option = self.sql(expression, "option") 3717 option = f" {option}" if option else "" 3718 3719 partition = self.sql(expression, "partition") 3720 partition = f" {partition}" if partition else "" 3721 3722 return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}" 3723 3724 # This transpiles T-SQL's CONVERT function 3725 # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16 3726 def convert_sql(self, expression: exp.Convert) -> str: 3727 to = expression.this 3728 value = expression.expression 3729 style = expression.args.get("style") 3730 safe = expression.args.get("safe") 3731 strict = expression.args.get("strict") 3732 3733 if not to or not value: 3734 return "" 3735 3736 # Retrieve length of datatype and override to default if not specified 3737 if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3738 to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False) 3739 3740 transformed: t.Optional[exp.Expression] = None 3741 cast = exp.Cast if strict else exp.TryCast 3742 3743 # Check whether a conversion with format (T-SQL calls this 'style') is applicable 3744 if isinstance(style, exp.Literal) and style.is_int: 3745 from sqlglot.dialects.tsql import TSQL 3746 3747 style_value = style.name 3748 converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value) 3749 if not converted_style: 3750 self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}") 3751 3752 fmt = exp.Literal.string(converted_style) 3753 3754 if to.this == exp.DataType.Type.DATE: 3755 transformed = exp.StrToDate(this=value, format=fmt) 3756 elif to.this == exp.DataType.Type.DATETIME: 3757 transformed = exp.StrToTime(this=value, format=fmt) 3758 elif to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3759 transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe) 3760 elif to.this == exp.DataType.Type.TEXT: 3761 transformed = exp.TimeToStr(this=value, format=fmt) 3762 3763 if not transformed: 3764 transformed = cast(this=value, to=to, safe=safe) 3765 3766 return self.sql(transformed) 3767 3768 def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str: 3769 this = expression.this 3770 if isinstance(this, exp.JSONPathWildcard): 3771 this = self.json_path_part(this) 3772 return f".{this}" if this else "" 3773 3774 if exp.SAFE_IDENTIFIER_RE.match(this): 3775 return f".{this}" 3776 3777 this = self.json_path_part(this) 3778 return f"[{this}]" if self.JSON_PATH_BRACKETED_KEY_SUPPORTED else f".{this}" 3779 3780 def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str: 3781 this = self.json_path_part(expression.this) 3782 return f"[{this}]" if this else "" 3783 3784 def _simplify_unless_literal(self, expression: E) -> E: 3785 if not isinstance(expression, exp.Literal): 3786 from sqlglot.optimizer.simplify import simplify 3787 3788 expression = simplify(expression, dialect=self.dialect) 3789 3790 return expression 3791 3792 def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str: 3793 if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"): 3794 # The first modifier here will be the one closest to the AggFunc's arg 3795 mods = sorted( 3796 expression.find_all(exp.HavingMax, exp.Order, exp.Limit), 3797 key=lambda x: 0 3798 if isinstance(x, exp.HavingMax) 3799 else (1 if isinstance(x, exp.Order) else 2), 3800 ) 3801 3802 if mods: 3803 mod = mods[0] 3804 this = expression.__class__(this=mod.this.copy()) 3805 this.meta["inline"] = True 3806 mod.this.replace(this) 3807 return self.sql(expression.this) 3808 3809 agg_func = expression.find(exp.AggFunc) 3810 3811 if agg_func: 3812 return self.sql(agg_func)[:-1] + f" {text})" 3813 3814 return f"{self.sql(expression, 'this')} {text}" 3815 3816 def _replace_line_breaks(self, string: str) -> str: 3817 """We don't want to extra indent line breaks so we temporarily replace them with sentinels.""" 3818 if self.pretty: 3819 return string.replace("\n", self.SENTINEL_LINE_BREAK) 3820 return string 3821 3822 def copyparameter_sql(self, expression: exp.CopyParameter) -> str: 3823 option = self.sql(expression, "this") 3824 3825 if option.upper() == "FILE_FORMAT": 3826 values = self.expressions(expression, key="expression", flat=True, sep=" ") 3827 return f"{option} = ({values})" 3828 3829 value = self.sql(expression, "expression") 3830 3831 if not value: 3832 return option 3833 3834 op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " " 3835 3836 return f"{option}{op}{value}" 3837 3838 def credentials_sql(self, expression: exp.Credentials) -> str: 3839 cred_expr = expression.args.get("credentials") 3840 if isinstance(cred_expr, exp.Literal): 3841 # Redshift case: CREDENTIALS <string> 3842 credentials = self.sql(expression, "credentials") 3843 credentials = f"CREDENTIALS {credentials}" if credentials else "" 3844 else: 3845 # Snowflake case: CREDENTIALS = (...) 3846 credentials = self.expressions(expression, key="credentials", flat=True, sep=" ") 3847 credentials = f"CREDENTIALS = ({credentials})" if credentials else "" 3848 3849 storage = self.sql(expression, "storage") 3850 3851 encryption = self.expressions(expression, key="encryption", flat=True, sep=" ") 3852 encryption = f" ENCRYPTION = ({encryption})" if encryption else "" 3853 3854 iam_role = self.sql(expression, "iam_role") 3855 iam_role = f"IAM_ROLE {iam_role}" if iam_role else "" 3856 3857 region = self.sql(expression, "region") 3858 region = f" REGION {region}" if region else "" 3859 3860 return f"{credentials}{storage}{encryption}{iam_role}{region}" 3861 3862 def copy_sql(self, expression: exp.Copy) -> str: 3863 this = self.sql(expression, "this") 3864 this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}" 3865 3866 credentials = self.sql(expression, "credentials") 3867 credentials = self.seg(credentials) if credentials else "" 3868 kind = self.seg("FROM" if expression.args.get("kind") else "TO") 3869 files = self.expressions(expression, key="files", flat=True) 3870 3871 sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " " 3872 params = self.expressions( 3873 expression, 3874 key="params", 3875 sep=sep, 3876 new_line=True, 3877 skip_last=True, 3878 skip_first=True, 3879 indent=self.COPY_PARAMS_ARE_WRAPPED, 3880 ) 3881 3882 if params: 3883 if self.COPY_PARAMS_ARE_WRAPPED: 3884 params = f" WITH ({params})" 3885 elif not self.pretty: 3886 params = f" {params}" 3887 3888 return f"COPY{this}{kind} {files}{credentials}{params}" 3889 3890 def semicolon_sql(self, expression: exp.Semicolon) -> str: 3891 return ""
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.AllowedValuesProperty: lambda self, 79 e: f"ALLOWED_VALUES {self.expressions(e, flat=True)}", 80 exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}", 81 exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}", 82 exp.CaseSpecificColumnConstraint: lambda _, 83 e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC", 84 exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}", 85 exp.CharacterSetProperty: lambda self, 86 e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}", 87 exp.ClusteredColumnConstraint: lambda self, 88 e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})", 89 exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}", 90 exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}", 91 exp.CopyGrantsProperty: lambda *_: "COPY GRANTS", 92 exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}", 93 exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}", 94 exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}", 95 exp.EphemeralColumnConstraint: lambda self, 96 e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}", 97 exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}", 98 exp.ExecuteAsProperty: lambda self, e: self.naked_property(e), 99 exp.ExternalProperty: lambda *_: "EXTERNAL", 100 exp.GlobalProperty: lambda *_: "GLOBAL", 101 exp.HeapProperty: lambda *_: "HEAP", 102 exp.IcebergProperty: lambda *_: "ICEBERG", 103 exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})", 104 exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}", 105 exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}", 106 exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}", 107 exp.JSONExtract: lambda self, e: self.func( 108 "JSON_EXTRACT", e.this, e.expression, *e.expressions 109 ), 110 exp.JSONExtractScalar: lambda self, e: self.func( 111 "JSON_EXTRACT_SCALAR", e.this, e.expression, *e.expressions 112 ), 113 exp.LanguageProperty: lambda self, e: self.naked_property(e), 114 exp.LocationProperty: lambda self, e: self.naked_property(e), 115 exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG", 116 exp.MaterializedProperty: lambda *_: "MATERIALIZED", 117 exp.NonClusteredColumnConstraint: lambda self, 118 e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})", 119 exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX", 120 exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION", 121 exp.OnCommitProperty: lambda _, 122 e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS", 123 exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}", 124 exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}", 125 exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}", 126 exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}", 127 exp.RemoteWithConnectionModelProperty: lambda self, 128 e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}", 129 exp.ReturnsProperty: lambda self, e: self.naked_property(e), 130 exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}", 131 exp.SetConfigProperty: lambda self, e: self.sql(e, "this"), 132 exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET", 133 exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}", 134 exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}", 135 exp.SqlReadWriteProperty: lambda _, e: e.name, 136 exp.SqlSecurityProperty: lambda _, 137 e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}", 138 exp.StabilityProperty: lambda _, e: e.name, 139 exp.TemporaryProperty: lambda *_: "TEMPORARY", 140 exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}", 141 exp.Timestamp: lambda self, e: self.func("TIMESTAMP", e.this, e.expression), 142 exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}", 143 exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}", 144 exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions), 145 exp.TransientProperty: lambda *_: "TRANSIENT", 146 exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE", 147 exp.UnloggedProperty: lambda *_: "UNLOGGED", 148 exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]), 149 exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}", 150 exp.VolatileProperty: lambda *_: "VOLATILE", 151 exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}", 152 exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}", 153 } 154 155 # Whether null ordering is supported in order by 156 # True: Full Support, None: No support, False: No support in window specifications 157 NULL_ORDERING_SUPPORTED: t.Optional[bool] = True 158 159 # Whether ignore nulls is inside the agg or outside. 160 # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER 161 IGNORE_NULLS_IN_FUNC = False 162 163 # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported 164 LOCKING_READS_SUPPORTED = False 165 166 # Always do union distinct or union all 167 EXPLICIT_UNION = False 168 169 # Wrap derived values in parens, usually standard but spark doesn't support it 170 WRAP_DERIVED_VALUES = True 171 172 # Whether create function uses an AS before the RETURN 173 CREATE_FUNCTION_RETURN_AS = True 174 175 # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed 176 MATCHED_BY_SOURCE = True 177 178 # Whether the INTERVAL expression works only with values like '1 day' 179 SINGLE_STRING_INTERVAL = False 180 181 # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs 182 INTERVAL_ALLOWS_PLURAL_FORM = True 183 184 # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH") 185 LIMIT_FETCH = "ALL" 186 187 # Whether limit and fetch allows expresions or just limits 188 LIMIT_ONLY_LITERALS = False 189 190 # Whether a table is allowed to be renamed with a db 191 RENAME_TABLE_WITH_DB = True 192 193 # The separator for grouping sets and rollups 194 GROUPINGS_SEP = "," 195 196 # The string used for creating an index on a table 197 INDEX_ON = "ON" 198 199 # Whether join hints should be generated 200 JOIN_HINTS = True 201 202 # Whether table hints should be generated 203 TABLE_HINTS = True 204 205 # Whether query hints should be generated 206 QUERY_HINTS = True 207 208 # What kind of separator to use for query hints 209 QUERY_HINT_SEP = ", " 210 211 # Whether comparing against booleans (e.g. x IS TRUE) is supported 212 IS_BOOL_ALLOWED = True 213 214 # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement 215 DUPLICATE_KEY_UPDATE_WITH_SET = True 216 217 # Whether to generate the limit as TOP <value> instead of LIMIT <value> 218 LIMIT_IS_TOP = False 219 220 # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ... 221 RETURNING_END = True 222 223 # Whether to generate the (+) suffix for columns used in old-style join conditions 224 COLUMN_JOIN_MARKS_SUPPORTED = False 225 226 # Whether to generate an unquoted value for EXTRACT's date part argument 227 EXTRACT_ALLOWS_QUOTES = True 228 229 # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax 230 TZ_TO_WITH_TIME_ZONE = False 231 232 # Whether the NVL2 function is supported 233 NVL2_SUPPORTED = True 234 235 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax 236 SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE") 237 238 # Whether VALUES statements can be used as derived tables. 239 # MySQL 5 and Redshift do not allow this, so when False, it will convert 240 # SELECT * VALUES into SELECT UNION 241 VALUES_AS_TABLE = True 242 243 # Whether the word COLUMN is included when adding a column with ALTER TABLE 244 ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True 245 246 # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery) 247 UNNEST_WITH_ORDINALITY = True 248 249 # Whether FILTER (WHERE cond) can be used for conditional aggregation 250 AGGREGATE_FILTER_SUPPORTED = True 251 252 # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds 253 SEMI_ANTI_JOIN_WITH_SIDE = True 254 255 # Whether to include the type of a computed column in the CREATE DDL 256 COMPUTED_COLUMN_WITH_TYPE = True 257 258 # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY 259 SUPPORTS_TABLE_COPY = True 260 261 # Whether parentheses are required around the table sample's expression 262 TABLESAMPLE_REQUIRES_PARENS = True 263 264 # Whether a table sample clause's size needs to be followed by the ROWS keyword 265 TABLESAMPLE_SIZE_IS_ROWS = True 266 267 # The keyword(s) to use when generating a sample clause 268 TABLESAMPLE_KEYWORDS = "TABLESAMPLE" 269 270 # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI 271 TABLESAMPLE_WITH_METHOD = True 272 273 # The keyword to use when specifying the seed of a sample clause 274 TABLESAMPLE_SEED_KEYWORD = "SEED" 275 276 # Whether COLLATE is a function instead of a binary operator 277 COLLATE_IS_FUNC = False 278 279 # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle) 280 DATA_TYPE_SPECIFIERS_ALLOWED = False 281 282 # Whether conditions require booleans WHERE x = 0 vs WHERE x 283 ENSURE_BOOLS = False 284 285 # Whether the "RECURSIVE" keyword is required when defining recursive CTEs 286 CTE_RECURSIVE_KEYWORD_REQUIRED = True 287 288 # Whether CONCAT requires >1 arguments 289 SUPPORTS_SINGLE_ARG_CONCAT = True 290 291 # Whether LAST_DAY function supports a date part argument 292 LAST_DAY_SUPPORTS_DATE_PART = True 293 294 # Whether named columns are allowed in table aliases 295 SUPPORTS_TABLE_ALIAS_COLUMNS = True 296 297 # Whether UNPIVOT aliases are Identifiers (False means they're Literals) 298 UNPIVOT_ALIASES_ARE_IDENTIFIERS = True 299 300 # What delimiter to use for separating JSON key/value pairs 301 JSON_KEY_VALUE_PAIR_SEP = ":" 302 303 # INSERT OVERWRITE TABLE x override 304 INSERT_OVERWRITE = " OVERWRITE TABLE" 305 306 # Whether the SELECT .. INTO syntax is used instead of CTAS 307 SUPPORTS_SELECT_INTO = False 308 309 # Whether UNLOGGED tables can be created 310 SUPPORTS_UNLOGGED_TABLES = False 311 312 # Whether the CREATE TABLE LIKE statement is supported 313 SUPPORTS_CREATE_TABLE_LIKE = True 314 315 # Whether the LikeProperty needs to be specified inside of the schema clause 316 LIKE_PROPERTY_INSIDE_SCHEMA = False 317 318 # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be 319 # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args 320 MULTI_ARG_DISTINCT = True 321 322 # Whether the JSON extraction operators expect a value of type JSON 323 JSON_TYPE_REQUIRED_FOR_EXTRACTION = False 324 325 # Whether bracketed keys like ["foo"] are supported in JSON paths 326 JSON_PATH_BRACKETED_KEY_SUPPORTED = True 327 328 # Whether to escape keys using single quotes in JSON paths 329 JSON_PATH_SINGLE_QUOTE_ESCAPE = False 330 331 # The JSONPathPart expressions supported by this dialect 332 SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy() 333 334 # Whether any(f(x) for x in array) can be implemented by this dialect 335 CAN_IMPLEMENT_ARRAY_ANY = False 336 337 # Whether the function TO_NUMBER is supported 338 SUPPORTS_TO_NUMBER = True 339 340 # Whether or not union modifiers apply to the outer union or select. 341 # SELECT * FROM x UNION SELECT * FROM y LIMIT 1 342 # True means limit 1 happens after the union, False means it it happens on y. 343 OUTER_UNION_MODIFIERS = True 344 345 # Whether parameters from COPY statement are wrapped in parentheses 346 COPY_PARAMS_ARE_WRAPPED = True 347 348 # Whether values of params are set with "=" token or empty space 349 COPY_PARAMS_EQ_REQUIRED = False 350 351 # Whether COPY statement has INTO keyword 352 COPY_HAS_INTO_KEYWORD = True 353 354 # Whether the conditional TRY(expression) function is supported 355 TRY_SUPPORTED = True 356 357 # The keyword to use when generating a star projection with excluded columns 358 STAR_EXCEPT = "EXCEPT" 359 360 # The HEX function name 361 HEX_FUNC = "HEX" 362 363 TYPE_MAPPING = { 364 exp.DataType.Type.NCHAR: "CHAR", 365 exp.DataType.Type.NVARCHAR: "VARCHAR", 366 exp.DataType.Type.MEDIUMTEXT: "TEXT", 367 exp.DataType.Type.LONGTEXT: "TEXT", 368 exp.DataType.Type.TINYTEXT: "TEXT", 369 exp.DataType.Type.MEDIUMBLOB: "BLOB", 370 exp.DataType.Type.LONGBLOB: "BLOB", 371 exp.DataType.Type.TINYBLOB: "BLOB", 372 exp.DataType.Type.INET: "INET", 373 exp.DataType.Type.ROWVERSION: "VARBINARY", 374 } 375 376 TIME_PART_SINGULARS = { 377 "MICROSECONDS": "MICROSECOND", 378 "SECONDS": "SECOND", 379 "MINUTES": "MINUTE", 380 "HOURS": "HOUR", 381 "DAYS": "DAY", 382 "WEEKS": "WEEK", 383 "MONTHS": "MONTH", 384 "QUARTERS": "QUARTER", 385 "YEARS": "YEAR", 386 } 387 388 AFTER_HAVING_MODIFIER_TRANSFORMS = { 389 "cluster": lambda self, e: self.sql(e, "cluster"), 390 "distribute": lambda self, e: self.sql(e, "distribute"), 391 "qualify": lambda self, e: self.sql(e, "qualify"), 392 "sort": lambda self, e: self.sql(e, "sort"), 393 "windows": lambda self, e: ( 394 self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True) 395 if e.args.get("windows") 396 else "" 397 ), 398 } 399 400 TOKEN_MAPPING: t.Dict[TokenType, str] = {} 401 402 STRUCT_DELIMITER = ("<", ">") 403 404 PARAMETER_TOKEN = "@" 405 NAMED_PLACEHOLDER_TOKEN = ":" 406 407 PROPERTIES_LOCATION = { 408 exp.AllowedValuesProperty: exp.Properties.Location.POST_SCHEMA, 409 exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE, 410 exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA, 411 exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA, 412 exp.BackupProperty: exp.Properties.Location.POST_SCHEMA, 413 exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME, 414 exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA, 415 exp.ChecksumProperty: exp.Properties.Location.POST_NAME, 416 exp.CollateProperty: exp.Properties.Location.POST_SCHEMA, 417 exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA, 418 exp.Cluster: exp.Properties.Location.POST_SCHEMA, 419 exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA, 420 exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME, 421 exp.DefinerProperty: exp.Properties.Location.POST_CREATE, 422 exp.DictRange: exp.Properties.Location.POST_SCHEMA, 423 exp.DictProperty: exp.Properties.Location.POST_SCHEMA, 424 exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA, 425 exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA, 426 exp.EngineProperty: exp.Properties.Location.POST_SCHEMA, 427 exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA, 428 exp.ExternalProperty: exp.Properties.Location.POST_CREATE, 429 exp.FallbackProperty: exp.Properties.Location.POST_NAME, 430 exp.FileFormatProperty: exp.Properties.Location.POST_WITH, 431 exp.FreespaceProperty: exp.Properties.Location.POST_NAME, 432 exp.GlobalProperty: exp.Properties.Location.POST_CREATE, 433 exp.HeapProperty: exp.Properties.Location.POST_WITH, 434 exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA, 435 exp.IcebergProperty: exp.Properties.Location.POST_CREATE, 436 exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA, 437 exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME, 438 exp.JournalProperty: exp.Properties.Location.POST_NAME, 439 exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA, 440 exp.LikeProperty: exp.Properties.Location.POST_SCHEMA, 441 exp.LocationProperty: exp.Properties.Location.POST_SCHEMA, 442 exp.LockProperty: exp.Properties.Location.POST_SCHEMA, 443 exp.LockingProperty: exp.Properties.Location.POST_ALIAS, 444 exp.LogProperty: exp.Properties.Location.POST_NAME, 445 exp.MaterializedProperty: exp.Properties.Location.POST_CREATE, 446 exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME, 447 exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION, 448 exp.OnProperty: exp.Properties.Location.POST_SCHEMA, 449 exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION, 450 exp.Order: exp.Properties.Location.POST_SCHEMA, 451 exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA, 452 exp.PartitionedByProperty: exp.Properties.Location.POST_WITH, 453 exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA, 454 exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA, 455 exp.Property: exp.Properties.Location.POST_WITH, 456 exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA, 457 exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA, 458 exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA, 459 exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA, 460 exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA, 461 exp.SampleProperty: exp.Properties.Location.POST_SCHEMA, 462 exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA, 463 exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA, 464 exp.Set: exp.Properties.Location.POST_SCHEMA, 465 exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA, 466 exp.SetProperty: exp.Properties.Location.POST_CREATE, 467 exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA, 468 exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION, 469 exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION, 470 exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA, 471 exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA, 472 exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE, 473 exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA, 474 exp.TemporaryProperty: exp.Properties.Location.POST_CREATE, 475 exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA, 476 exp.TransientProperty: exp.Properties.Location.POST_CREATE, 477 exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA, 478 exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA, 479 exp.UnloggedProperty: exp.Properties.Location.POST_CREATE, 480 exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA, 481 exp.VolatileProperty: exp.Properties.Location.POST_CREATE, 482 exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION, 483 exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME, 484 exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA, 485 } 486 487 # Keywords that can't be used as unquoted identifier names 488 RESERVED_KEYWORDS: t.Set[str] = set() 489 490 # Expressions whose comments are separated from them for better formatting 491 WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 492 exp.Create, 493 exp.Delete, 494 exp.Drop, 495 exp.From, 496 exp.Insert, 497 exp.Join, 498 exp.Select, 499 exp.Union, 500 exp.Update, 501 exp.Where, 502 exp.With, 503 ) 504 505 # Expressions that should not have their comments generated in maybe_comment 506 EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 507 exp.Binary, 508 exp.Union, 509 ) 510 511 # Expressions that can remain unwrapped when appearing in the context of an INTERVAL 512 UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = ( 513 exp.Column, 514 exp.Literal, 515 exp.Neg, 516 exp.Paren, 517 ) 518 519 PARAMETERIZABLE_TEXT_TYPES = { 520 exp.DataType.Type.NVARCHAR, 521 exp.DataType.Type.VARCHAR, 522 exp.DataType.Type.CHAR, 523 exp.DataType.Type.NCHAR, 524 } 525 526 # Expressions that need to have all CTEs under them bubbled up to them 527 EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set() 528 529 SENTINEL_LINE_BREAK = "__SQLGLOT__LB__" 530 531 __slots__ = ( 532 "pretty", 533 "identify", 534 "normalize", 535 "pad", 536 "_indent", 537 "normalize_functions", 538 "unsupported_level", 539 "max_unsupported", 540 "leading_comma", 541 "max_text_width", 542 "comments", 543 "dialect", 544 "unsupported_messages", 545 "_escaped_quote_end", 546 "_escaped_identifier_end", 547 "_next_name", 548 ) 549 550 def __init__( 551 self, 552 pretty: t.Optional[bool] = None, 553 identify: str | bool = False, 554 normalize: bool = False, 555 pad: int = 2, 556 indent: int = 2, 557 normalize_functions: t.Optional[str | bool] = None, 558 unsupported_level: ErrorLevel = ErrorLevel.WARN, 559 max_unsupported: int = 3, 560 leading_comma: bool = False, 561 max_text_width: int = 80, 562 comments: bool = True, 563 dialect: DialectType = None, 564 ): 565 import sqlglot 566 from sqlglot.dialects import Dialect 567 568 self.pretty = pretty if pretty is not None else sqlglot.pretty 569 self.identify = identify 570 self.normalize = normalize 571 self.pad = pad 572 self._indent = indent 573 self.unsupported_level = unsupported_level 574 self.max_unsupported = max_unsupported 575 self.leading_comma = leading_comma 576 self.max_text_width = max_text_width 577 self.comments = comments 578 self.dialect = Dialect.get_or_raise(dialect) 579 580 # This is both a Dialect property and a Generator argument, so we prioritize the latter 581 self.normalize_functions = ( 582 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 583 ) 584 585 self.unsupported_messages: t.List[str] = [] 586 self._escaped_quote_end: str = ( 587 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 588 ) 589 self._escaped_identifier_end: str = ( 590 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 591 ) 592 593 self._next_name = name_sequence("_t") 594 595 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 596 """ 597 Generates the SQL string corresponding to the given syntax tree. 598 599 Args: 600 expression: The syntax tree. 601 copy: Whether to copy the expression. The generator performs mutations so 602 it is safer to copy. 603 604 Returns: 605 The SQL string corresponding to `expression`. 606 """ 607 if copy: 608 expression = expression.copy() 609 610 expression = self.preprocess(expression) 611 612 self.unsupported_messages = [] 613 sql = self.sql(expression).strip() 614 615 if self.pretty: 616 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 617 618 if self.unsupported_level == ErrorLevel.IGNORE: 619 return sql 620 621 if self.unsupported_level == ErrorLevel.WARN: 622 for msg in self.unsupported_messages: 623 logger.warning(msg) 624 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 625 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 626 627 return sql 628 629 def preprocess(self, expression: exp.Expression) -> exp.Expression: 630 """Apply generic preprocessing transformations to a given expression.""" 631 if ( 632 not expression.parent 633 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 634 and any(node.parent is not expression for node in expression.find_all(exp.With)) 635 ): 636 from sqlglot.transforms import move_ctes_to_top_level 637 638 expression = move_ctes_to_top_level(expression) 639 640 if self.ENSURE_BOOLS: 641 from sqlglot.transforms import ensure_bools 642 643 expression = ensure_bools(expression) 644 645 return expression 646 647 def unsupported(self, message: str) -> None: 648 if self.unsupported_level == ErrorLevel.IMMEDIATE: 649 raise UnsupportedError(message) 650 self.unsupported_messages.append(message) 651 652 def sep(self, sep: str = " ") -> str: 653 return f"{sep.strip()}\n" if self.pretty else sep 654 655 def seg(self, sql: str, sep: str = " ") -> str: 656 return f"{self.sep(sep)}{sql}" 657 658 def pad_comment(self, comment: str) -> str: 659 comment = " " + comment if comment[0].strip() else comment 660 comment = comment + " " if comment[-1].strip() else comment 661 return comment 662 663 def maybe_comment( 664 self, 665 sql: str, 666 expression: t.Optional[exp.Expression] = None, 667 comments: t.Optional[t.List[str]] = None, 668 separated: bool = False, 669 ) -> str: 670 comments = ( 671 ((expression and expression.comments) if comments is None else comments) # type: ignore 672 if self.comments 673 else None 674 ) 675 676 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 677 return sql 678 679 comments_sql = " ".join( 680 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 681 ) 682 683 if not comments_sql: 684 return sql 685 686 comments_sql = self._replace_line_breaks(comments_sql) 687 688 if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS): 689 return ( 690 f"{self.sep()}{comments_sql}{sql}" 691 if not sql or sql[0].isspace() 692 else f"{comments_sql}{self.sep()}{sql}" 693 ) 694 695 return f"{sql} {comments_sql}" 696 697 def wrap(self, expression: exp.Expression | str) -> str: 698 this_sql = ( 699 self.sql(expression) 700 if isinstance(expression, exp.UNWRAPPED_QUERIES) 701 else self.sql(expression, "this") 702 ) 703 if not this_sql: 704 return "()" 705 706 this_sql = self.indent(this_sql, level=1, pad=0) 707 return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}" 708 709 def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str: 710 original = self.identify 711 self.identify = False 712 result = func(*args, **kwargs) 713 self.identify = original 714 return result 715 716 def normalize_func(self, name: str) -> str: 717 if self.normalize_functions == "upper" or self.normalize_functions is True: 718 return name.upper() 719 if self.normalize_functions == "lower": 720 return name.lower() 721 return name 722 723 def indent( 724 self, 725 sql: str, 726 level: int = 0, 727 pad: t.Optional[int] = None, 728 skip_first: bool = False, 729 skip_last: bool = False, 730 ) -> str: 731 if not self.pretty or not sql: 732 return sql 733 734 pad = self.pad if pad is None else pad 735 lines = sql.split("\n") 736 737 return "\n".join( 738 ( 739 line 740 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 741 else f"{' ' * (level * self._indent + pad)}{line}" 742 ) 743 for i, line in enumerate(lines) 744 ) 745 746 def sql( 747 self, 748 expression: t.Optional[str | exp.Expression], 749 key: t.Optional[str] = None, 750 comment: bool = True, 751 ) -> str: 752 if not expression: 753 return "" 754 755 if isinstance(expression, str): 756 return expression 757 758 if key: 759 value = expression.args.get(key) 760 if value: 761 return self.sql(value) 762 return "" 763 764 transform = self.TRANSFORMS.get(expression.__class__) 765 766 if callable(transform): 767 sql = transform(self, expression) 768 elif isinstance(expression, exp.Expression): 769 exp_handler_name = f"{expression.key}_sql" 770 771 if hasattr(self, exp_handler_name): 772 sql = getattr(self, exp_handler_name)(expression) 773 elif isinstance(expression, exp.Func): 774 sql = self.function_fallback_sql(expression) 775 elif isinstance(expression, exp.Property): 776 sql = self.property_sql(expression) 777 else: 778 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 779 else: 780 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 781 782 return self.maybe_comment(sql, expression) if self.comments and comment else sql 783 784 def uncache_sql(self, expression: exp.Uncache) -> str: 785 table = self.sql(expression, "this") 786 exists_sql = " IF EXISTS" if expression.args.get("exists") else "" 787 return f"UNCACHE TABLE{exists_sql} {table}" 788 789 def cache_sql(self, expression: exp.Cache) -> str: 790 lazy = " LAZY" if expression.args.get("lazy") else "" 791 table = self.sql(expression, "this") 792 options = expression.args.get("options") 793 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 794 sql = self.sql(expression, "expression") 795 sql = f" AS{self.sep()}{sql}" if sql else "" 796 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 797 return self.prepend_ctes(expression, sql) 798 799 def characterset_sql(self, expression: exp.CharacterSet) -> str: 800 if isinstance(expression.parent, exp.Cast): 801 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 802 default = "DEFAULT " if expression.args.get("default") else "" 803 return f"{default}CHARACTER SET={self.sql(expression, 'this')}" 804 805 def column_parts(self, expression: exp.Column) -> str: 806 return ".".join( 807 self.sql(part) 808 for part in ( 809 expression.args.get("catalog"), 810 expression.args.get("db"), 811 expression.args.get("table"), 812 expression.args.get("this"), 813 ) 814 if part 815 ) 816 817 def column_sql(self, expression: exp.Column) -> str: 818 join_mark = " (+)" if expression.args.get("join_mark") else "" 819 820 if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED: 821 join_mark = "" 822 self.unsupported("Outer join syntax using the (+) operator is not supported.") 823 824 return f"{self.column_parts(expression)}{join_mark}" 825 826 def columnposition_sql(self, expression: exp.ColumnPosition) -> str: 827 this = self.sql(expression, "this") 828 this = f" {this}" if this else "" 829 position = self.sql(expression, "position") 830 return f"{position}{this}" 831 832 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 833 column = self.sql(expression, "this") 834 kind = self.sql(expression, "kind") 835 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 836 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 837 kind = f"{sep}{kind}" if kind else "" 838 constraints = f" {constraints}" if constraints else "" 839 position = self.sql(expression, "position") 840 position = f" {position}" if position else "" 841 842 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 843 kind = "" 844 845 return f"{exists}{column}{kind}{constraints}{position}" 846 847 def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str: 848 this = self.sql(expression, "this") 849 kind_sql = self.sql(expression, "kind").strip() 850 return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql 851 852 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 853 this = self.sql(expression, "this") 854 if expression.args.get("not_null"): 855 persisted = " PERSISTED NOT NULL" 856 elif expression.args.get("persisted"): 857 persisted = " PERSISTED" 858 else: 859 persisted = "" 860 return f"AS {this}{persisted}" 861 862 def autoincrementcolumnconstraint_sql(self, _) -> str: 863 return self.token_sql(TokenType.AUTO_INCREMENT) 864 865 def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str: 866 if isinstance(expression.this, list): 867 this = self.wrap(self.expressions(expression, key="this", flat=True)) 868 else: 869 this = self.sql(expression, "this") 870 871 return f"COMPRESS {this}" 872 873 def generatedasidentitycolumnconstraint_sql( 874 self, expression: exp.GeneratedAsIdentityColumnConstraint 875 ) -> str: 876 this = "" 877 if expression.this is not None: 878 on_null = " ON NULL" if expression.args.get("on_null") else "" 879 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 880 881 start = expression.args.get("start") 882 start = f"START WITH {start}" if start else "" 883 increment = expression.args.get("increment") 884 increment = f" INCREMENT BY {increment}" if increment else "" 885 minvalue = expression.args.get("minvalue") 886 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 887 maxvalue = expression.args.get("maxvalue") 888 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 889 cycle = expression.args.get("cycle") 890 cycle_sql = "" 891 892 if cycle is not None: 893 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 894 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 895 896 sequence_opts = "" 897 if start or increment or cycle_sql: 898 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 899 sequence_opts = f" ({sequence_opts.strip()})" 900 901 expr = self.sql(expression, "expression") 902 expr = f"({expr})" if expr else "IDENTITY" 903 904 return f"GENERATED{this} AS {expr}{sequence_opts}" 905 906 def generatedasrowcolumnconstraint_sql( 907 self, expression: exp.GeneratedAsRowColumnConstraint 908 ) -> str: 909 start = "START" if expression.args.get("start") else "END" 910 hidden = " HIDDEN" if expression.args.get("hidden") else "" 911 return f"GENERATED ALWAYS AS ROW {start}{hidden}" 912 913 def periodforsystemtimeconstraint_sql( 914 self, expression: exp.PeriodForSystemTimeConstraint 915 ) -> str: 916 return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})" 917 918 def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str: 919 return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL" 920 921 def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str: 922 return f"AS {self.sql(expression, 'this')}" 923 924 def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str: 925 desc = expression.args.get("desc") 926 if desc is not None: 927 return f"PRIMARY KEY{' DESC' if desc else ' ASC'}" 928 return "PRIMARY KEY" 929 930 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 931 this = self.sql(expression, "this") 932 this = f" {this}" if this else "" 933 index_type = expression.args.get("index_type") 934 index_type = f" USING {index_type}" if index_type else "" 935 on_conflict = self.sql(expression, "on_conflict") 936 on_conflict = f" {on_conflict}" if on_conflict else "" 937 return f"UNIQUE{this}{index_type}{on_conflict}" 938 939 def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str: 940 return self.sql(expression, "this") 941 942 def create_sql(self, expression: exp.Create) -> str: 943 kind = self.sql(expression, "kind") 944 properties = expression.args.get("properties") 945 properties_locs = self.locate_properties(properties) if properties else defaultdict() 946 947 this = self.createable_sql(expression, properties_locs) 948 949 properties_sql = "" 950 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 951 exp.Properties.Location.POST_WITH 952 ): 953 properties_sql = self.sql( 954 exp.Properties( 955 expressions=[ 956 *properties_locs[exp.Properties.Location.POST_SCHEMA], 957 *properties_locs[exp.Properties.Location.POST_WITH], 958 ] 959 ) 960 ) 961 962 begin = " BEGIN" if expression.args.get("begin") else "" 963 end = " END" if expression.args.get("end") else "" 964 965 expression_sql = self.sql(expression, "expression") 966 if expression_sql: 967 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 968 969 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 970 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 971 postalias_props_sql = self.properties( 972 exp.Properties( 973 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 974 ), 975 wrapped=False, 976 ) 977 expression_sql = f" AS {postalias_props_sql}{expression_sql}" 978 else: 979 expression_sql = f" AS{expression_sql}" 980 981 postindex_props_sql = "" 982 if properties_locs.get(exp.Properties.Location.POST_INDEX): 983 postindex_props_sql = self.properties( 984 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 985 wrapped=False, 986 prefix=" ", 987 ) 988 989 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 990 indexes = f" {indexes}" if indexes else "" 991 index_sql = indexes + postindex_props_sql 992 993 replace = " OR REPLACE" if expression.args.get("replace") else "" 994 unique = " UNIQUE" if expression.args.get("unique") else "" 995 996 postcreate_props_sql = "" 997 if properties_locs.get(exp.Properties.Location.POST_CREATE): 998 postcreate_props_sql = self.properties( 999 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 1000 sep=" ", 1001 prefix=" ", 1002 wrapped=False, 1003 ) 1004 1005 modifiers = "".join((replace, unique, postcreate_props_sql)) 1006 1007 postexpression_props_sql = "" 1008 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 1009 postexpression_props_sql = self.properties( 1010 exp.Properties( 1011 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 1012 ), 1013 sep=" ", 1014 prefix=" ", 1015 wrapped=False, 1016 ) 1017 1018 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 1019 no_schema_binding = ( 1020 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 1021 ) 1022 1023 clone = self.sql(expression, "clone") 1024 clone = f" {clone}" if clone else "" 1025 1026 expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 1027 return self.prepend_ctes(expression, expression_sql) 1028 1029 def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str: 1030 start = self.sql(expression, "start") 1031 start = f"START WITH {start}" if start else "" 1032 increment = self.sql(expression, "increment") 1033 increment = f" INCREMENT BY {increment}" if increment else "" 1034 minvalue = self.sql(expression, "minvalue") 1035 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 1036 maxvalue = self.sql(expression, "maxvalue") 1037 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 1038 owned = self.sql(expression, "owned") 1039 owned = f" OWNED BY {owned}" if owned else "" 1040 1041 cache = expression.args.get("cache") 1042 if cache is None: 1043 cache_str = "" 1044 elif cache is True: 1045 cache_str = " CACHE" 1046 else: 1047 cache_str = f" CACHE {cache}" 1048 1049 options = self.expressions(expression, key="options", flat=True, sep=" ") 1050 options = f" {options}" if options else "" 1051 1052 return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip() 1053 1054 def clone_sql(self, expression: exp.Clone) -> str: 1055 this = self.sql(expression, "this") 1056 shallow = "SHALLOW " if expression.args.get("shallow") else "" 1057 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 1058 return f"{shallow}{keyword} {this}" 1059 1060 def describe_sql(self, expression: exp.Describe) -> str: 1061 style = expression.args.get("style") 1062 style = f" {style}" if style else "" 1063 return f"DESCRIBE{style} {self.sql(expression, 'this')}" 1064 1065 def heredoc_sql(self, expression: exp.Heredoc) -> str: 1066 tag = self.sql(expression, "tag") 1067 return f"${tag}${self.sql(expression, 'this')}${tag}$" 1068 1069 def prepend_ctes(self, expression: exp.Expression, sql: str) -> str: 1070 with_ = self.sql(expression, "with") 1071 if with_: 1072 sql = f"{with_}{self.sep()}{sql}" 1073 return sql 1074 1075 def with_sql(self, expression: exp.With) -> str: 1076 sql = self.expressions(expression, flat=True) 1077 recursive = ( 1078 "RECURSIVE " 1079 if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive") 1080 else "" 1081 ) 1082 1083 return f"WITH {recursive}{sql}" 1084 1085 def cte_sql(self, expression: exp.CTE) -> str: 1086 alias = self.sql(expression, "alias") 1087 1088 materialized = expression.args.get("materialized") 1089 if materialized is False: 1090 materialized = "NOT MATERIALIZED " 1091 elif materialized: 1092 materialized = "MATERIALIZED " 1093 1094 return f"{alias} AS {materialized or ''}{self.wrap(expression)}" 1095 1096 def tablealias_sql(self, expression: exp.TableAlias) -> str: 1097 alias = self.sql(expression, "this") 1098 columns = self.expressions(expression, key="columns", flat=True) 1099 columns = f"({columns})" if columns else "" 1100 1101 if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS: 1102 columns = "" 1103 self.unsupported("Named columns are not supported in table alias.") 1104 1105 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 1106 alias = self._next_name() 1107 1108 return f"{alias}{columns}" 1109 1110 def bitstring_sql(self, expression: exp.BitString) -> str: 1111 this = self.sql(expression, "this") 1112 if self.dialect.BIT_START: 1113 return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}" 1114 return f"{int(this, 2)}" 1115 1116 def hexstring_sql(self, expression: exp.HexString) -> str: 1117 this = self.sql(expression, "this") 1118 if self.dialect.HEX_START: 1119 return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}" 1120 return f"{int(this, 16)}" 1121 1122 def bytestring_sql(self, expression: exp.ByteString) -> str: 1123 this = self.sql(expression, "this") 1124 if self.dialect.BYTE_START: 1125 return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}" 1126 return this 1127 1128 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 1129 this = self.sql(expression, "this") 1130 escape = expression.args.get("escape") 1131 1132 if self.dialect.UNICODE_START: 1133 escape = f" UESCAPE {self.sql(escape)}" if escape else "" 1134 return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}" 1135 1136 if escape: 1137 pattern = re.compile(rf"{escape.name}(\d+)") 1138 else: 1139 pattern = ESCAPED_UNICODE_RE 1140 1141 this = pattern.sub(r"\\u\1", this) 1142 return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}" 1143 1144 def rawstring_sql(self, expression: exp.RawString) -> str: 1145 string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False) 1146 return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}" 1147 1148 def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str: 1149 this = self.sql(expression, "this") 1150 specifier = self.sql(expression, "expression") 1151 specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else "" 1152 return f"{this}{specifier}" 1153 1154 def datatype_sql(self, expression: exp.DataType) -> str: 1155 type_value = expression.this 1156 1157 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 1158 type_sql = self.sql(expression, "kind") 1159 else: 1160 type_sql = ( 1161 self.TYPE_MAPPING.get(type_value, type_value.value) 1162 if isinstance(type_value, exp.DataType.Type) 1163 else type_value 1164 ) 1165 1166 nested = "" 1167 interior = self.expressions(expression, flat=True) 1168 values = "" 1169 1170 if interior: 1171 if expression.args.get("nested"): 1172 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 1173 if expression.args.get("values") is not None: 1174 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 1175 values = self.expressions(expression, key="values", flat=True) 1176 values = f"{delimiters[0]}{values}{delimiters[1]}" 1177 elif type_value == exp.DataType.Type.INTERVAL: 1178 nested = f" {interior}" 1179 else: 1180 nested = f"({interior})" 1181 1182 type_sql = f"{type_sql}{nested}{values}" 1183 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 1184 exp.DataType.Type.TIMETZ, 1185 exp.DataType.Type.TIMESTAMPTZ, 1186 ): 1187 type_sql = f"{type_sql} WITH TIME ZONE" 1188 1189 return type_sql 1190 1191 def directory_sql(self, expression: exp.Directory) -> str: 1192 local = "LOCAL " if expression.args.get("local") else "" 1193 row_format = self.sql(expression, "row_format") 1194 row_format = f" {row_format}" if row_format else "" 1195 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}" 1196 1197 def delete_sql(self, expression: exp.Delete) -> str: 1198 this = self.sql(expression, "this") 1199 this = f" FROM {this}" if this else "" 1200 using = self.sql(expression, "using") 1201 using = f" USING {using}" if using else "" 1202 where = self.sql(expression, "where") 1203 returning = self.sql(expression, "returning") 1204 limit = self.sql(expression, "limit") 1205 tables = self.expressions(expression, key="tables") 1206 tables = f" {tables}" if tables else "" 1207 if self.RETURNING_END: 1208 expression_sql = f"{this}{using}{where}{returning}{limit}" 1209 else: 1210 expression_sql = f"{returning}{this}{using}{where}{limit}" 1211 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}") 1212 1213 def drop_sql(self, expression: exp.Drop) -> str: 1214 this = self.sql(expression, "this") 1215 expressions = self.expressions(expression, flat=True) 1216 expressions = f" ({expressions})" if expressions else "" 1217 kind = expression.args["kind"] 1218 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1219 on_cluster = self.sql(expression, "cluster") 1220 on_cluster = f" {on_cluster}" if on_cluster else "" 1221 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1222 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1223 cascade = " CASCADE" if expression.args.get("cascade") else "" 1224 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1225 purge = " PURGE" if expression.args.get("purge") else "" 1226 return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}" 1227 1228 def except_sql(self, expression: exp.Except) -> str: 1229 return self.set_operations(expression) 1230 1231 def except_op(self, expression: exp.Except) -> str: 1232 return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}" 1233 1234 def fetch_sql(self, expression: exp.Fetch) -> str: 1235 direction = expression.args.get("direction") 1236 direction = f" {direction}" if direction else "" 1237 count = expression.args.get("count") 1238 count = f" {count}" if count else "" 1239 if expression.args.get("percent"): 1240 count = f"{count} PERCENT" 1241 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1242 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}" 1243 1244 def filter_sql(self, expression: exp.Filter) -> str: 1245 if self.AGGREGATE_FILTER_SUPPORTED: 1246 this = self.sql(expression, "this") 1247 where = self.sql(expression, "expression").strip() 1248 return f"{this} FILTER({where})" 1249 1250 agg = expression.this 1251 agg_arg = agg.this 1252 cond = expression.expression.this 1253 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1254 return self.sql(agg) 1255 1256 def hint_sql(self, expression: exp.Hint) -> str: 1257 if not self.QUERY_HINTS: 1258 self.unsupported("Hints are not supported") 1259 return "" 1260 1261 return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */" 1262 1263 def indexparameters_sql(self, expression: exp.IndexParameters) -> str: 1264 using = self.sql(expression, "using") 1265 using = f" USING {using}" if using else "" 1266 columns = self.expressions(expression, key="columns", flat=True) 1267 columns = f"({columns})" if columns else "" 1268 partition_by = self.expressions(expression, key="partition_by", flat=True) 1269 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1270 where = self.sql(expression, "where") 1271 include = self.expressions(expression, key="include", flat=True) 1272 if include: 1273 include = f" INCLUDE ({include})" 1274 with_storage = self.expressions(expression, key="with_storage", flat=True) 1275 with_storage = f" WITH ({with_storage})" if with_storage else "" 1276 tablespace = self.sql(expression, "tablespace") 1277 tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else "" 1278 1279 return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}" 1280 1281 def index_sql(self, expression: exp.Index) -> str: 1282 unique = "UNIQUE " if expression.args.get("unique") else "" 1283 primary = "PRIMARY " if expression.args.get("primary") else "" 1284 amp = "AMP " if expression.args.get("amp") else "" 1285 name = self.sql(expression, "this") 1286 name = f"{name} " if name else "" 1287 table = self.sql(expression, "table") 1288 table = f"{self.INDEX_ON} {table}" if table else "" 1289 1290 index = "INDEX " if not table else "" 1291 1292 params = self.sql(expression, "params") 1293 return f"{unique}{primary}{amp}{index}{name}{table}{params}" 1294 1295 def identifier_sql(self, expression: exp.Identifier) -> str: 1296 text = expression.name 1297 lower = text.lower() 1298 text = lower if self.normalize and not expression.quoted else text 1299 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1300 if ( 1301 expression.quoted 1302 or self.dialect.can_identify(text, self.identify) 1303 or lower in self.RESERVED_KEYWORDS 1304 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1305 ): 1306 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1307 return text 1308 1309 def hex_sql(self, expression: exp.Hex) -> str: 1310 text = self.func(self.HEX_FUNC, self.sql(expression, "this")) 1311 if self.dialect.HEX_LOWERCASE: 1312 text = self.func("LOWER", text) 1313 1314 return text 1315 1316 def lowerhex_sql(self, expression: exp.LowerHex) -> str: 1317 text = self.func(self.HEX_FUNC, self.sql(expression, "this")) 1318 if not self.dialect.HEX_LOWERCASE: 1319 text = self.func("LOWER", text) 1320 return text 1321 1322 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1323 input_format = self.sql(expression, "input_format") 1324 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1325 output_format = self.sql(expression, "output_format") 1326 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1327 return self.sep().join((input_format, output_format)) 1328 1329 def national_sql(self, expression: exp.National, prefix: str = "N") -> str: 1330 string = self.sql(exp.Literal.string(expression.name)) 1331 return f"{prefix}{string}" 1332 1333 def partition_sql(self, expression: exp.Partition) -> str: 1334 return f"PARTITION({self.expressions(expression, flat=True)})" 1335 1336 def properties_sql(self, expression: exp.Properties) -> str: 1337 root_properties = [] 1338 with_properties = [] 1339 1340 for p in expression.expressions: 1341 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1342 if p_loc == exp.Properties.Location.POST_WITH: 1343 with_properties.append(p) 1344 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1345 root_properties.append(p) 1346 1347 return self.root_properties( 1348 exp.Properties(expressions=root_properties) 1349 ) + self.with_properties(exp.Properties(expressions=with_properties)) 1350 1351 def root_properties(self, properties: exp.Properties) -> str: 1352 if properties.expressions: 1353 return self.sep() + self.expressions(properties, indent=False, sep=" ") 1354 return "" 1355 1356 def properties( 1357 self, 1358 properties: exp.Properties, 1359 prefix: str = "", 1360 sep: str = ", ", 1361 suffix: str = "", 1362 wrapped: bool = True, 1363 ) -> str: 1364 if properties.expressions: 1365 expressions = self.expressions(properties, sep=sep, indent=False) 1366 if expressions: 1367 expressions = self.wrap(expressions) if wrapped else expressions 1368 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1369 return "" 1370 1371 def with_properties(self, properties: exp.Properties) -> str: 1372 return self.properties(properties, prefix=self.seg("WITH")) 1373 1374 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1375 properties_locs = defaultdict(list) 1376 for p in properties.expressions: 1377 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1378 if p_loc != exp.Properties.Location.UNSUPPORTED: 1379 properties_locs[p_loc].append(p) 1380 else: 1381 self.unsupported(f"Unsupported property {p.key}") 1382 1383 return properties_locs 1384 1385 def property_name(self, expression: exp.Property, string_key: bool = False) -> str: 1386 if isinstance(expression.this, exp.Dot): 1387 return self.sql(expression, "this") 1388 return f"'{expression.name}'" if string_key else expression.name 1389 1390 def property_sql(self, expression: exp.Property) -> str: 1391 property_cls = expression.__class__ 1392 if property_cls == exp.Property: 1393 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1394 1395 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1396 if not property_name: 1397 self.unsupported(f"Unsupported property {expression.key}") 1398 1399 return f"{property_name}={self.sql(expression, 'this')}" 1400 1401 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1402 if self.SUPPORTS_CREATE_TABLE_LIKE: 1403 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1404 options = f" {options}" if options else "" 1405 1406 like = f"LIKE {self.sql(expression, 'this')}{options}" 1407 if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema): 1408 like = f"({like})" 1409 1410 return like 1411 1412 if expression.expressions: 1413 self.unsupported("Transpilation of LIKE property options is unsupported") 1414 1415 select = exp.select("*").from_(expression.this).limit(0) 1416 return f"AS {self.sql(select)}" 1417 1418 def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str: 1419 no = "NO " if expression.args.get("no") else "" 1420 protection = " PROTECTION" if expression.args.get("protection") else "" 1421 return f"{no}FALLBACK{protection}" 1422 1423 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1424 no = "NO " if expression.args.get("no") else "" 1425 local = expression.args.get("local") 1426 local = f"{local} " if local else "" 1427 dual = "DUAL " if expression.args.get("dual") else "" 1428 before = "BEFORE " if expression.args.get("before") else "" 1429 after = "AFTER " if expression.args.get("after") else "" 1430 return f"{no}{local}{dual}{before}{after}JOURNAL" 1431 1432 def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str: 1433 freespace = self.sql(expression, "this") 1434 percent = " PERCENT" if expression.args.get("percent") else "" 1435 return f"FREESPACE={freespace}{percent}" 1436 1437 def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str: 1438 if expression.args.get("default"): 1439 property = "DEFAULT" 1440 elif expression.args.get("on"): 1441 property = "ON" 1442 else: 1443 property = "OFF" 1444 return f"CHECKSUM={property}" 1445 1446 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1447 if expression.args.get("no"): 1448 return "NO MERGEBLOCKRATIO" 1449 if expression.args.get("default"): 1450 return "DEFAULT MERGEBLOCKRATIO" 1451 1452 percent = " PERCENT" if expression.args.get("percent") else "" 1453 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}" 1454 1455 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1456 default = expression.args.get("default") 1457 minimum = expression.args.get("minimum") 1458 maximum = expression.args.get("maximum") 1459 if default or minimum or maximum: 1460 if default: 1461 prop = "DEFAULT" 1462 elif minimum: 1463 prop = "MINIMUM" 1464 else: 1465 prop = "MAXIMUM" 1466 return f"{prop} DATABLOCKSIZE" 1467 units = expression.args.get("units") 1468 units = f" {units}" if units else "" 1469 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}" 1470 1471 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1472 autotemp = expression.args.get("autotemp") 1473 always = expression.args.get("always") 1474 default = expression.args.get("default") 1475 manual = expression.args.get("manual") 1476 never = expression.args.get("never") 1477 1478 if autotemp is not None: 1479 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1480 elif always: 1481 prop = "ALWAYS" 1482 elif default: 1483 prop = "DEFAULT" 1484 elif manual: 1485 prop = "MANUAL" 1486 elif never: 1487 prop = "NEVER" 1488 return f"BLOCKCOMPRESSION={prop}" 1489 1490 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1491 no = expression.args.get("no") 1492 no = " NO" if no else "" 1493 concurrent = expression.args.get("concurrent") 1494 concurrent = " CONCURRENT" if concurrent else "" 1495 target = self.sql(expression, "target") 1496 target = f" {target}" if target else "" 1497 return f"WITH{no}{concurrent} ISOLATED LOADING{target}" 1498 1499 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1500 if isinstance(expression.this, list): 1501 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1502 if expression.this: 1503 modulus = self.sql(expression, "this") 1504 remainder = self.sql(expression, "expression") 1505 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1506 1507 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1508 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1509 return f"FROM ({from_expressions}) TO ({to_expressions})" 1510 1511 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1512 this = self.sql(expression, "this") 1513 1514 for_values_or_default = expression.expression 1515 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1516 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1517 else: 1518 for_values_or_default = " DEFAULT" 1519 1520 return f"PARTITION OF {this}{for_values_or_default}" 1521 1522 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1523 kind = expression.args.get("kind") 1524 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1525 for_or_in = expression.args.get("for_or_in") 1526 for_or_in = f" {for_or_in}" if for_or_in else "" 1527 lock_type = expression.args.get("lock_type") 1528 override = " OVERRIDE" if expression.args.get("override") else "" 1529 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}" 1530 1531 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1532 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1533 statistics = expression.args.get("statistics") 1534 statistics_sql = "" 1535 if statistics is not None: 1536 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1537 return f"{data_sql}{statistics_sql}" 1538 1539 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1540 sql = "WITH(SYSTEM_VERSIONING=ON" 1541 1542 if expression.this: 1543 history_table = self.sql(expression, "this") 1544 sql = f"{sql}(HISTORY_TABLE={history_table}" 1545 1546 if expression.expression: 1547 data_consistency_check = self.sql(expression, "expression") 1548 sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}" 1549 1550 sql = f"{sql})" 1551 1552 return f"{sql})" 1553 1554 def insert_sql(self, expression: exp.Insert) -> str: 1555 hint = self.sql(expression, "hint") 1556 overwrite = expression.args.get("overwrite") 1557 1558 if isinstance(expression.this, exp.Directory): 1559 this = " OVERWRITE" if overwrite else " INTO" 1560 else: 1561 this = self.INSERT_OVERWRITE if overwrite else " INTO" 1562 1563 stored = self.sql(expression, "stored") 1564 stored = f" {stored}" if stored else "" 1565 alternative = expression.args.get("alternative") 1566 alternative = f" OR {alternative}" if alternative else "" 1567 ignore = " IGNORE" if expression.args.get("ignore") else "" 1568 is_function = expression.args.get("is_function") 1569 if is_function: 1570 this = f"{this} FUNCTION" 1571 this = f"{this} {self.sql(expression, 'this')}" 1572 1573 exists = " IF EXISTS" if expression.args.get("exists") else "" 1574 where = self.sql(expression, "where") 1575 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1576 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1577 on_conflict = self.sql(expression, "conflict") 1578 on_conflict = f" {on_conflict}" if on_conflict else "" 1579 by_name = " BY NAME" if expression.args.get("by_name") else "" 1580 returning = self.sql(expression, "returning") 1581 1582 if self.RETURNING_END: 1583 expression_sql = f"{expression_sql}{on_conflict}{returning}" 1584 else: 1585 expression_sql = f"{returning}{expression_sql}{on_conflict}" 1586 1587 sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{where}{expression_sql}" 1588 return self.prepend_ctes(expression, sql) 1589 1590 def intersect_sql(self, expression: exp.Intersect) -> str: 1591 return self.set_operations(expression) 1592 1593 def intersect_op(self, expression: exp.Intersect) -> str: 1594 return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}" 1595 1596 def introducer_sql(self, expression: exp.Introducer) -> str: 1597 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 1598 1599 def kill_sql(self, expression: exp.Kill) -> str: 1600 kind = self.sql(expression, "kind") 1601 kind = f" {kind}" if kind else "" 1602 this = self.sql(expression, "this") 1603 this = f" {this}" if this else "" 1604 return f"KILL{kind}{this}" 1605 1606 def pseudotype_sql(self, expression: exp.PseudoType) -> str: 1607 return expression.name 1608 1609 def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str: 1610 return expression.name 1611 1612 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1613 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1614 1615 constraint = self.sql(expression, "constraint") 1616 constraint = f" ON CONSTRAINT {constraint}" if constraint else "" 1617 1618 conflict_keys = self.expressions(expression, key="conflict_keys", flat=True) 1619 conflict_keys = f"({conflict_keys}) " if conflict_keys else " " 1620 action = self.sql(expression, "action") 1621 1622 expressions = self.expressions(expression, flat=True) 1623 if expressions: 1624 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1625 expressions = f" {set_keyword}{expressions}" 1626 1627 return f"{conflict}{constraint}{conflict_keys}{action}{expressions}" 1628 1629 def returning_sql(self, expression: exp.Returning) -> str: 1630 return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}" 1631 1632 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1633 fields = self.sql(expression, "fields") 1634 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1635 escaped = self.sql(expression, "escaped") 1636 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1637 items = self.sql(expression, "collection_items") 1638 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1639 keys = self.sql(expression, "map_keys") 1640 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1641 lines = self.sql(expression, "lines") 1642 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1643 null = self.sql(expression, "null") 1644 null = f" NULL DEFINED AS {null}" if null else "" 1645 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}" 1646 1647 def withtablehint_sql(self, expression: exp.WithTableHint) -> str: 1648 return f"WITH ({self.expressions(expression, flat=True)})" 1649 1650 def indextablehint_sql(self, expression: exp.IndexTableHint) -> str: 1651 this = f"{self.sql(expression, 'this')} INDEX" 1652 target = self.sql(expression, "target") 1653 target = f" FOR {target}" if target else "" 1654 return f"{this}{target} ({self.expressions(expression, flat=True)})" 1655 1656 def historicaldata_sql(self, expression: exp.HistoricalData) -> str: 1657 this = self.sql(expression, "this") 1658 kind = self.sql(expression, "kind") 1659 expr = self.sql(expression, "expression") 1660 return f"{this} ({kind} => {expr})" 1661 1662 def table_parts(self, expression: exp.Table) -> str: 1663 return ".".join( 1664 self.sql(part) 1665 for part in ( 1666 expression.args.get("catalog"), 1667 expression.args.get("db"), 1668 expression.args.get("this"), 1669 ) 1670 if part is not None 1671 ) 1672 1673 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1674 table = self.table_parts(expression) 1675 only = "ONLY " if expression.args.get("only") else "" 1676 partition = self.sql(expression, "partition") 1677 partition = f" {partition}" if partition else "" 1678 version = self.sql(expression, "version") 1679 version = f" {version}" if version else "" 1680 alias = self.sql(expression, "alias") 1681 alias = f"{sep}{alias}" if alias else "" 1682 hints = self.expressions(expression, key="hints", sep=" ") 1683 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1684 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 1685 pivots = f" {pivots}" if pivots else "" 1686 joins = self.indent( 1687 self.expressions(expression, key="joins", sep="", flat=True), skip_first=True 1688 ) 1689 laterals = self.expressions(expression, key="laterals", sep="") 1690 1691 file_format = self.sql(expression, "format") 1692 if file_format: 1693 pattern = self.sql(expression, "pattern") 1694 pattern = f", PATTERN => {pattern}" if pattern else "" 1695 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1696 1697 ordinality = expression.args.get("ordinality") or "" 1698 if ordinality: 1699 ordinality = f" WITH ORDINALITY{alias}" 1700 alias = "" 1701 1702 when = self.sql(expression, "when") 1703 if when: 1704 table = f"{table} {when}" 1705 1706 return f"{only}{table}{partition}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}" 1707 1708 def tablesample_sql( 1709 self, 1710 expression: exp.TableSample, 1711 sep: str = " AS ", 1712 tablesample_keyword: t.Optional[str] = None, 1713 ) -> str: 1714 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1715 table = expression.this.copy() 1716 table.set("alias", None) 1717 this = self.sql(table) 1718 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1719 else: 1720 this = self.sql(expression, "this") 1721 alias = "" 1722 1723 method = self.sql(expression, "method") 1724 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1725 numerator = self.sql(expression, "bucket_numerator") 1726 denominator = self.sql(expression, "bucket_denominator") 1727 field = self.sql(expression, "bucket_field") 1728 field = f" ON {field}" if field else "" 1729 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1730 seed = self.sql(expression, "seed") 1731 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1732 1733 size = self.sql(expression, "size") 1734 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1735 size = f"{size} ROWS" 1736 1737 percent = self.sql(expression, "percent") 1738 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1739 percent = f"{percent} PERCENT" 1740 1741 expr = f"{bucket}{percent}{size}" 1742 if self.TABLESAMPLE_REQUIRES_PARENS: 1743 expr = f"({expr})" 1744 1745 return ( 1746 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1747 ) 1748 1749 def pivot_sql(self, expression: exp.Pivot) -> str: 1750 expressions = self.expressions(expression, flat=True) 1751 1752 if expression.this: 1753 this = self.sql(expression, "this") 1754 if not expressions: 1755 return f"UNPIVOT {this}" 1756 1757 on = f"{self.seg('ON')} {expressions}" 1758 using = self.expressions(expression, key="using", flat=True) 1759 using = f"{self.seg('USING')} {using}" if using else "" 1760 group = self.sql(expression, "group") 1761 return f"PIVOT {this}{on}{using}{group}" 1762 1763 alias = self.sql(expression, "alias") 1764 alias = f" AS {alias}" if alias else "" 1765 direction = "UNPIVOT" if expression.unpivot else "PIVOT" 1766 field = self.sql(expression, "field") 1767 include_nulls = expression.args.get("include_nulls") 1768 if include_nulls is not None: 1769 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1770 else: 1771 nulls = "" 1772 return f"{direction}{nulls}({expressions} FOR {field}){alias}" 1773 1774 def version_sql(self, expression: exp.Version) -> str: 1775 this = f"FOR {expression.name}" 1776 kind = expression.text("kind") 1777 expr = self.sql(expression, "expression") 1778 return f"{this} {kind} {expr}" 1779 1780 def tuple_sql(self, expression: exp.Tuple) -> str: 1781 return f"({self.expressions(expression, flat=True)})" 1782 1783 def update_sql(self, expression: exp.Update) -> str: 1784 this = self.sql(expression, "this") 1785 set_sql = self.expressions(expression, flat=True) 1786 from_sql = self.sql(expression, "from") 1787 where_sql = self.sql(expression, "where") 1788 returning = self.sql(expression, "returning") 1789 order = self.sql(expression, "order") 1790 limit = self.sql(expression, "limit") 1791 if self.RETURNING_END: 1792 expression_sql = f"{from_sql}{where_sql}{returning}" 1793 else: 1794 expression_sql = f"{returning}{from_sql}{where_sql}" 1795 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1796 return self.prepend_ctes(expression, sql) 1797 1798 def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str: 1799 values_as_table = values_as_table and self.VALUES_AS_TABLE 1800 1801 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1802 if values_as_table or not expression.find_ancestor(exp.From, exp.Join): 1803 args = self.expressions(expression) 1804 alias = self.sql(expression, "alias") 1805 values = f"VALUES{self.seg('')}{args}" 1806 values = ( 1807 f"({values})" 1808 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1809 else values 1810 ) 1811 return f"{values} AS {alias}" if alias else values 1812 1813 # Converts `VALUES...` expression into a series of select unions. 1814 alias_node = expression.args.get("alias") 1815 column_names = alias_node and alias_node.columns 1816 1817 selects: t.List[exp.Query] = [] 1818 1819 for i, tup in enumerate(expression.expressions): 1820 row = tup.expressions 1821 1822 if i == 0 and column_names: 1823 row = [ 1824 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1825 ] 1826 1827 selects.append(exp.Select(expressions=row)) 1828 1829 if self.pretty: 1830 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1831 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1832 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1833 query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1834 return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False)) 1835 1836 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1837 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1838 return f"({unions}){alias}" 1839 1840 def var_sql(self, expression: exp.Var) -> str: 1841 return self.sql(expression, "this") 1842 1843 def into_sql(self, expression: exp.Into) -> str: 1844 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1845 unlogged = " UNLOGGED" if expression.args.get("unlogged") else "" 1846 return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}" 1847 1848 def from_sql(self, expression: exp.From) -> str: 1849 return f"{self.seg('FROM')} {self.sql(expression, 'this')}" 1850 1851 def group_sql(self, expression: exp.Group) -> str: 1852 group_by_all = expression.args.get("all") 1853 if group_by_all is True: 1854 modifier = " ALL" 1855 elif group_by_all is False: 1856 modifier = " DISTINCT" 1857 else: 1858 modifier = "" 1859 1860 group_by = self.op_expressions(f"GROUP BY{modifier}", expression) 1861 1862 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1863 grouping_sets = ( 1864 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1865 ) 1866 1867 cube = expression.args.get("cube", []) 1868 if seq_get(cube, 0) is True: 1869 return f"{group_by}{self.seg('WITH CUBE')}" 1870 else: 1871 cube_sql = self.expressions(expression, key="cube", indent=False) 1872 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1873 1874 rollup = expression.args.get("rollup", []) 1875 if seq_get(rollup, 0) is True: 1876 return f"{group_by}{self.seg('WITH ROLLUP')}" 1877 else: 1878 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1879 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1880 1881 groupings = csv( 1882 grouping_sets, 1883 cube_sql, 1884 rollup_sql, 1885 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1886 sep=self.GROUPINGS_SEP, 1887 ) 1888 1889 if expression.args.get("expressions") and groupings: 1890 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1891 1892 return f"{group_by}{groupings}" 1893 1894 def having_sql(self, expression: exp.Having) -> str: 1895 this = self.indent(self.sql(expression, "this")) 1896 return f"{self.seg('HAVING')}{self.sep()}{this}" 1897 1898 def connect_sql(self, expression: exp.Connect) -> str: 1899 start = self.sql(expression, "start") 1900 start = self.seg(f"START WITH {start}") if start else "" 1901 nocycle = " NOCYCLE" if expression.args.get("nocycle") else "" 1902 connect = self.sql(expression, "connect") 1903 connect = self.seg(f"CONNECT BY{nocycle} {connect}") 1904 return start + connect 1905 1906 def prior_sql(self, expression: exp.Prior) -> str: 1907 return f"PRIOR {self.sql(expression, 'this')}" 1908 1909 def join_sql(self, expression: exp.Join) -> str: 1910 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1911 side = None 1912 else: 1913 side = expression.side 1914 1915 op_sql = " ".join( 1916 op 1917 for op in ( 1918 expression.method, 1919 "GLOBAL" if expression.args.get("global") else None, 1920 side, 1921 expression.kind, 1922 expression.hint if self.JOIN_HINTS else None, 1923 ) 1924 if op 1925 ) 1926 match_cond = self.sql(expression, "match_condition") 1927 match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else "" 1928 on_sql = self.sql(expression, "on") 1929 using = expression.args.get("using") 1930 1931 if not on_sql and using: 1932 on_sql = csv(*(self.sql(column) for column in using)) 1933 1934 this = expression.this 1935 this_sql = self.sql(this) 1936 1937 if on_sql: 1938 on_sql = self.indent(on_sql, skip_first=True) 1939 space = self.seg(" " * self.pad) if self.pretty else " " 1940 if using: 1941 on_sql = f"{space}USING ({on_sql})" 1942 else: 1943 on_sql = f"{space}ON {on_sql}" 1944 elif not op_sql: 1945 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 1946 return f" {this_sql}" 1947 1948 return f", {this_sql}" 1949 1950 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1951 return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}" 1952 1953 def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str: 1954 args = self.expressions(expression, flat=True) 1955 args = f"({args})" if len(args.split(",")) > 1 else args 1956 return f"{args} {arrow_sep} {self.sql(expression, 'this')}" 1957 1958 def lateral_op(self, expression: exp.Lateral) -> str: 1959 cross_apply = expression.args.get("cross_apply") 1960 1961 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 1962 if cross_apply is True: 1963 op = "INNER JOIN " 1964 elif cross_apply is False: 1965 op = "LEFT JOIN " 1966 else: 1967 op = "" 1968 1969 return f"{op}LATERAL" 1970 1971 def lateral_sql(self, expression: exp.Lateral) -> str: 1972 this = self.sql(expression, "this") 1973 1974 if expression.args.get("view"): 1975 alias = expression.args["alias"] 1976 columns = self.expressions(alias, key="columns", flat=True) 1977 table = f" {alias.name}" if alias.name else "" 1978 columns = f" AS {columns}" if columns else "" 1979 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1980 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1981 1982 alias = self.sql(expression, "alias") 1983 alias = f" AS {alias}" if alias else "" 1984 return f"{self.lateral_op(expression)} {this}{alias}" 1985 1986 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1987 this = self.sql(expression, "this") 1988 1989 args = [ 1990 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1991 for e in (expression.args.get(k) for k in ("offset", "expression")) 1992 if e 1993 ] 1994 1995 args_sql = ", ".join(self.sql(e) for e in args) 1996 args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql 1997 expressions = self.expressions(expression, flat=True) 1998 expressions = f" BY {expressions}" if expressions else "" 1999 2000 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}" 2001 2002 def offset_sql(self, expression: exp.Offset) -> str: 2003 this = self.sql(expression, "this") 2004 value = expression.expression 2005 value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value 2006 expressions = self.expressions(expression, flat=True) 2007 expressions = f" BY {expressions}" if expressions else "" 2008 return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}" 2009 2010 def setitem_sql(self, expression: exp.SetItem) -> str: 2011 kind = self.sql(expression, "kind") 2012 kind = f"{kind} " if kind else "" 2013 this = self.sql(expression, "this") 2014 expressions = self.expressions(expression) 2015 collate = self.sql(expression, "collate") 2016 collate = f" COLLATE {collate}" if collate else "" 2017 global_ = "GLOBAL " if expression.args.get("global") else "" 2018 return f"{global_}{kind}{this}{expressions}{collate}" 2019 2020 def set_sql(self, expression: exp.Set) -> str: 2021 expressions = ( 2022 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 2023 ) 2024 tag = " TAG" if expression.args.get("tag") else "" 2025 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}" 2026 2027 def pragma_sql(self, expression: exp.Pragma) -> str: 2028 return f"PRAGMA {self.sql(expression, 'this')}" 2029 2030 def lock_sql(self, expression: exp.Lock) -> str: 2031 if not self.LOCKING_READS_SUPPORTED: 2032 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 2033 return "" 2034 2035 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 2036 expressions = self.expressions(expression, flat=True) 2037 expressions = f" OF {expressions}" if expressions else "" 2038 wait = expression.args.get("wait") 2039 2040 if wait is not None: 2041 if isinstance(wait, exp.Literal): 2042 wait = f" WAIT {self.sql(wait)}" 2043 else: 2044 wait = " NOWAIT" if wait else " SKIP LOCKED" 2045 2046 return f"{lock_type}{expressions}{wait or ''}" 2047 2048 def literal_sql(self, expression: exp.Literal) -> str: 2049 text = expression.this or "" 2050 if expression.is_string: 2051 text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}" 2052 return text 2053 2054 def escape_str(self, text: str, escape_backslash: bool = True) -> str: 2055 if self.dialect.ESCAPED_SEQUENCES: 2056 to_escaped = self.dialect.ESCAPED_SEQUENCES 2057 text = "".join( 2058 to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text 2059 ) 2060 2061 return self._replace_line_breaks(text).replace( 2062 self.dialect.QUOTE_END, self._escaped_quote_end 2063 ) 2064 2065 def loaddata_sql(self, expression: exp.LoadData) -> str: 2066 local = " LOCAL" if expression.args.get("local") else "" 2067 inpath = f" INPATH {self.sql(expression, 'inpath')}" 2068 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 2069 this = f" INTO TABLE {self.sql(expression, 'this')}" 2070 partition = self.sql(expression, "partition") 2071 partition = f" {partition}" if partition else "" 2072 input_format = self.sql(expression, "input_format") 2073 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 2074 serde = self.sql(expression, "serde") 2075 serde = f" SERDE {serde}" if serde else "" 2076 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}" 2077 2078 def null_sql(self, *_) -> str: 2079 return "NULL" 2080 2081 def boolean_sql(self, expression: exp.Boolean) -> str: 2082 return "TRUE" if expression.this else "FALSE" 2083 2084 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 2085 this = self.sql(expression, "this") 2086 this = f"{this} " if this else this 2087 siblings = "SIBLINGS " if expression.args.get("siblings") else "" 2088 order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore 2089 interpolated_values = [ 2090 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 2091 for named_expression in expression.args.get("interpolate") or [] 2092 ] 2093 interpolate = ( 2094 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 2095 ) 2096 return f"{order}{interpolate}" 2097 2098 def withfill_sql(self, expression: exp.WithFill) -> str: 2099 from_sql = self.sql(expression, "from") 2100 from_sql = f" FROM {from_sql}" if from_sql else "" 2101 to_sql = self.sql(expression, "to") 2102 to_sql = f" TO {to_sql}" if to_sql else "" 2103 step_sql = self.sql(expression, "step") 2104 step_sql = f" STEP {step_sql}" if step_sql else "" 2105 return f"WITH FILL{from_sql}{to_sql}{step_sql}" 2106 2107 def cluster_sql(self, expression: exp.Cluster) -> str: 2108 return self.op_expressions("CLUSTER BY", expression) 2109 2110 def distribute_sql(self, expression: exp.Distribute) -> str: 2111 return self.op_expressions("DISTRIBUTE BY", expression) 2112 2113 def sort_sql(self, expression: exp.Sort) -> str: 2114 return self.op_expressions("SORT BY", expression) 2115 2116 def ordered_sql(self, expression: exp.Ordered) -> str: 2117 desc = expression.args.get("desc") 2118 asc = not desc 2119 2120 nulls_first = expression.args.get("nulls_first") 2121 nulls_last = not nulls_first 2122 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 2123 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 2124 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 2125 2126 this = self.sql(expression, "this") 2127 2128 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 2129 nulls_sort_change = "" 2130 if nulls_first and ( 2131 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 2132 ): 2133 nulls_sort_change = " NULLS FIRST" 2134 elif ( 2135 nulls_last 2136 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 2137 and not nulls_are_last 2138 ): 2139 nulls_sort_change = " NULLS LAST" 2140 2141 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 2142 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 2143 window = expression.find_ancestor(exp.Window, exp.Select) 2144 if isinstance(window, exp.Window) and window.args.get("spec"): 2145 self.unsupported( 2146 f"'{nulls_sort_change.strip()}' translation not supported in window functions" 2147 ) 2148 nulls_sort_change = "" 2149 elif self.NULL_ORDERING_SUPPORTED is None: 2150 if expression.this.is_int: 2151 self.unsupported( 2152 f"'{nulls_sort_change.strip()}' translation not supported with positional ordering" 2153 ) 2154 elif not isinstance(expression.this, exp.Rand): 2155 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 2156 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 2157 nulls_sort_change = "" 2158 2159 with_fill = self.sql(expression, "with_fill") 2160 with_fill = f" {with_fill}" if with_fill else "" 2161 2162 return f"{this}{sort_order}{nulls_sort_change}{with_fill}" 2163 2164 def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str: 2165 window_frame = self.sql(expression, "window_frame") 2166 window_frame = f"{window_frame} " if window_frame else "" 2167 2168 this = self.sql(expression, "this") 2169 2170 return f"{window_frame}{this}" 2171 2172 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 2173 partition = self.partition_by_sql(expression) 2174 order = self.sql(expression, "order") 2175 measures = self.expressions(expression, key="measures") 2176 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 2177 rows = self.sql(expression, "rows") 2178 rows = self.seg(rows) if rows else "" 2179 after = self.sql(expression, "after") 2180 after = self.seg(after) if after else "" 2181 pattern = self.sql(expression, "pattern") 2182 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 2183 definition_sqls = [ 2184 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 2185 for definition in expression.args.get("define", []) 2186 ] 2187 definitions = self.expressions(sqls=definition_sqls) 2188 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 2189 body = "".join( 2190 ( 2191 partition, 2192 order, 2193 measures, 2194 rows, 2195 after, 2196 pattern, 2197 define, 2198 ) 2199 ) 2200 alias = self.sql(expression, "alias") 2201 alias = f" {alias}" if alias else "" 2202 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}" 2203 2204 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 2205 limit = expression.args.get("limit") 2206 2207 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 2208 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 2209 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 2210 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 2211 2212 options = self.expressions(expression, key="options") 2213 if options: 2214 options = f" OPTION{self.wrap(options)}" 2215 2216 return csv( 2217 *sqls, 2218 *[self.sql(join) for join in expression.args.get("joins") or []], 2219 self.sql(expression, "connect"), 2220 self.sql(expression, "match"), 2221 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 2222 self.sql(expression, "prewhere"), 2223 self.sql(expression, "where"), 2224 self.sql(expression, "group"), 2225 self.sql(expression, "having"), 2226 *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()], 2227 self.sql(expression, "order"), 2228 *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit), 2229 *self.after_limit_modifiers(expression), 2230 options, 2231 sep="", 2232 ) 2233 2234 def queryoption_sql(self, expression: exp.QueryOption) -> str: 2235 return "" 2236 2237 def offset_limit_modifiers( 2238 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 2239 ) -> t.List[str]: 2240 return [ 2241 self.sql(expression, "offset") if fetch else self.sql(limit), 2242 self.sql(limit) if fetch else self.sql(expression, "offset"), 2243 ] 2244 2245 def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]: 2246 locks = self.expressions(expression, key="locks", sep=" ") 2247 locks = f" {locks}" if locks else "" 2248 return [locks, self.sql(expression, "sample")] 2249 2250 def select_sql(self, expression: exp.Select) -> str: 2251 into = expression.args.get("into") 2252 if not self.SUPPORTS_SELECT_INTO and into: 2253 into.pop() 2254 2255 hint = self.sql(expression, "hint") 2256 distinct = self.sql(expression, "distinct") 2257 distinct = f" {distinct}" if distinct else "" 2258 kind = self.sql(expression, "kind") 2259 2260 limit = expression.args.get("limit") 2261 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP: 2262 top = self.limit_sql(limit, top=True) 2263 limit.pop() 2264 else: 2265 top = "" 2266 2267 expressions = self.expressions(expression) 2268 2269 if kind: 2270 if kind in self.SELECT_KINDS: 2271 kind = f" AS {kind}" 2272 else: 2273 if kind == "STRUCT": 2274 expressions = self.expressions( 2275 sqls=[ 2276 self.sql( 2277 exp.Struct( 2278 expressions=[ 2279 exp.PropertyEQ(this=e.args.get("alias"), expression=e.this) 2280 if isinstance(e, exp.Alias) 2281 else e 2282 for e in expression.expressions 2283 ] 2284 ) 2285 ) 2286 ] 2287 ) 2288 kind = "" 2289 2290 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2291 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2292 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2293 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2294 sql = self.query_modifiers( 2295 expression, 2296 f"SELECT{top_distinct}{kind}{expressions}", 2297 self.sql(expression, "into", comment=False), 2298 self.sql(expression, "from", comment=False), 2299 ) 2300 2301 sql = self.prepend_ctes(expression, sql) 2302 2303 if not self.SUPPORTS_SELECT_INTO and into: 2304 if into.args.get("temporary"): 2305 table_kind = " TEMPORARY" 2306 elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"): 2307 table_kind = " UNLOGGED" 2308 else: 2309 table_kind = "" 2310 sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}" 2311 2312 return sql 2313 2314 def schema_sql(self, expression: exp.Schema) -> str: 2315 this = self.sql(expression, "this") 2316 sql = self.schema_columns_sql(expression) 2317 return f"{this} {sql}" if this and sql else this or sql 2318 2319 def schema_columns_sql(self, expression: exp.Schema) -> str: 2320 if expression.expressions: 2321 return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}" 2322 return "" 2323 2324 def star_sql(self, expression: exp.Star) -> str: 2325 except_ = self.expressions(expression, key="except", flat=True) 2326 except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else "" 2327 replace = self.expressions(expression, key="replace", flat=True) 2328 replace = f"{self.seg('REPLACE')} ({replace})" if replace else "" 2329 rename = self.expressions(expression, key="rename", flat=True) 2330 rename = f"{self.seg('RENAME')} ({rename})" if rename else "" 2331 return f"*{except_}{replace}{rename}" 2332 2333 def parameter_sql(self, expression: exp.Parameter) -> str: 2334 this = self.sql(expression, "this") 2335 return f"{self.PARAMETER_TOKEN}{this}" 2336 2337 def sessionparameter_sql(self, expression: exp.SessionParameter) -> str: 2338 this = self.sql(expression, "this") 2339 kind = expression.text("kind") 2340 if kind: 2341 kind = f"{kind}." 2342 return f"@@{kind}{this}" 2343 2344 def placeholder_sql(self, expression: exp.Placeholder) -> str: 2345 return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?" 2346 2347 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2348 alias = self.sql(expression, "alias") 2349 alias = f"{sep}{alias}" if alias else "" 2350 2351 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2352 pivots = f" {pivots}" if pivots else "" 2353 2354 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2355 return self.prepend_ctes(expression, sql) 2356 2357 def qualify_sql(self, expression: exp.Qualify) -> str: 2358 this = self.indent(self.sql(expression, "this")) 2359 return f"{self.seg('QUALIFY')}{self.sep()}{this}" 2360 2361 def set_operations(self, expression: exp.Union) -> str: 2362 if not self.OUTER_UNION_MODIFIERS: 2363 limit = expression.args.get("limit") 2364 order = expression.args.get("order") 2365 2366 if limit or order: 2367 select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False) 2368 2369 if limit: 2370 select = select.limit(limit.pop(), copy=False) 2371 if order: 2372 select = select.order_by(order.pop(), copy=False) 2373 return self.sql(select) 2374 2375 sqls: t.List[str] = [] 2376 stack: t.List[t.Union[str, exp.Expression]] = [expression] 2377 2378 while stack: 2379 node = stack.pop() 2380 2381 if isinstance(node, exp.Union): 2382 stack.append(node.expression) 2383 stack.append( 2384 self.maybe_comment( 2385 getattr(self, f"{node.key}_op")(node), 2386 comments=node.comments, 2387 separated=True, 2388 ) 2389 ) 2390 stack.append(node.this) 2391 else: 2392 sqls.append(self.sql(node)) 2393 2394 this = self.sep().join(sqls) 2395 this = self.query_modifiers(expression, this) 2396 return self.prepend_ctes(expression, this) 2397 2398 def union_sql(self, expression: exp.Union) -> str: 2399 return self.set_operations(expression) 2400 2401 def union_op(self, expression: exp.Union) -> str: 2402 kind = " DISTINCT" if self.EXPLICIT_UNION else "" 2403 kind = kind if expression.args.get("distinct") else " ALL" 2404 by_name = " BY NAME" if expression.args.get("by_name") else "" 2405 return f"UNION{kind}{by_name}" 2406 2407 def unnest_sql(self, expression: exp.Unnest) -> str: 2408 args = self.expressions(expression, flat=True) 2409 2410 alias = expression.args.get("alias") 2411 offset = expression.args.get("offset") 2412 2413 if self.UNNEST_WITH_ORDINALITY: 2414 if alias and isinstance(offset, exp.Expression): 2415 alias.append("columns", offset) 2416 2417 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2418 columns = alias.columns 2419 alias = self.sql(columns[0]) if columns else "" 2420 else: 2421 alias = self.sql(alias) 2422 2423 alias = f" AS {alias}" if alias else alias 2424 if self.UNNEST_WITH_ORDINALITY: 2425 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2426 else: 2427 if isinstance(offset, exp.Expression): 2428 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2429 elif offset: 2430 suffix = f"{alias} WITH OFFSET" 2431 else: 2432 suffix = alias 2433 2434 return f"UNNEST({args}){suffix}" 2435 2436 def prewhere_sql(self, expression: exp.PreWhere) -> str: 2437 return "" 2438 2439 def where_sql(self, expression: exp.Where) -> str: 2440 this = self.indent(self.sql(expression, "this")) 2441 return f"{self.seg('WHERE')}{self.sep()}{this}" 2442 2443 def window_sql(self, expression: exp.Window) -> str: 2444 this = self.sql(expression, "this") 2445 partition = self.partition_by_sql(expression) 2446 order = expression.args.get("order") 2447 order = self.order_sql(order, flat=True) if order else "" 2448 spec = self.sql(expression, "spec") 2449 alias = self.sql(expression, "alias") 2450 over = self.sql(expression, "over") or "OVER" 2451 2452 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2453 2454 first = expression.args.get("first") 2455 if first is None: 2456 first = "" 2457 else: 2458 first = "FIRST" if first else "LAST" 2459 2460 if not partition and not order and not spec and alias: 2461 return f"{this} {alias}" 2462 2463 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2464 return f"{this} ({args})" 2465 2466 def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str: 2467 partition = self.expressions(expression, key="partition_by", flat=True) 2468 return f"PARTITION BY {partition}" if partition else "" 2469 2470 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2471 kind = self.sql(expression, "kind") 2472 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2473 end = ( 2474 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2475 or "CURRENT ROW" 2476 ) 2477 return f"{kind} BETWEEN {start} AND {end}" 2478 2479 def withingroup_sql(self, expression: exp.WithinGroup) -> str: 2480 this = self.sql(expression, "this") 2481 expression_sql = self.sql(expression, "expression")[1:] # order has a leading space 2482 return f"{this} WITHIN GROUP ({expression_sql})" 2483 2484 def between_sql(self, expression: exp.Between) -> str: 2485 this = self.sql(expression, "this") 2486 low = self.sql(expression, "low") 2487 high = self.sql(expression, "high") 2488 return f"{this} BETWEEN {low} AND {high}" 2489 2490 def bracket_offset_expressions(self, expression: exp.Bracket) -> t.List[exp.Expression]: 2491 return apply_index_offset( 2492 expression.this, 2493 expression.expressions, 2494 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2495 ) 2496 2497 def bracket_sql(self, expression: exp.Bracket) -> str: 2498 expressions = self.bracket_offset_expressions(expression) 2499 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2500 return f"{self.sql(expression, 'this')}[{expressions_sql}]" 2501 2502 def all_sql(self, expression: exp.All) -> str: 2503 return f"ALL {self.wrap(expression)}" 2504 2505 def any_sql(self, expression: exp.Any) -> str: 2506 this = self.sql(expression, "this") 2507 if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)): 2508 if isinstance(expression.this, exp.UNWRAPPED_QUERIES): 2509 this = self.wrap(this) 2510 return f"ANY{this}" 2511 return f"ANY {this}" 2512 2513 def exists_sql(self, expression: exp.Exists) -> str: 2514 return f"EXISTS{self.wrap(expression)}" 2515 2516 def case_sql(self, expression: exp.Case) -> str: 2517 this = self.sql(expression, "this") 2518 statements = [f"CASE {this}" if this else "CASE"] 2519 2520 for e in expression.args["ifs"]: 2521 statements.append(f"WHEN {self.sql(e, 'this')}") 2522 statements.append(f"THEN {self.sql(e, 'true')}") 2523 2524 default = self.sql(expression, "default") 2525 2526 if default: 2527 statements.append(f"ELSE {default}") 2528 2529 statements.append("END") 2530 2531 if self.pretty and self.too_wide(statements): 2532 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2533 2534 return " ".join(statements) 2535 2536 def constraint_sql(self, expression: exp.Constraint) -> str: 2537 this = self.sql(expression, "this") 2538 expressions = self.expressions(expression, flat=True) 2539 return f"CONSTRAINT {this} {expressions}" 2540 2541 def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str: 2542 order = expression.args.get("order") 2543 order = f" OVER ({self.order_sql(order, flat=True)})" if order else "" 2544 return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}" 2545 2546 def extract_sql(self, expression: exp.Extract) -> str: 2547 this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name 2548 expression_sql = self.sql(expression, "expression") 2549 return f"EXTRACT({this} FROM {expression_sql})" 2550 2551 def trim_sql(self, expression: exp.Trim) -> str: 2552 trim_type = self.sql(expression, "position") 2553 2554 if trim_type == "LEADING": 2555 return self.func("LTRIM", expression.this) 2556 elif trim_type == "TRAILING": 2557 return self.func("RTRIM", expression.this) 2558 else: 2559 return self.func("TRIM", expression.this, expression.expression) 2560 2561 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2562 args = expression.expressions 2563 if isinstance(expression, exp.ConcatWs): 2564 args = args[1:] # Skip the delimiter 2565 2566 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2567 args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args] 2568 2569 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2570 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2571 2572 return args 2573 2574 def concat_sql(self, expression: exp.Concat) -> str: 2575 expressions = self.convert_concat_args(expression) 2576 2577 # Some dialects don't allow a single-argument CONCAT call 2578 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2579 return self.sql(expressions[0]) 2580 2581 return self.func("CONCAT", *expressions) 2582 2583 def concatws_sql(self, expression: exp.ConcatWs) -> str: 2584 return self.func( 2585 "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression) 2586 ) 2587 2588 def check_sql(self, expression: exp.Check) -> str: 2589 this = self.sql(expression, key="this") 2590 return f"CHECK ({this})" 2591 2592 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2593 expressions = self.expressions(expression, flat=True) 2594 reference = self.sql(expression, "reference") 2595 reference = f" {reference}" if reference else "" 2596 delete = self.sql(expression, "delete") 2597 delete = f" ON DELETE {delete}" if delete else "" 2598 update = self.sql(expression, "update") 2599 update = f" ON UPDATE {update}" if update else "" 2600 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}" 2601 2602 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2603 expressions = self.expressions(expression, flat=True) 2604 options = self.expressions(expression, key="options", flat=True, sep=" ") 2605 options = f" {options}" if options else "" 2606 return f"PRIMARY KEY ({expressions}){options}" 2607 2608 def if_sql(self, expression: exp.If) -> str: 2609 return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false"))) 2610 2611 def matchagainst_sql(self, expression: exp.MatchAgainst) -> str: 2612 modifier = expression.args.get("modifier") 2613 modifier = f" {modifier}" if modifier else "" 2614 return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})" 2615 2616 def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str: 2617 return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}" 2618 2619 def jsonpath_sql(self, expression: exp.JSONPath) -> str: 2620 path = self.expressions(expression, sep="", flat=True).lstrip(".") 2621 return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}" 2622 2623 def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str: 2624 if isinstance(expression, exp.JSONPathPart): 2625 transform = self.TRANSFORMS.get(expression.__class__) 2626 if not callable(transform): 2627 self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}") 2628 return "" 2629 2630 return transform(self, expression) 2631 2632 if isinstance(expression, int): 2633 return str(expression) 2634 2635 if self.JSON_PATH_SINGLE_QUOTE_ESCAPE: 2636 escaped = expression.replace("'", "\\'") 2637 escaped = f"\\'{expression}\\'" 2638 else: 2639 escaped = expression.replace('"', '\\"') 2640 escaped = f'"{escaped}"' 2641 2642 return escaped 2643 2644 def formatjson_sql(self, expression: exp.FormatJson) -> str: 2645 return f"{self.sql(expression, 'this')} FORMAT JSON" 2646 2647 def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str: 2648 null_handling = expression.args.get("null_handling") 2649 null_handling = f" {null_handling}" if null_handling else "" 2650 2651 unique_keys = expression.args.get("unique_keys") 2652 if unique_keys is not None: 2653 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2654 else: 2655 unique_keys = "" 2656 2657 return_type = self.sql(expression, "return_type") 2658 return_type = f" RETURNING {return_type}" if return_type else "" 2659 encoding = self.sql(expression, "encoding") 2660 encoding = f" ENCODING {encoding}" if encoding else "" 2661 2662 return self.func( 2663 "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG", 2664 *expression.expressions, 2665 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2666 ) 2667 2668 def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str: 2669 return self.jsonobject_sql(expression) 2670 2671 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2672 null_handling = expression.args.get("null_handling") 2673 null_handling = f" {null_handling}" if null_handling else "" 2674 return_type = self.sql(expression, "return_type") 2675 return_type = f" RETURNING {return_type}" if return_type else "" 2676 strict = " STRICT" if expression.args.get("strict") else "" 2677 return self.func( 2678 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2679 ) 2680 2681 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2682 this = self.sql(expression, "this") 2683 order = self.sql(expression, "order") 2684 null_handling = expression.args.get("null_handling") 2685 null_handling = f" {null_handling}" if null_handling else "" 2686 return_type = self.sql(expression, "return_type") 2687 return_type = f" RETURNING {return_type}" if return_type else "" 2688 strict = " STRICT" if expression.args.get("strict") else "" 2689 return self.func( 2690 "JSON_ARRAYAGG", 2691 this, 2692 suffix=f"{order}{null_handling}{return_type}{strict})", 2693 ) 2694 2695 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2696 path = self.sql(expression, "path") 2697 path = f" PATH {path}" if path else "" 2698 nested_schema = self.sql(expression, "nested_schema") 2699 2700 if nested_schema: 2701 return f"NESTED{path} {nested_schema}" 2702 2703 this = self.sql(expression, "this") 2704 kind = self.sql(expression, "kind") 2705 kind = f" {kind}" if kind else "" 2706 return f"{this}{kind}{path}" 2707 2708 def jsonschema_sql(self, expression: exp.JSONSchema) -> str: 2709 return self.func("COLUMNS", *expression.expressions) 2710 2711 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2712 this = self.sql(expression, "this") 2713 path = self.sql(expression, "path") 2714 path = f", {path}" if path else "" 2715 error_handling = expression.args.get("error_handling") 2716 error_handling = f" {error_handling}" if error_handling else "" 2717 empty_handling = expression.args.get("empty_handling") 2718 empty_handling = f" {empty_handling}" if empty_handling else "" 2719 schema = self.sql(expression, "schema") 2720 return self.func( 2721 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2722 ) 2723 2724 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2725 this = self.sql(expression, "this") 2726 kind = self.sql(expression, "kind") 2727 path = self.sql(expression, "path") 2728 path = f" {path}" if path else "" 2729 as_json = " AS JSON" if expression.args.get("as_json") else "" 2730 return f"{this} {kind}{path}{as_json}" 2731 2732 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2733 this = self.sql(expression, "this") 2734 path = self.sql(expression, "path") 2735 path = f", {path}" if path else "" 2736 expressions = self.expressions(expression) 2737 with_ = ( 2738 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2739 if expressions 2740 else "" 2741 ) 2742 return f"OPENJSON({this}{path}){with_}" 2743 2744 def in_sql(self, expression: exp.In) -> str: 2745 query = expression.args.get("query") 2746 unnest = expression.args.get("unnest") 2747 field = expression.args.get("field") 2748 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2749 2750 if query: 2751 in_sql = self.sql(query) 2752 elif unnest: 2753 in_sql = self.in_unnest_op(unnest) 2754 elif field: 2755 in_sql = self.sql(field) 2756 else: 2757 in_sql = f"({self.expressions(expression, flat=True)})" 2758 2759 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}" 2760 2761 def in_unnest_op(self, unnest: exp.Unnest) -> str: 2762 return f"(SELECT {self.sql(unnest)})" 2763 2764 def interval_sql(self, expression: exp.Interval) -> str: 2765 unit = self.sql(expression, "unit") 2766 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2767 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2768 unit = f" {unit}" if unit else "" 2769 2770 if self.SINGLE_STRING_INTERVAL: 2771 this = expression.this.name if expression.this else "" 2772 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2773 2774 this = self.sql(expression, "this") 2775 if this: 2776 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2777 this = f" {this}" if unwrapped else f" ({this})" 2778 2779 return f"INTERVAL{this}{unit}" 2780 2781 def return_sql(self, expression: exp.Return) -> str: 2782 return f"RETURN {self.sql(expression, 'this')}" 2783 2784 def reference_sql(self, expression: exp.Reference) -> str: 2785 this = self.sql(expression, "this") 2786 expressions = self.expressions(expression, flat=True) 2787 expressions = f"({expressions})" if expressions else "" 2788 options = self.expressions(expression, key="options", flat=True, sep=" ") 2789 options = f" {options}" if options else "" 2790 return f"REFERENCES {this}{expressions}{options}" 2791 2792 def anonymous_sql(self, expression: exp.Anonymous) -> str: 2793 return self.func(self.sql(expression, "this"), *expression.expressions) 2794 2795 def paren_sql(self, expression: exp.Paren) -> str: 2796 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2797 return f"({sql}{self.seg(')', sep='')}" 2798 2799 def neg_sql(self, expression: exp.Neg) -> str: 2800 # This makes sure we don't convert "- - 5" to "--5", which is a comment 2801 this_sql = self.sql(expression, "this") 2802 sep = " " if this_sql[0] == "-" else "" 2803 return f"-{sep}{this_sql}" 2804 2805 def not_sql(self, expression: exp.Not) -> str: 2806 return f"NOT {self.sql(expression, 'this')}" 2807 2808 def alias_sql(self, expression: exp.Alias) -> str: 2809 alias = self.sql(expression, "alias") 2810 alias = f" AS {alias}" if alias else "" 2811 return f"{self.sql(expression, 'this')}{alias}" 2812 2813 def pivotalias_sql(self, expression: exp.PivotAlias) -> str: 2814 alias = expression.args["alias"] 2815 identifier_alias = isinstance(alias, exp.Identifier) 2816 2817 if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2818 alias.replace(exp.Literal.string(alias.output_name)) 2819 elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2820 alias.replace(exp.to_identifier(alias.output_name)) 2821 2822 return self.alias_sql(expression) 2823 2824 def aliases_sql(self, expression: exp.Aliases) -> str: 2825 return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})" 2826 2827 def atindex_sql(self, expression: exp.AtTimeZone) -> str: 2828 this = self.sql(expression, "this") 2829 index = self.sql(expression, "expression") 2830 return f"{this} AT {index}" 2831 2832 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 2833 this = self.sql(expression, "this") 2834 zone = self.sql(expression, "zone") 2835 return f"{this} AT TIME ZONE {zone}" 2836 2837 def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str: 2838 this = self.sql(expression, "this") 2839 zone = self.sql(expression, "zone") 2840 return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'" 2841 2842 def add_sql(self, expression: exp.Add) -> str: 2843 return self.binary(expression, "+") 2844 2845 def and_sql( 2846 self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None 2847 ) -> str: 2848 return self.connector_sql(expression, "AND", stack) 2849 2850 def or_sql( 2851 self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None 2852 ) -> str: 2853 return self.connector_sql(expression, "OR", stack) 2854 2855 def xor_sql( 2856 self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None 2857 ) -> str: 2858 return self.connector_sql(expression, "XOR", stack) 2859 2860 def connector_sql( 2861 self, 2862 expression: exp.Connector, 2863 op: str, 2864 stack: t.Optional[t.List[str | exp.Expression]] = None, 2865 ) -> str: 2866 if stack is not None: 2867 if expression.expressions: 2868 stack.append(self.expressions(expression, sep=f" {op} ")) 2869 else: 2870 stack.append(expression.right) 2871 if expression.comments and self.comments: 2872 for comment in expression.comments: 2873 if comment: 2874 op += f" /*{self.pad_comment(comment)}*/" 2875 stack.extend((op, expression.left)) 2876 return op 2877 2878 stack = [expression] 2879 sqls: t.List[str] = [] 2880 ops = set() 2881 2882 while stack: 2883 node = stack.pop() 2884 if isinstance(node, exp.Connector): 2885 ops.add(getattr(self, f"{node.key}_sql")(node, stack)) 2886 else: 2887 sql = self.sql(node) 2888 if sqls and sqls[-1] in ops: 2889 sqls[-1] += f" {sql}" 2890 else: 2891 sqls.append(sql) 2892 2893 sep = "\n" if self.pretty and self.too_wide(sqls) else " " 2894 return sep.join(sqls) 2895 2896 def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str: 2897 return self.binary(expression, "&") 2898 2899 def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str: 2900 return self.binary(expression, "<<") 2901 2902 def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str: 2903 return f"~{self.sql(expression, 'this')}" 2904 2905 def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str: 2906 return self.binary(expression, "|") 2907 2908 def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str: 2909 return self.binary(expression, ">>") 2910 2911 def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str: 2912 return self.binary(expression, "^") 2913 2914 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2915 format_sql = self.sql(expression, "format") 2916 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2917 to_sql = self.sql(expression, "to") 2918 to_sql = f" {to_sql}" if to_sql else "" 2919 action = self.sql(expression, "action") 2920 action = f" {action}" if action else "" 2921 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql}{action})" 2922 2923 def currentdate_sql(self, expression: exp.CurrentDate) -> str: 2924 zone = self.sql(expression, "this") 2925 return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE" 2926 2927 def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str: 2928 return self.func("CURRENT_TIMESTAMP", expression.this) 2929 2930 def collate_sql(self, expression: exp.Collate) -> str: 2931 if self.COLLATE_IS_FUNC: 2932 return self.function_fallback_sql(expression) 2933 return self.binary(expression, "COLLATE") 2934 2935 def command_sql(self, expression: exp.Command) -> str: 2936 return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}" 2937 2938 def comment_sql(self, expression: exp.Comment) -> str: 2939 this = self.sql(expression, "this") 2940 kind = expression.args["kind"] 2941 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 2942 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2943 expression_sql = self.sql(expression, "expression") 2944 return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}" 2945 2946 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2947 this = self.sql(expression, "this") 2948 delete = " DELETE" if expression.args.get("delete") else "" 2949 recompress = self.sql(expression, "recompress") 2950 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2951 to_disk = self.sql(expression, "to_disk") 2952 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2953 to_volume = self.sql(expression, "to_volume") 2954 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2955 return f"{this}{delete}{recompress}{to_disk}{to_volume}" 2956 2957 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2958 where = self.sql(expression, "where") 2959 group = self.sql(expression, "group") 2960 aggregates = self.expressions(expression, key="aggregates") 2961 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2962 2963 if not (where or group or aggregates) and len(expression.expressions) == 1: 2964 return f"TTL {self.expressions(expression, flat=True)}" 2965 2966 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}" 2967 2968 def transaction_sql(self, expression: exp.Transaction) -> str: 2969 return "BEGIN" 2970 2971 def commit_sql(self, expression: exp.Commit) -> str: 2972 chain = expression.args.get("chain") 2973 if chain is not None: 2974 chain = " AND CHAIN" if chain else " AND NO CHAIN" 2975 2976 return f"COMMIT{chain or ''}" 2977 2978 def rollback_sql(self, expression: exp.Rollback) -> str: 2979 savepoint = expression.args.get("savepoint") 2980 savepoint = f" TO {savepoint}" if savepoint else "" 2981 return f"ROLLBACK{savepoint}" 2982 2983 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2984 this = self.sql(expression, "this") 2985 2986 dtype = self.sql(expression, "dtype") 2987 if dtype: 2988 collate = self.sql(expression, "collate") 2989 collate = f" COLLATE {collate}" if collate else "" 2990 using = self.sql(expression, "using") 2991 using = f" USING {using}" if using else "" 2992 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2993 2994 default = self.sql(expression, "default") 2995 if default: 2996 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2997 2998 comment = self.sql(expression, "comment") 2999 if comment: 3000 return f"ALTER COLUMN {this} COMMENT {comment}" 3001 3002 if not expression.args.get("drop"): 3003 self.unsupported("Unsupported ALTER COLUMN syntax") 3004 3005 return f"ALTER COLUMN {this} DROP DEFAULT" 3006 3007 def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str: 3008 this = self.sql(expression, "this") 3009 if not isinstance(expression.this, exp.Var): 3010 this = f"KEY DISTKEY {this}" 3011 return f"ALTER DISTSTYLE {this}" 3012 3013 def altersortkey_sql(self, expression: exp.AlterSortKey) -> str: 3014 compound = " COMPOUND" if expression.args.get("compound") else "" 3015 this = self.sql(expression, "this") 3016 expressions = self.expressions(expression, flat=True) 3017 expressions = f"({expressions})" if expressions else "" 3018 return f"ALTER{compound} SORTKEY {this or expressions}" 3019 3020 def renametable_sql(self, expression: exp.RenameTable) -> str: 3021 if not self.RENAME_TABLE_WITH_DB: 3022 # Remove db from tables 3023 expression = expression.transform( 3024 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 3025 ).assert_is(exp.RenameTable) 3026 this = self.sql(expression, "this") 3027 return f"RENAME TO {this}" 3028 3029 def renamecolumn_sql(self, expression: exp.RenameColumn) -> str: 3030 exists = " IF EXISTS" if expression.args.get("exists") else "" 3031 old_column = self.sql(expression, "this") 3032 new_column = self.sql(expression, "to") 3033 return f"RENAME COLUMN{exists} {old_column} TO {new_column}" 3034 3035 def altertable_sql(self, expression: exp.AlterTable) -> str: 3036 actions = expression.args["actions"] 3037 3038 if isinstance(actions[0], exp.ColumnDef): 3039 actions = self.add_column_sql(expression) 3040 elif isinstance(actions[0], exp.Schema): 3041 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 3042 elif isinstance(actions[0], exp.Delete): 3043 actions = self.expressions(expression, key="actions", flat=True) 3044 else: 3045 actions = self.expressions(expression, key="actions", flat=True) 3046 3047 exists = " IF EXISTS" if expression.args.get("exists") else "" 3048 on_cluster = self.sql(expression, "cluster") 3049 on_cluster = f" {on_cluster}" if on_cluster else "" 3050 only = " ONLY" if expression.args.get("only") else "" 3051 options = self.expressions(expression, key="options") 3052 options = f", {options}" if options else "" 3053 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{options}" 3054 3055 def add_column_sql(self, expression: exp.AlterTable) -> str: 3056 if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD: 3057 return self.expressions( 3058 expression, 3059 key="actions", 3060 prefix="ADD COLUMN ", 3061 ) 3062 return f"ADD {self.expressions(expression, key='actions', flat=True)}" 3063 3064 def droppartition_sql(self, expression: exp.DropPartition) -> str: 3065 expressions = self.expressions(expression) 3066 exists = " IF EXISTS " if expression.args.get("exists") else " " 3067 return f"DROP{exists}{expressions}" 3068 3069 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 3070 return f"ADD {self.expressions(expression)}" 3071 3072 def distinct_sql(self, expression: exp.Distinct) -> str: 3073 this = self.expressions(expression, flat=True) 3074 3075 if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1: 3076 case = exp.case() 3077 for arg in expression.expressions: 3078 case = case.when(arg.is_(exp.null()), exp.null()) 3079 this = self.sql(case.else_(f"({this})")) 3080 3081 this = f" {this}" if this else "" 3082 3083 on = self.sql(expression, "on") 3084 on = f" ON {on}" if on else "" 3085 return f"DISTINCT{this}{on}" 3086 3087 def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: 3088 return self._embed_ignore_nulls(expression, "IGNORE NULLS") 3089 3090 def respectnulls_sql(self, expression: exp.RespectNulls) -> str: 3091 return self._embed_ignore_nulls(expression, "RESPECT NULLS") 3092 3093 def havingmax_sql(self, expression: exp.HavingMax) -> str: 3094 this_sql = self.sql(expression, "this") 3095 expression_sql = self.sql(expression, "expression") 3096 kind = "MAX" if expression.args.get("max") else "MIN" 3097 return f"{this_sql} HAVING {kind} {expression_sql}" 3098 3099 def intdiv_sql(self, expression: exp.IntDiv) -> str: 3100 return self.sql( 3101 exp.Cast( 3102 this=exp.Div(this=expression.this, expression=expression.expression), 3103 to=exp.DataType(this=exp.DataType.Type.INT), 3104 ) 3105 ) 3106 3107 def dpipe_sql(self, expression: exp.DPipe) -> str: 3108 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 3109 return self.func( 3110 "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten()) 3111 ) 3112 return self.binary(expression, "||") 3113 3114 def div_sql(self, expression: exp.Div) -> str: 3115 l, r = expression.left, expression.right 3116 3117 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 3118 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 3119 3120 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 3121 if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES): 3122 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 3123 3124 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 3125 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 3126 return self.sql( 3127 exp.cast( 3128 l / r, 3129 to=exp.DataType.Type.BIGINT, 3130 ) 3131 ) 3132 3133 return self.binary(expression, "/") 3134 3135 def overlaps_sql(self, expression: exp.Overlaps) -> str: 3136 return self.binary(expression, "OVERLAPS") 3137 3138 def distance_sql(self, expression: exp.Distance) -> str: 3139 return self.binary(expression, "<->") 3140 3141 def dot_sql(self, expression: exp.Dot) -> str: 3142 return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}" 3143 3144 def eq_sql(self, expression: exp.EQ) -> str: 3145 return self.binary(expression, "=") 3146 3147 def propertyeq_sql(self, expression: exp.PropertyEQ) -> str: 3148 return self.binary(expression, ":=") 3149 3150 def escape_sql(self, expression: exp.Escape) -> str: 3151 return self.binary(expression, "ESCAPE") 3152 3153 def glob_sql(self, expression: exp.Glob) -> str: 3154 return self.binary(expression, "GLOB") 3155 3156 def gt_sql(self, expression: exp.GT) -> str: 3157 return self.binary(expression, ">") 3158 3159 def gte_sql(self, expression: exp.GTE) -> str: 3160 return self.binary(expression, ">=") 3161 3162 def ilike_sql(self, expression: exp.ILike) -> str: 3163 return self.binary(expression, "ILIKE") 3164 3165 def ilikeany_sql(self, expression: exp.ILikeAny) -> str: 3166 return self.binary(expression, "ILIKE ANY") 3167 3168 def is_sql(self, expression: exp.Is) -> str: 3169 if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean): 3170 return self.sql( 3171 expression.this if expression.expression.this else exp.not_(expression.this) 3172 ) 3173 return self.binary(expression, "IS") 3174 3175 def like_sql(self, expression: exp.Like) -> str: 3176 return self.binary(expression, "LIKE") 3177 3178 def likeany_sql(self, expression: exp.LikeAny) -> str: 3179 return self.binary(expression, "LIKE ANY") 3180 3181 def similarto_sql(self, expression: exp.SimilarTo) -> str: 3182 return self.binary(expression, "SIMILAR TO") 3183 3184 def lt_sql(self, expression: exp.LT) -> str: 3185 return self.binary(expression, "<") 3186 3187 def lte_sql(self, expression: exp.LTE) -> str: 3188 return self.binary(expression, "<=") 3189 3190 def mod_sql(self, expression: exp.Mod) -> str: 3191 return self.binary(expression, "%") 3192 3193 def mul_sql(self, expression: exp.Mul) -> str: 3194 return self.binary(expression, "*") 3195 3196 def neq_sql(self, expression: exp.NEQ) -> str: 3197 return self.binary(expression, "<>") 3198 3199 def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str: 3200 return self.binary(expression, "IS NOT DISTINCT FROM") 3201 3202 def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str: 3203 return self.binary(expression, "IS DISTINCT FROM") 3204 3205 def slice_sql(self, expression: exp.Slice) -> str: 3206 return self.binary(expression, ":") 3207 3208 def sub_sql(self, expression: exp.Sub) -> str: 3209 return self.binary(expression, "-") 3210 3211 def trycast_sql(self, expression: exp.TryCast) -> str: 3212 return self.cast_sql(expression, safe_prefix="TRY_") 3213 3214 def try_sql(self, expression: exp.Try) -> str: 3215 if not self.TRY_SUPPORTED: 3216 self.unsupported("Unsupported TRY function") 3217 return self.sql(expression, "this") 3218 3219 return self.func("TRY", expression.this) 3220 3221 def log_sql(self, expression: exp.Log) -> str: 3222 this = expression.this 3223 expr = expression.expression 3224 3225 if self.dialect.LOG_BASE_FIRST is False: 3226 this, expr = expr, this 3227 elif self.dialect.LOG_BASE_FIRST is None and expr: 3228 if this.name in ("2", "10"): 3229 return self.func(f"LOG{this.name}", expr) 3230 3231 self.unsupported(f"Unsupported logarithm with base {self.sql(this)}") 3232 3233 return self.func("LOG", this, expr) 3234 3235 def use_sql(self, expression: exp.Use) -> str: 3236 kind = self.sql(expression, "kind") 3237 kind = f" {kind}" if kind else "" 3238 this = self.sql(expression, "this") 3239 this = f" {this}" if this else "" 3240 return f"USE{kind}{this}" 3241 3242 def binary(self, expression: exp.Binary, op: str) -> str: 3243 op = self.maybe_comment(op, comments=expression.comments) 3244 return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}" 3245 3246 def function_fallback_sql(self, expression: exp.Func) -> str: 3247 args = [] 3248 3249 for key in expression.arg_types: 3250 arg_value = expression.args.get(key) 3251 3252 if isinstance(arg_value, list): 3253 for value in arg_value: 3254 args.append(value) 3255 elif arg_value is not None: 3256 args.append(arg_value) 3257 3258 if self.normalize_functions: 3259 name = expression.sql_name() 3260 else: 3261 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 3262 3263 return self.func(name, *args) 3264 3265 def func( 3266 self, 3267 name: str, 3268 *args: t.Optional[exp.Expression | str], 3269 prefix: str = "(", 3270 suffix: str = ")", 3271 ) -> str: 3272 return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}" 3273 3274 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 3275 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 3276 if self.pretty and self.too_wide(arg_sqls): 3277 return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 3278 return ", ".join(arg_sqls) 3279 3280 def too_wide(self, args: t.Iterable) -> bool: 3281 return sum(len(arg) for arg in args) > self.max_text_width 3282 3283 def format_time( 3284 self, 3285 expression: exp.Expression, 3286 inverse_time_mapping: t.Optional[t.Dict[str, str]] = None, 3287 inverse_time_trie: t.Optional[t.Dict] = None, 3288 ) -> t.Optional[str]: 3289 return format_time( 3290 self.sql(expression, "format"), 3291 inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING, 3292 inverse_time_trie or self.dialect.INVERSE_TIME_TRIE, 3293 ) 3294 3295 def expressions( 3296 self, 3297 expression: t.Optional[exp.Expression] = None, 3298 key: t.Optional[str] = None, 3299 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 3300 flat: bool = False, 3301 indent: bool = True, 3302 skip_first: bool = False, 3303 skip_last: bool = False, 3304 sep: str = ", ", 3305 prefix: str = "", 3306 dynamic: bool = False, 3307 new_line: bool = False, 3308 ) -> str: 3309 expressions = expression.args.get(key or "expressions") if expression else sqls 3310 3311 if not expressions: 3312 return "" 3313 3314 if flat: 3315 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 3316 3317 num_sqls = len(expressions) 3318 3319 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 3320 if self.pretty and not self.leading_comma: 3321 stripped_sep = sep.strip() 3322 3323 result_sqls = [] 3324 for i, e in enumerate(expressions): 3325 sql = self.sql(e, comment=False) 3326 if not sql: 3327 continue 3328 3329 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 3330 3331 if self.pretty: 3332 if self.leading_comma: 3333 result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}") 3334 else: 3335 result_sqls.append( 3336 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 3337 ) 3338 else: 3339 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 3340 3341 if self.pretty and (not dynamic or self.too_wide(result_sqls)): 3342 if new_line: 3343 result_sqls.insert(0, "") 3344 result_sqls.append("") 3345 result_sql = "\n".join(result_sqls) 3346 else: 3347 result_sql = "".join(result_sqls) 3348 return ( 3349 self.indent(result_sql, skip_first=skip_first, skip_last=skip_last) 3350 if indent 3351 else result_sql 3352 ) 3353 3354 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 3355 flat = flat or isinstance(expression.parent, exp.Properties) 3356 expressions_sql = self.expressions(expression, flat=flat) 3357 if flat: 3358 return f"{op} {expressions_sql}" 3359 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}" 3360 3361 def naked_property(self, expression: exp.Property) -> str: 3362 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 3363 if not property_name: 3364 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 3365 return f"{property_name} {self.sql(expression, 'this')}" 3366 3367 def tag_sql(self, expression: exp.Tag) -> str: 3368 return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}" 3369 3370 def token_sql(self, token_type: TokenType) -> str: 3371 return self.TOKEN_MAPPING.get(token_type, token_type.name) 3372 3373 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 3374 this = self.sql(expression, "this") 3375 expressions = self.no_identify(self.expressions, expression) 3376 expressions = ( 3377 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 3378 ) 3379 return f"{this}{expressions}" 3380 3381 def joinhint_sql(self, expression: exp.JoinHint) -> str: 3382 this = self.sql(expression, "this") 3383 expressions = self.expressions(expression, flat=True) 3384 return f"{this}({expressions})" 3385 3386 def kwarg_sql(self, expression: exp.Kwarg) -> str: 3387 return self.binary(expression, "=>") 3388 3389 def when_sql(self, expression: exp.When) -> str: 3390 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 3391 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 3392 condition = self.sql(expression, "condition") 3393 condition = f" AND {condition}" if condition else "" 3394 3395 then_expression = expression.args.get("then") 3396 if isinstance(then_expression, exp.Insert): 3397 this = self.sql(then_expression, "this") 3398 this = f"INSERT {this}" if this else "INSERT" 3399 then = self.sql(then_expression, "expression") 3400 then = f"{this} VALUES {then}" if then else this 3401 elif isinstance(then_expression, exp.Update): 3402 if isinstance(then_expression.args.get("expressions"), exp.Star): 3403 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 3404 else: 3405 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 3406 else: 3407 then = self.sql(then_expression) 3408 return f"WHEN {matched}{source}{condition} THEN {then}" 3409 3410 def merge_sql(self, expression: exp.Merge) -> str: 3411 table = expression.this 3412 table_alias = "" 3413 3414 hints = table.args.get("hints") 3415 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 3416 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 3417 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 3418 3419 this = self.sql(table) 3420 using = f"USING {self.sql(expression, 'using')}" 3421 on = f"ON {self.sql(expression, 'on')}" 3422 expressions = self.expressions(expression, sep=" ", indent=False) 3423 sep = self.sep() 3424 3425 return self.prepend_ctes( 3426 expression, f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{expressions}" 3427 ) 3428 3429 def tochar_sql(self, expression: exp.ToChar) -> str: 3430 if expression.args.get("format"): 3431 self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function") 3432 3433 return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT)) 3434 3435 def tonumber_sql(self, expression: exp.ToNumber) -> str: 3436 if not self.SUPPORTS_TO_NUMBER: 3437 self.unsupported("Unsupported TO_NUMBER function") 3438 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3439 3440 fmt = expression.args.get("format") 3441 if not fmt: 3442 self.unsupported("Conversion format is required for TO_NUMBER") 3443 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3444 3445 return self.func("TO_NUMBER", expression.this, fmt) 3446 3447 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 3448 this = self.sql(expression, "this") 3449 kind = self.sql(expression, "kind") 3450 settings_sql = self.expressions(expression, key="settings", sep=" ") 3451 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 3452 return f"{this}({kind}{args})" 3453 3454 def dictrange_sql(self, expression: exp.DictRange) -> str: 3455 this = self.sql(expression, "this") 3456 max = self.sql(expression, "max") 3457 min = self.sql(expression, "min") 3458 return f"{this}(MIN {min} MAX {max})" 3459 3460 def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str: 3461 return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}" 3462 3463 def oncluster_sql(self, expression: exp.OnCluster) -> str: 3464 return "" 3465 3466 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3467 expressions = self.expressions(expression, key="expressions", flat=True) 3468 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3469 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3470 buckets = self.sql(expression, "buckets") 3471 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS" 3472 3473 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3474 this = self.sql(expression, "this") 3475 having = self.sql(expression, "having") 3476 3477 if having: 3478 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3479 3480 return self.func("ANY_VALUE", this) 3481 3482 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3483 transform = self.func("TRANSFORM", *expression.expressions) 3484 row_format_before = self.sql(expression, "row_format_before") 3485 row_format_before = f" {row_format_before}" if row_format_before else "" 3486 record_writer = self.sql(expression, "record_writer") 3487 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3488 using = f" USING {self.sql(expression, 'command_script')}" 3489 schema = self.sql(expression, "schema") 3490 schema = f" AS {schema}" if schema else "" 3491 row_format_after = self.sql(expression, "row_format_after") 3492 row_format_after = f" {row_format_after}" if row_format_after else "" 3493 record_reader = self.sql(expression, "record_reader") 3494 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3495 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}" 3496 3497 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3498 key_block_size = self.sql(expression, "key_block_size") 3499 if key_block_size: 3500 return f"KEY_BLOCK_SIZE = {key_block_size}" 3501 3502 using = self.sql(expression, "using") 3503 if using: 3504 return f"USING {using}" 3505 3506 parser = self.sql(expression, "parser") 3507 if parser: 3508 return f"WITH PARSER {parser}" 3509 3510 comment = self.sql(expression, "comment") 3511 if comment: 3512 return f"COMMENT {comment}" 3513 3514 visible = expression.args.get("visible") 3515 if visible is not None: 3516 return "VISIBLE" if visible else "INVISIBLE" 3517 3518 engine_attr = self.sql(expression, "engine_attr") 3519 if engine_attr: 3520 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3521 3522 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3523 if secondary_engine_attr: 3524 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3525 3526 self.unsupported("Unsupported index constraint option.") 3527 return "" 3528 3529 def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str: 3530 enforced = " ENFORCED" if expression.args.get("enforced") else "" 3531 return f"CHECK ({self.sql(expression, 'this')}){enforced}" 3532 3533 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3534 kind = self.sql(expression, "kind") 3535 kind = f"{kind} INDEX" if kind else "INDEX" 3536 this = self.sql(expression, "this") 3537 this = f" {this}" if this else "" 3538 index_type = self.sql(expression, "index_type") 3539 index_type = f" USING {index_type}" if index_type else "" 3540 expressions = self.expressions(expression, flat=True) 3541 expressions = f" ({expressions})" if expressions else "" 3542 options = self.expressions(expression, key="options", sep=" ") 3543 options = f" {options}" if options else "" 3544 return f"{kind}{this}{index_type}{expressions}{options}" 3545 3546 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3547 if self.NVL2_SUPPORTED: 3548 return self.function_fallback_sql(expression) 3549 3550 case = exp.Case().when( 3551 expression.this.is_(exp.null()).not_(copy=False), 3552 expression.args["true"], 3553 copy=False, 3554 ) 3555 else_cond = expression.args.get("false") 3556 if else_cond: 3557 case.else_(else_cond, copy=False) 3558 3559 return self.sql(case) 3560 3561 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3562 this = self.sql(expression, "this") 3563 expr = self.sql(expression, "expression") 3564 iterator = self.sql(expression, "iterator") 3565 condition = self.sql(expression, "condition") 3566 condition = f" IF {condition}" if condition else "" 3567 return f"{this} FOR {expr} IN {iterator}{condition}" 3568 3569 def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str: 3570 return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})" 3571 3572 def opclass_sql(self, expression: exp.Opclass) -> str: 3573 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 3574 3575 def predict_sql(self, expression: exp.Predict) -> str: 3576 model = self.sql(expression, "this") 3577 model = f"MODEL {model}" 3578 table = self.sql(expression, "expression") 3579 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3580 parameters = self.sql(expression, "params_struct") 3581 return self.func("PREDICT", model, table, parameters or None) 3582 3583 def forin_sql(self, expression: exp.ForIn) -> str: 3584 this = self.sql(expression, "this") 3585 expression_sql = self.sql(expression, "expression") 3586 return f"FOR {this} DO {expression_sql}" 3587 3588 def refresh_sql(self, expression: exp.Refresh) -> str: 3589 this = self.sql(expression, "this") 3590 table = "" if isinstance(expression.this, exp.Literal) else "TABLE " 3591 return f"REFRESH {table}{this}" 3592 3593 def operator_sql(self, expression: exp.Operator) -> str: 3594 return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})") 3595 3596 def toarray_sql(self, expression: exp.ToArray) -> str: 3597 arg = expression.this 3598 if not arg.type: 3599 from sqlglot.optimizer.annotate_types import annotate_types 3600 3601 arg = annotate_types(arg) 3602 3603 if arg.is_type(exp.DataType.Type.ARRAY): 3604 return self.sql(arg) 3605 3606 cond_for_null = arg.is_(exp.null()) 3607 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False))) 3608 3609 def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str: 3610 this = expression.this 3611 if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME): 3612 return self.sql(this) 3613 3614 return self.sql(exp.cast(this, exp.DataType.Type.TIME)) 3615 3616 def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str: 3617 this = expression.this 3618 if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP): 3619 return self.sql(this) 3620 3621 return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP)) 3622 3623 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3624 this = expression.this 3625 time_format = self.format_time(expression) 3626 3627 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3628 return self.sql( 3629 exp.cast( 3630 exp.StrToTime(this=this, format=expression.args["format"]), 3631 exp.DataType.Type.DATE, 3632 ) 3633 ) 3634 3635 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3636 return self.sql(this) 3637 3638 return self.sql(exp.cast(this, exp.DataType.Type.DATE)) 3639 3640 def unixdate_sql(self, expression: exp.UnixDate) -> str: 3641 return self.sql( 3642 exp.func( 3643 "DATEDIFF", 3644 expression.this, 3645 exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE), 3646 "day", 3647 ) 3648 ) 3649 3650 def lastday_sql(self, expression: exp.LastDay) -> str: 3651 if self.LAST_DAY_SUPPORTS_DATE_PART: 3652 return self.function_fallback_sql(expression) 3653 3654 unit = expression.text("unit") 3655 if unit and unit != "MONTH": 3656 self.unsupported("Date parts are not supported in LAST_DAY.") 3657 3658 return self.func("LAST_DAY", expression.this) 3659 3660 def dateadd_sql(self, expression: exp.DateAdd) -> str: 3661 from sqlglot.dialects.dialect import unit_to_str 3662 3663 return self.func( 3664 "DATE_ADD", expression.this, expression.expression, unit_to_str(expression) 3665 ) 3666 3667 def arrayany_sql(self, expression: exp.ArrayAny) -> str: 3668 if self.CAN_IMPLEMENT_ARRAY_ANY: 3669 filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression) 3670 filtered_not_empty = exp.ArraySize(this=filtered).neq(0) 3671 original_is_empty = exp.ArraySize(this=expression.this).eq(0) 3672 return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty))) 3673 3674 from sqlglot.dialects import Dialect 3675 3676 # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect 3677 if self.dialect.__class__ != Dialect: 3678 self.unsupported("ARRAY_ANY is unsupported") 3679 3680 return self.function_fallback_sql(expression) 3681 3682 def generateseries_sql(self, expression: exp.GenerateSeries) -> str: 3683 expression.set("is_end_exclusive", None) 3684 return self.function_fallback_sql(expression) 3685 3686 def struct_sql(self, expression: exp.Struct) -> str: 3687 expression.set( 3688 "expressions", 3689 [ 3690 exp.alias_(e.expression, e.name if e.this.is_string else e.this) 3691 if isinstance(e, exp.PropertyEQ) 3692 else e 3693 for e in expression.expressions 3694 ], 3695 ) 3696 3697 return self.function_fallback_sql(expression) 3698 3699 def partitionrange_sql(self, expression: exp.PartitionRange) -> str: 3700 low = self.sql(expression, "this") 3701 high = self.sql(expression, "expression") 3702 3703 return f"{low} TO {high}" 3704 3705 def truncatetable_sql(self, expression: exp.TruncateTable) -> str: 3706 target = "DATABASE" if expression.args.get("is_database") else "TABLE" 3707 tables = f" {self.expressions(expression)}" 3708 3709 exists = " IF EXISTS" if expression.args.get("exists") else "" 3710 3711 on_cluster = self.sql(expression, "cluster") 3712 on_cluster = f" {on_cluster}" if on_cluster else "" 3713 3714 identity = self.sql(expression, "identity") 3715 identity = f" {identity} IDENTITY" if identity else "" 3716 3717 option = self.sql(expression, "option") 3718 option = f" {option}" if option else "" 3719 3720 partition = self.sql(expression, "partition") 3721 partition = f" {partition}" if partition else "" 3722 3723 return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}" 3724 3725 # This transpiles T-SQL's CONVERT function 3726 # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16 3727 def convert_sql(self, expression: exp.Convert) -> str: 3728 to = expression.this 3729 value = expression.expression 3730 style = expression.args.get("style") 3731 safe = expression.args.get("safe") 3732 strict = expression.args.get("strict") 3733 3734 if not to or not value: 3735 return "" 3736 3737 # Retrieve length of datatype and override to default if not specified 3738 if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3739 to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False) 3740 3741 transformed: t.Optional[exp.Expression] = None 3742 cast = exp.Cast if strict else exp.TryCast 3743 3744 # Check whether a conversion with format (T-SQL calls this 'style') is applicable 3745 if isinstance(style, exp.Literal) and style.is_int: 3746 from sqlglot.dialects.tsql import TSQL 3747 3748 style_value = style.name 3749 converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value) 3750 if not converted_style: 3751 self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}") 3752 3753 fmt = exp.Literal.string(converted_style) 3754 3755 if to.this == exp.DataType.Type.DATE: 3756 transformed = exp.StrToDate(this=value, format=fmt) 3757 elif to.this == exp.DataType.Type.DATETIME: 3758 transformed = exp.StrToTime(this=value, format=fmt) 3759 elif to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3760 transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe) 3761 elif to.this == exp.DataType.Type.TEXT: 3762 transformed = exp.TimeToStr(this=value, format=fmt) 3763 3764 if not transformed: 3765 transformed = cast(this=value, to=to, safe=safe) 3766 3767 return self.sql(transformed) 3768 3769 def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str: 3770 this = expression.this 3771 if isinstance(this, exp.JSONPathWildcard): 3772 this = self.json_path_part(this) 3773 return f".{this}" if this else "" 3774 3775 if exp.SAFE_IDENTIFIER_RE.match(this): 3776 return f".{this}" 3777 3778 this = self.json_path_part(this) 3779 return f"[{this}]" if self.JSON_PATH_BRACKETED_KEY_SUPPORTED else f".{this}" 3780 3781 def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str: 3782 this = self.json_path_part(expression.this) 3783 return f"[{this}]" if this else "" 3784 3785 def _simplify_unless_literal(self, expression: E) -> E: 3786 if not isinstance(expression, exp.Literal): 3787 from sqlglot.optimizer.simplify import simplify 3788 3789 expression = simplify(expression, dialect=self.dialect) 3790 3791 return expression 3792 3793 def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str: 3794 if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"): 3795 # The first modifier here will be the one closest to the AggFunc's arg 3796 mods = sorted( 3797 expression.find_all(exp.HavingMax, exp.Order, exp.Limit), 3798 key=lambda x: 0 3799 if isinstance(x, exp.HavingMax) 3800 else (1 if isinstance(x, exp.Order) else 2), 3801 ) 3802 3803 if mods: 3804 mod = mods[0] 3805 this = expression.__class__(this=mod.this.copy()) 3806 this.meta["inline"] = True 3807 mod.this.replace(this) 3808 return self.sql(expression.this) 3809 3810 agg_func = expression.find(exp.AggFunc) 3811 3812 if agg_func: 3813 return self.sql(agg_func)[:-1] + f" {text})" 3814 3815 return f"{self.sql(expression, 'this')} {text}" 3816 3817 def _replace_line_breaks(self, string: str) -> str: 3818 """We don't want to extra indent line breaks so we temporarily replace them with sentinels.""" 3819 if self.pretty: 3820 return string.replace("\n", self.SENTINEL_LINE_BREAK) 3821 return string 3822 3823 def copyparameter_sql(self, expression: exp.CopyParameter) -> str: 3824 option = self.sql(expression, "this") 3825 3826 if option.upper() == "FILE_FORMAT": 3827 values = self.expressions(expression, key="expression", flat=True, sep=" ") 3828 return f"{option} = ({values})" 3829 3830 value = self.sql(expression, "expression") 3831 3832 if not value: 3833 return option 3834 3835 op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " " 3836 3837 return f"{option}{op}{value}" 3838 3839 def credentials_sql(self, expression: exp.Credentials) -> str: 3840 cred_expr = expression.args.get("credentials") 3841 if isinstance(cred_expr, exp.Literal): 3842 # Redshift case: CREDENTIALS <string> 3843 credentials = self.sql(expression, "credentials") 3844 credentials = f"CREDENTIALS {credentials}" if credentials else "" 3845 else: 3846 # Snowflake case: CREDENTIALS = (...) 3847 credentials = self.expressions(expression, key="credentials", flat=True, sep=" ") 3848 credentials = f"CREDENTIALS = ({credentials})" if credentials else "" 3849 3850 storage = self.sql(expression, "storage") 3851 3852 encryption = self.expressions(expression, key="encryption", flat=True, sep=" ") 3853 encryption = f" ENCRYPTION = ({encryption})" if encryption else "" 3854 3855 iam_role = self.sql(expression, "iam_role") 3856 iam_role = f"IAM_ROLE {iam_role}" if iam_role else "" 3857 3858 region = self.sql(expression, "region") 3859 region = f" REGION {region}" if region else "" 3860 3861 return f"{credentials}{storage}{encryption}{iam_role}{region}" 3862 3863 def copy_sql(self, expression: exp.Copy) -> str: 3864 this = self.sql(expression, "this") 3865 this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}" 3866 3867 credentials = self.sql(expression, "credentials") 3868 credentials = self.seg(credentials) if credentials else "" 3869 kind = self.seg("FROM" if expression.args.get("kind") else "TO") 3870 files = self.expressions(expression, key="files", flat=True) 3871 3872 sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " " 3873 params = self.expressions( 3874 expression, 3875 key="params", 3876 sep=sep, 3877 new_line=True, 3878 skip_last=True, 3879 skip_first=True, 3880 indent=self.COPY_PARAMS_ARE_WRAPPED, 3881 ) 3882 3883 if params: 3884 if self.COPY_PARAMS_ARE_WRAPPED: 3885 params = f" WITH ({params})" 3886 elif not self.pretty: 3887 params = f" {params}" 3888 3889 return f"COPY{this}{kind} {files}{credentials}{params}" 3890 3891 def semicolon_sql(self, expression: exp.Semicolon) -> str: 3892 return ""
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)
550 def __init__( 551 self, 552 pretty: t.Optional[bool] = None, 553 identify: str | bool = False, 554 normalize: bool = False, 555 pad: int = 2, 556 indent: int = 2, 557 normalize_functions: t.Optional[str | bool] = None, 558 unsupported_level: ErrorLevel = ErrorLevel.WARN, 559 max_unsupported: int = 3, 560 leading_comma: bool = False, 561 max_text_width: int = 80, 562 comments: bool = True, 563 dialect: DialectType = None, 564 ): 565 import sqlglot 566 from sqlglot.dialects import Dialect 567 568 self.pretty = pretty if pretty is not None else sqlglot.pretty 569 self.identify = identify 570 self.normalize = normalize 571 self.pad = pad 572 self._indent = indent 573 self.unsupported_level = unsupported_level 574 self.max_unsupported = max_unsupported 575 self.leading_comma = leading_comma 576 self.max_text_width = max_text_width 577 self.comments = comments 578 self.dialect = Dialect.get_or_raise(dialect) 579 580 # This is both a Dialect property and a Generator argument, so we prioritize the latter 581 self.normalize_functions = ( 582 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 583 ) 584 585 self.unsupported_messages: t.List[str] = [] 586 self._escaped_quote_end: str = ( 587 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 588 ) 589 self._escaped_identifier_end: str = ( 590 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 591 ) 592 593 self._next_name = name_sequence("_t")
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.AllowedValuesProperty'>: <function Generator.<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.JSONPathFilter'>, <class 'sqlglot.expressions.JSONPathUnion'>, <class 'sqlglot.expressions.JSONPathSubscript'>, <class 'sqlglot.expressions.JSONPathSelector'>, <class 'sqlglot.expressions.JSONPathWildcard'>, <class 'sqlglot.expressions.JSONPathSlice'>, <class 'sqlglot.expressions.JSONPathScript'>, <class 'sqlglot.expressions.JSONPathRoot'>, <class 'sqlglot.expressions.JSONPathRecursive'>, <class 'sqlglot.expressions.JSONPathKey'>}
TYPE_MAPPING =
{<Type.NCHAR: 'NCHAR'>: 'CHAR', <Type.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <Type.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <Type.LONGTEXT: 'LONGTEXT'>: 'TEXT', <Type.TINYTEXT: 'TINYTEXT'>: 'TEXT', <Type.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <Type.LONGBLOB: 'LONGBLOB'>: 'BLOB', <Type.TINYBLOB: 'TINYBLOB'>: 'BLOB', <Type.INET: 'INET'>: 'INET', <Type.ROWVERSION: 'ROWVERSION'>: 'VARBINARY'}
TIME_PART_SINGULARS =
{'MICROSECONDS': 'MICROSECOND', 'SECONDS': 'SECOND', 'MINUTES': 'MINUTE', 'HOURS': 'HOUR', 'DAYS': 'DAY', 'WEEKS': 'WEEK', 'MONTHS': 'MONTH', 'QUARTERS': 'QUARTER', 'YEARS': 'YEAR'}
AFTER_HAVING_MODIFIER_TRANSFORMS =
{'cluster': <function Generator.<lambda>>, 'distribute': <function Generator.<lambda>>, 'qualify': <function Generator.<lambda>>, 'sort': <function Generator.<lambda>>, 'windows': <function Generator.<lambda>>}
PROPERTIES_LOCATION =
{<class 'sqlglot.expressions.AllowedValuesProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <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.NCHAR: 'NCHAR'>, <Type.CHAR: 'CHAR'>, <Type.VARCHAR: 'VARCHAR'>, <Type.NVARCHAR: 'NVARCHAR'>}
595 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 596 """ 597 Generates the SQL string corresponding to the given syntax tree. 598 599 Args: 600 expression: The syntax tree. 601 copy: Whether to copy the expression. The generator performs mutations so 602 it is safer to copy. 603 604 Returns: 605 The SQL string corresponding to `expression`. 606 """ 607 if copy: 608 expression = expression.copy() 609 610 expression = self.preprocess(expression) 611 612 self.unsupported_messages = [] 613 sql = self.sql(expression).strip() 614 615 if self.pretty: 616 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 617 618 if self.unsupported_level == ErrorLevel.IGNORE: 619 return sql 620 621 if self.unsupported_level == ErrorLevel.WARN: 622 for msg in self.unsupported_messages: 623 logger.warning(msg) 624 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 625 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 626 627 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:
629 def preprocess(self, expression: exp.Expression) -> exp.Expression: 630 """Apply generic preprocessing transformations to a given expression.""" 631 if ( 632 not expression.parent 633 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 634 and any(node.parent is not expression for node in expression.find_all(exp.With)) 635 ): 636 from sqlglot.transforms import move_ctes_to_top_level 637 638 expression = move_ctes_to_top_level(expression) 639 640 if self.ENSURE_BOOLS: 641 from sqlglot.transforms import ensure_bools 642 643 expression = ensure_bools(expression) 644 645 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:
663 def maybe_comment( 664 self, 665 sql: str, 666 expression: t.Optional[exp.Expression] = None, 667 comments: t.Optional[t.List[str]] = None, 668 separated: bool = False, 669 ) -> str: 670 comments = ( 671 ((expression and expression.comments) if comments is None else comments) # type: ignore 672 if self.comments 673 else None 674 ) 675 676 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 677 return sql 678 679 comments_sql = " ".join( 680 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 681 ) 682 683 if not comments_sql: 684 return sql 685 686 comments_sql = self._replace_line_breaks(comments_sql) 687 688 if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS): 689 return ( 690 f"{self.sep()}{comments_sql}{sql}" 691 if not sql or sql[0].isspace() 692 else f"{comments_sql}{self.sep()}{sql}" 693 ) 694 695 return f"{sql} {comments_sql}"
697 def wrap(self, expression: exp.Expression | str) -> str: 698 this_sql = ( 699 self.sql(expression) 700 if isinstance(expression, exp.UNWRAPPED_QUERIES) 701 else self.sql(expression, "this") 702 ) 703 if not this_sql: 704 return "()" 705 706 this_sql = self.indent(this_sql, level=1, pad=0) 707 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:
723 def indent( 724 self, 725 sql: str, 726 level: int = 0, 727 pad: t.Optional[int] = None, 728 skip_first: bool = False, 729 skip_last: bool = False, 730 ) -> str: 731 if not self.pretty or not sql: 732 return sql 733 734 pad = self.pad if pad is None else pad 735 lines = sql.split("\n") 736 737 return "\n".join( 738 ( 739 line 740 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 741 else f"{' ' * (level * self._indent + pad)}{line}" 742 ) 743 for i, line in enumerate(lines) 744 )
def
sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str:
746 def sql( 747 self, 748 expression: t.Optional[str | exp.Expression], 749 key: t.Optional[str] = None, 750 comment: bool = True, 751 ) -> str: 752 if not expression: 753 return "" 754 755 if isinstance(expression, str): 756 return expression 757 758 if key: 759 value = expression.args.get(key) 760 if value: 761 return self.sql(value) 762 return "" 763 764 transform = self.TRANSFORMS.get(expression.__class__) 765 766 if callable(transform): 767 sql = transform(self, expression) 768 elif isinstance(expression, exp.Expression): 769 exp_handler_name = f"{expression.key}_sql" 770 771 if hasattr(self, exp_handler_name): 772 sql = getattr(self, exp_handler_name)(expression) 773 elif isinstance(expression, exp.Func): 774 sql = self.function_fallback_sql(expression) 775 elif isinstance(expression, exp.Property): 776 sql = self.property_sql(expression) 777 else: 778 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 779 else: 780 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 781 782 return self.maybe_comment(sql, expression) if self.comments and comment else sql
789 def cache_sql(self, expression: exp.Cache) -> str: 790 lazy = " LAZY" if expression.args.get("lazy") else "" 791 table = self.sql(expression, "this") 792 options = expression.args.get("options") 793 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 794 sql = self.sql(expression, "expression") 795 sql = f" AS{self.sep()}{sql}" if sql else "" 796 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 797 return self.prepend_ctes(expression, sql)
799 def characterset_sql(self, expression: exp.CharacterSet) -> str: 800 if isinstance(expression.parent, exp.Cast): 801 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 802 default = "DEFAULT " if expression.args.get("default") else "" 803 return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
817 def column_sql(self, expression: exp.Column) -> str: 818 join_mark = " (+)" if expression.args.get("join_mark") else "" 819 820 if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED: 821 join_mark = "" 822 self.unsupported("Outer join syntax using the (+) operator is not supported.") 823 824 return f"{self.column_parts(expression)}{join_mark}"
832 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 833 column = self.sql(expression, "this") 834 kind = self.sql(expression, "kind") 835 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 836 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 837 kind = f"{sep}{kind}" if kind else "" 838 constraints = f" {constraints}" if constraints else "" 839 position = self.sql(expression, "position") 840 position = f" {position}" if position else "" 841 842 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 843 kind = "" 844 845 return f"{exists}{column}{kind}{constraints}{position}"
def
computedcolumnconstraint_sql(self, expression: sqlglot.expressions.ComputedColumnConstraint) -> str:
852 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 853 this = self.sql(expression, "this") 854 if expression.args.get("not_null"): 855 persisted = " PERSISTED NOT NULL" 856 elif expression.args.get("persisted"): 857 persisted = " PERSISTED" 858 else: 859 persisted = "" 860 return f"AS {this}{persisted}"
def
compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str:
def
generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str:
873 def generatedasidentitycolumnconstraint_sql( 874 self, expression: exp.GeneratedAsIdentityColumnConstraint 875 ) -> str: 876 this = "" 877 if expression.this is not None: 878 on_null = " ON NULL" if expression.args.get("on_null") else "" 879 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 880 881 start = expression.args.get("start") 882 start = f"START WITH {start}" if start else "" 883 increment = expression.args.get("increment") 884 increment = f" INCREMENT BY {increment}" if increment else "" 885 minvalue = expression.args.get("minvalue") 886 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 887 maxvalue = expression.args.get("maxvalue") 888 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 889 cycle = expression.args.get("cycle") 890 cycle_sql = "" 891 892 if cycle is not None: 893 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 894 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 895 896 sequence_opts = "" 897 if start or increment or cycle_sql: 898 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 899 sequence_opts = f" ({sequence_opts.strip()})" 900 901 expr = self.sql(expression, "expression") 902 expr = f"({expr})" if expr else "IDENTITY" 903 904 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:
930 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 931 this = self.sql(expression, "this") 932 this = f" {this}" if this else "" 933 index_type = expression.args.get("index_type") 934 index_type = f" USING {index_type}" if index_type else "" 935 on_conflict = self.sql(expression, "on_conflict") 936 on_conflict = f" {on_conflict}" if on_conflict else "" 937 return f"UNIQUE{this}{index_type}{on_conflict}"
942 def create_sql(self, expression: exp.Create) -> str: 943 kind = self.sql(expression, "kind") 944 properties = expression.args.get("properties") 945 properties_locs = self.locate_properties(properties) if properties else defaultdict() 946 947 this = self.createable_sql(expression, properties_locs) 948 949 properties_sql = "" 950 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 951 exp.Properties.Location.POST_WITH 952 ): 953 properties_sql = self.sql( 954 exp.Properties( 955 expressions=[ 956 *properties_locs[exp.Properties.Location.POST_SCHEMA], 957 *properties_locs[exp.Properties.Location.POST_WITH], 958 ] 959 ) 960 ) 961 962 begin = " BEGIN" if expression.args.get("begin") else "" 963 end = " END" if expression.args.get("end") else "" 964 965 expression_sql = self.sql(expression, "expression") 966 if expression_sql: 967 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 968 969 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 970 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 971 postalias_props_sql = self.properties( 972 exp.Properties( 973 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 974 ), 975 wrapped=False, 976 ) 977 expression_sql = f" AS {postalias_props_sql}{expression_sql}" 978 else: 979 expression_sql = f" AS{expression_sql}" 980 981 postindex_props_sql = "" 982 if properties_locs.get(exp.Properties.Location.POST_INDEX): 983 postindex_props_sql = self.properties( 984 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 985 wrapped=False, 986 prefix=" ", 987 ) 988 989 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 990 indexes = f" {indexes}" if indexes else "" 991 index_sql = indexes + postindex_props_sql 992 993 replace = " OR REPLACE" if expression.args.get("replace") else "" 994 unique = " UNIQUE" if expression.args.get("unique") else "" 995 996 postcreate_props_sql = "" 997 if properties_locs.get(exp.Properties.Location.POST_CREATE): 998 postcreate_props_sql = self.properties( 999 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 1000 sep=" ", 1001 prefix=" ", 1002 wrapped=False, 1003 ) 1004 1005 modifiers = "".join((replace, unique, postcreate_props_sql)) 1006 1007 postexpression_props_sql = "" 1008 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 1009 postexpression_props_sql = self.properties( 1010 exp.Properties( 1011 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 1012 ), 1013 sep=" ", 1014 prefix=" ", 1015 wrapped=False, 1016 ) 1017 1018 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 1019 no_schema_binding = ( 1020 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 1021 ) 1022 1023 clone = self.sql(expression, "clone") 1024 clone = f" {clone}" if clone else "" 1025 1026 expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 1027 return self.prepend_ctes(expression, expression_sql)
1029 def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str: 1030 start = self.sql(expression, "start") 1031 start = f"START WITH {start}" if start else "" 1032 increment = self.sql(expression, "increment") 1033 increment = f" INCREMENT BY {increment}" if increment else "" 1034 minvalue = self.sql(expression, "minvalue") 1035 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 1036 maxvalue = self.sql(expression, "maxvalue") 1037 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 1038 owned = self.sql(expression, "owned") 1039 owned = f" OWNED BY {owned}" if owned else "" 1040 1041 cache = expression.args.get("cache") 1042 if cache is None: 1043 cache_str = "" 1044 elif cache is True: 1045 cache_str = " CACHE" 1046 else: 1047 cache_str = f" CACHE {cache}" 1048 1049 options = self.expressions(expression, key="options", flat=True, sep=" ") 1050 options = f" {options}" if options else "" 1051 1052 return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
1054 def clone_sql(self, expression: exp.Clone) -> str: 1055 this = self.sql(expression, "this") 1056 shallow = "SHALLOW " if expression.args.get("shallow") else "" 1057 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 1058 return f"{shallow}{keyword} {this}"
1085 def cte_sql(self, expression: exp.CTE) -> str: 1086 alias = self.sql(expression, "alias") 1087 1088 materialized = expression.args.get("materialized") 1089 if materialized is False: 1090 materialized = "NOT MATERIALIZED " 1091 elif materialized: 1092 materialized = "MATERIALIZED " 1093 1094 return f"{alias} AS {materialized or ''}{self.wrap(expression)}"
1096 def tablealias_sql(self, expression: exp.TableAlias) -> str: 1097 alias = self.sql(expression, "this") 1098 columns = self.expressions(expression, key="columns", flat=True) 1099 columns = f"({columns})" if columns else "" 1100 1101 if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS: 1102 columns = "" 1103 self.unsupported("Named columns are not supported in table alias.") 1104 1105 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 1106 alias = self._next_name() 1107 1108 return f"{alias}{columns}"
1128 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 1129 this = self.sql(expression, "this") 1130 escape = expression.args.get("escape") 1131 1132 if self.dialect.UNICODE_START: 1133 escape = f" UESCAPE {self.sql(escape)}" if escape else "" 1134 return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}" 1135 1136 if escape: 1137 pattern = re.compile(rf"{escape.name}(\d+)") 1138 else: 1139 pattern = ESCAPED_UNICODE_RE 1140 1141 this = pattern.sub(r"\\u\1", this) 1142 return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}"
1154 def datatype_sql(self, expression: exp.DataType) -> str: 1155 type_value = expression.this 1156 1157 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 1158 type_sql = self.sql(expression, "kind") 1159 else: 1160 type_sql = ( 1161 self.TYPE_MAPPING.get(type_value, type_value.value) 1162 if isinstance(type_value, exp.DataType.Type) 1163 else type_value 1164 ) 1165 1166 nested = "" 1167 interior = self.expressions(expression, flat=True) 1168 values = "" 1169 1170 if interior: 1171 if expression.args.get("nested"): 1172 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 1173 if expression.args.get("values") is not None: 1174 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 1175 values = self.expressions(expression, key="values", flat=True) 1176 values = f"{delimiters[0]}{values}{delimiters[1]}" 1177 elif type_value == exp.DataType.Type.INTERVAL: 1178 nested = f" {interior}" 1179 else: 1180 nested = f"({interior})" 1181 1182 type_sql = f"{type_sql}{nested}{values}" 1183 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 1184 exp.DataType.Type.TIMETZ, 1185 exp.DataType.Type.TIMESTAMPTZ, 1186 ): 1187 type_sql = f"{type_sql} WITH TIME ZONE" 1188 1189 return type_sql
1191 def directory_sql(self, expression: exp.Directory) -> str: 1192 local = "LOCAL " if expression.args.get("local") else "" 1193 row_format = self.sql(expression, "row_format") 1194 row_format = f" {row_format}" if row_format else "" 1195 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
1197 def delete_sql(self, expression: exp.Delete) -> str: 1198 this = self.sql(expression, "this") 1199 this = f" FROM {this}" if this else "" 1200 using = self.sql(expression, "using") 1201 using = f" USING {using}" if using else "" 1202 where = self.sql(expression, "where") 1203 returning = self.sql(expression, "returning") 1204 limit = self.sql(expression, "limit") 1205 tables = self.expressions(expression, key="tables") 1206 tables = f" {tables}" if tables else "" 1207 if self.RETURNING_END: 1208 expression_sql = f"{this}{using}{where}{returning}{limit}" 1209 else: 1210 expression_sql = f"{returning}{this}{using}{where}{limit}" 1211 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
1213 def drop_sql(self, expression: exp.Drop) -> str: 1214 this = self.sql(expression, "this") 1215 expressions = self.expressions(expression, flat=True) 1216 expressions = f" ({expressions})" if expressions else "" 1217 kind = expression.args["kind"] 1218 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1219 on_cluster = self.sql(expression, "cluster") 1220 on_cluster = f" {on_cluster}" if on_cluster else "" 1221 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1222 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1223 cascade = " CASCADE" if expression.args.get("cascade") else "" 1224 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1225 purge = " PURGE" if expression.args.get("purge") else "" 1226 return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
1234 def fetch_sql(self, expression: exp.Fetch) -> str: 1235 direction = expression.args.get("direction") 1236 direction = f" {direction}" if direction else "" 1237 count = expression.args.get("count") 1238 count = f" {count}" if count else "" 1239 if expression.args.get("percent"): 1240 count = f"{count} PERCENT" 1241 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1242 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
1244 def filter_sql(self, expression: exp.Filter) -> str: 1245 if self.AGGREGATE_FILTER_SUPPORTED: 1246 this = self.sql(expression, "this") 1247 where = self.sql(expression, "expression").strip() 1248 return f"{this} FILTER({where})" 1249 1250 agg = expression.this 1251 agg_arg = agg.this 1252 cond = expression.expression.this 1253 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1254 return self.sql(agg)
1263 def indexparameters_sql(self, expression: exp.IndexParameters) -> str: 1264 using = self.sql(expression, "using") 1265 using = f" USING {using}" if using else "" 1266 columns = self.expressions(expression, key="columns", flat=True) 1267 columns = f"({columns})" if columns else "" 1268 partition_by = self.expressions(expression, key="partition_by", flat=True) 1269 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1270 where = self.sql(expression, "where") 1271 include = self.expressions(expression, key="include", flat=True) 1272 if include: 1273 include = f" INCLUDE ({include})" 1274 with_storage = self.expressions(expression, key="with_storage", flat=True) 1275 with_storage = f" WITH ({with_storage})" if with_storage else "" 1276 tablespace = self.sql(expression, "tablespace") 1277 tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else "" 1278 1279 return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}"
1281 def index_sql(self, expression: exp.Index) -> str: 1282 unique = "UNIQUE " if expression.args.get("unique") else "" 1283 primary = "PRIMARY " if expression.args.get("primary") else "" 1284 amp = "AMP " if expression.args.get("amp") else "" 1285 name = self.sql(expression, "this") 1286 name = f"{name} " if name else "" 1287 table = self.sql(expression, "table") 1288 table = f"{self.INDEX_ON} {table}" if table else "" 1289 1290 index = "INDEX " if not table else "" 1291 1292 params = self.sql(expression, "params") 1293 return f"{unique}{primary}{amp}{index}{name}{table}{params}"
1295 def identifier_sql(self, expression: exp.Identifier) -> str: 1296 text = expression.name 1297 lower = text.lower() 1298 text = lower if self.normalize and not expression.quoted else text 1299 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1300 if ( 1301 expression.quoted 1302 or self.dialect.can_identify(text, self.identify) 1303 or lower in self.RESERVED_KEYWORDS 1304 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1305 ): 1306 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1307 return text
1322 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1323 input_format = self.sql(expression, "input_format") 1324 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1325 output_format = self.sql(expression, "output_format") 1326 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1327 return self.sep().join((input_format, output_format))
1336 def properties_sql(self, expression: exp.Properties) -> str: 1337 root_properties = [] 1338 with_properties = [] 1339 1340 for p in expression.expressions: 1341 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1342 if p_loc == exp.Properties.Location.POST_WITH: 1343 with_properties.append(p) 1344 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1345 root_properties.append(p) 1346 1347 return self.root_properties( 1348 exp.Properties(expressions=root_properties) 1349 ) + 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:
1356 def properties( 1357 self, 1358 properties: exp.Properties, 1359 prefix: str = "", 1360 sep: str = ", ", 1361 suffix: str = "", 1362 wrapped: bool = True, 1363 ) -> str: 1364 if properties.expressions: 1365 expressions = self.expressions(properties, sep=sep, indent=False) 1366 if expressions: 1367 expressions = self.wrap(expressions) if wrapped else expressions 1368 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1369 return ""
1374 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1375 properties_locs = defaultdict(list) 1376 for p in properties.expressions: 1377 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1378 if p_loc != exp.Properties.Location.UNSUPPORTED: 1379 properties_locs[p_loc].append(p) 1380 else: 1381 self.unsupported(f"Unsupported property {p.key}") 1382 1383 return properties_locs
def
property_name( self, expression: sqlglot.expressions.Property, string_key: bool = False) -> str:
1390 def property_sql(self, expression: exp.Property) -> str: 1391 property_cls = expression.__class__ 1392 if property_cls == exp.Property: 1393 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1394 1395 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1396 if not property_name: 1397 self.unsupported(f"Unsupported property {expression.key}") 1398 1399 return f"{property_name}={self.sql(expression, 'this')}"
1401 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1402 if self.SUPPORTS_CREATE_TABLE_LIKE: 1403 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1404 options = f" {options}" if options else "" 1405 1406 like = f"LIKE {self.sql(expression, 'this')}{options}" 1407 if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema): 1408 like = f"({like})" 1409 1410 return like 1411 1412 if expression.expressions: 1413 self.unsupported("Transpilation of LIKE property options is unsupported") 1414 1415 select = exp.select("*").from_(expression.this).limit(0) 1416 return f"AS {self.sql(select)}"
1423 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1424 no = "NO " if expression.args.get("no") else "" 1425 local = expression.args.get("local") 1426 local = f"{local} " if local else "" 1427 dual = "DUAL " if expression.args.get("dual") else "" 1428 before = "BEFORE " if expression.args.get("before") else "" 1429 after = "AFTER " if expression.args.get("after") else "" 1430 return f"{no}{local}{dual}{before}{after}JOURNAL"
def
mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str:
1446 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1447 if expression.args.get("no"): 1448 return "NO MERGEBLOCKRATIO" 1449 if expression.args.get("default"): 1450 return "DEFAULT MERGEBLOCKRATIO" 1451 1452 percent = " PERCENT" if expression.args.get("percent") else "" 1453 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
1455 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1456 default = expression.args.get("default") 1457 minimum = expression.args.get("minimum") 1458 maximum = expression.args.get("maximum") 1459 if default or minimum or maximum: 1460 if default: 1461 prop = "DEFAULT" 1462 elif minimum: 1463 prop = "MINIMUM" 1464 else: 1465 prop = "MAXIMUM" 1466 return f"{prop} DATABLOCKSIZE" 1467 units = expression.args.get("units") 1468 units = f" {units}" if units else "" 1469 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
def
blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str:
1471 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1472 autotemp = expression.args.get("autotemp") 1473 always = expression.args.get("always") 1474 default = expression.args.get("default") 1475 manual = expression.args.get("manual") 1476 never = expression.args.get("never") 1477 1478 if autotemp is not None: 1479 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1480 elif always: 1481 prop = "ALWAYS" 1482 elif default: 1483 prop = "DEFAULT" 1484 elif manual: 1485 prop = "MANUAL" 1486 elif never: 1487 prop = "NEVER" 1488 return f"BLOCKCOMPRESSION={prop}"
def
isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str:
1490 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1491 no = expression.args.get("no") 1492 no = " NO" if no else "" 1493 concurrent = expression.args.get("concurrent") 1494 concurrent = " CONCURRENT" if concurrent else "" 1495 target = self.sql(expression, "target") 1496 target = f" {target}" if target else "" 1497 return f"WITH{no}{concurrent} ISOLATED LOADING{target}"
1499 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1500 if isinstance(expression.this, list): 1501 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1502 if expression.this: 1503 modulus = self.sql(expression, "this") 1504 remainder = self.sql(expression, "expression") 1505 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1506 1507 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1508 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1509 return f"FROM ({from_expressions}) TO ({to_expressions})"
1511 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1512 this = self.sql(expression, "this") 1513 1514 for_values_or_default = expression.expression 1515 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1516 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1517 else: 1518 for_values_or_default = " DEFAULT" 1519 1520 return f"PARTITION OF {this}{for_values_or_default}"
1522 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1523 kind = expression.args.get("kind") 1524 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1525 for_or_in = expression.args.get("for_or_in") 1526 for_or_in = f" {for_or_in}" if for_or_in else "" 1527 lock_type = expression.args.get("lock_type") 1528 override = " OVERRIDE" if expression.args.get("override") else "" 1529 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
1531 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1532 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1533 statistics = expression.args.get("statistics") 1534 statistics_sql = "" 1535 if statistics is not None: 1536 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1537 return f"{data_sql}{statistics_sql}"
def
withsystemversioningproperty_sql( self, expression: sqlglot.expressions.WithSystemVersioningProperty) -> str:
1539 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1540 sql = "WITH(SYSTEM_VERSIONING=ON" 1541 1542 if expression.this: 1543 history_table = self.sql(expression, "this") 1544 sql = f"{sql}(HISTORY_TABLE={history_table}" 1545 1546 if expression.expression: 1547 data_consistency_check = self.sql(expression, "expression") 1548 sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}" 1549 1550 sql = f"{sql})" 1551 1552 return f"{sql})"
1554 def insert_sql(self, expression: exp.Insert) -> str: 1555 hint = self.sql(expression, "hint") 1556 overwrite = expression.args.get("overwrite") 1557 1558 if isinstance(expression.this, exp.Directory): 1559 this = " OVERWRITE" if overwrite else " INTO" 1560 else: 1561 this = self.INSERT_OVERWRITE if overwrite else " INTO" 1562 1563 stored = self.sql(expression, "stored") 1564 stored = f" {stored}" if stored else "" 1565 alternative = expression.args.get("alternative") 1566 alternative = f" OR {alternative}" if alternative else "" 1567 ignore = " IGNORE" if expression.args.get("ignore") else "" 1568 is_function = expression.args.get("is_function") 1569 if is_function: 1570 this = f"{this} FUNCTION" 1571 this = f"{this} {self.sql(expression, 'this')}" 1572 1573 exists = " IF EXISTS" if expression.args.get("exists") else "" 1574 where = self.sql(expression, "where") 1575 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1576 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1577 on_conflict = self.sql(expression, "conflict") 1578 on_conflict = f" {on_conflict}" if on_conflict else "" 1579 by_name = " BY NAME" if expression.args.get("by_name") else "" 1580 returning = self.sql(expression, "returning") 1581 1582 if self.RETURNING_END: 1583 expression_sql = f"{expression_sql}{on_conflict}{returning}" 1584 else: 1585 expression_sql = f"{returning}{expression_sql}{on_conflict}" 1586 1587 sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{where}{expression_sql}" 1588 return self.prepend_ctes(expression, sql)
1612 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1613 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1614 1615 constraint = self.sql(expression, "constraint") 1616 constraint = f" ON CONSTRAINT {constraint}" if constraint else "" 1617 1618 conflict_keys = self.expressions(expression, key="conflict_keys", flat=True) 1619 conflict_keys = f"({conflict_keys}) " if conflict_keys else " " 1620 action = self.sql(expression, "action") 1621 1622 expressions = self.expressions(expression, flat=True) 1623 if expressions: 1624 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1625 expressions = f" {set_keyword}{expressions}" 1626 1627 return f"{conflict}{constraint}{conflict_keys}{action}{expressions}"
def
rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str:
1632 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1633 fields = self.sql(expression, "fields") 1634 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1635 escaped = self.sql(expression, "escaped") 1636 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1637 items = self.sql(expression, "collection_items") 1638 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1639 keys = self.sql(expression, "map_keys") 1640 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1641 lines = self.sql(expression, "lines") 1642 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1643 null = self.sql(expression, "null") 1644 null = f" NULL DEFINED AS {null}" if null else "" 1645 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
1673 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1674 table = self.table_parts(expression) 1675 only = "ONLY " if expression.args.get("only") else "" 1676 partition = self.sql(expression, "partition") 1677 partition = f" {partition}" if partition else "" 1678 version = self.sql(expression, "version") 1679 version = f" {version}" if version else "" 1680 alias = self.sql(expression, "alias") 1681 alias = f"{sep}{alias}" if alias else "" 1682 hints = self.expressions(expression, key="hints", sep=" ") 1683 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1684 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 1685 pivots = f" {pivots}" if pivots else "" 1686 joins = self.indent( 1687 self.expressions(expression, key="joins", sep="", flat=True), skip_first=True 1688 ) 1689 laterals = self.expressions(expression, key="laterals", sep="") 1690 1691 file_format = self.sql(expression, "format") 1692 if file_format: 1693 pattern = self.sql(expression, "pattern") 1694 pattern = f", PATTERN => {pattern}" if pattern else "" 1695 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1696 1697 ordinality = expression.args.get("ordinality") or "" 1698 if ordinality: 1699 ordinality = f" WITH ORDINALITY{alias}" 1700 alias = "" 1701 1702 when = self.sql(expression, "when") 1703 if when: 1704 table = f"{table} {when}" 1705 1706 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:
1708 def tablesample_sql( 1709 self, 1710 expression: exp.TableSample, 1711 sep: str = " AS ", 1712 tablesample_keyword: t.Optional[str] = None, 1713 ) -> str: 1714 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1715 table = expression.this.copy() 1716 table.set("alias", None) 1717 this = self.sql(table) 1718 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1719 else: 1720 this = self.sql(expression, "this") 1721 alias = "" 1722 1723 method = self.sql(expression, "method") 1724 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1725 numerator = self.sql(expression, "bucket_numerator") 1726 denominator = self.sql(expression, "bucket_denominator") 1727 field = self.sql(expression, "bucket_field") 1728 field = f" ON {field}" if field else "" 1729 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1730 seed = self.sql(expression, "seed") 1731 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1732 1733 size = self.sql(expression, "size") 1734 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1735 size = f"{size} ROWS" 1736 1737 percent = self.sql(expression, "percent") 1738 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1739 percent = f"{percent} PERCENT" 1740 1741 expr = f"{bucket}{percent}{size}" 1742 if self.TABLESAMPLE_REQUIRES_PARENS: 1743 expr = f"({expr})" 1744 1745 return ( 1746 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1747 )
1749 def pivot_sql(self, expression: exp.Pivot) -> str: 1750 expressions = self.expressions(expression, flat=True) 1751 1752 if expression.this: 1753 this = self.sql(expression, "this") 1754 if not expressions: 1755 return f"UNPIVOT {this}" 1756 1757 on = f"{self.seg('ON')} {expressions}" 1758 using = self.expressions(expression, key="using", flat=True) 1759 using = f"{self.seg('USING')} {using}" if using else "" 1760 group = self.sql(expression, "group") 1761 return f"PIVOT {this}{on}{using}{group}" 1762 1763 alias = self.sql(expression, "alias") 1764 alias = f" AS {alias}" if alias else "" 1765 direction = "UNPIVOT" if expression.unpivot else "PIVOT" 1766 field = self.sql(expression, "field") 1767 include_nulls = expression.args.get("include_nulls") 1768 if include_nulls is not None: 1769 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1770 else: 1771 nulls = "" 1772 return f"{direction}{nulls}({expressions} FOR {field}){alias}"
1783 def update_sql(self, expression: exp.Update) -> str: 1784 this = self.sql(expression, "this") 1785 set_sql = self.expressions(expression, flat=True) 1786 from_sql = self.sql(expression, "from") 1787 where_sql = self.sql(expression, "where") 1788 returning = self.sql(expression, "returning") 1789 order = self.sql(expression, "order") 1790 limit = self.sql(expression, "limit") 1791 if self.RETURNING_END: 1792 expression_sql = f"{from_sql}{where_sql}{returning}" 1793 else: 1794 expression_sql = f"{returning}{from_sql}{where_sql}" 1795 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1796 return self.prepend_ctes(expression, sql)
1798 def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str: 1799 values_as_table = values_as_table and self.VALUES_AS_TABLE 1800 1801 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1802 if values_as_table or not expression.find_ancestor(exp.From, exp.Join): 1803 args = self.expressions(expression) 1804 alias = self.sql(expression, "alias") 1805 values = f"VALUES{self.seg('')}{args}" 1806 values = ( 1807 f"({values})" 1808 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1809 else values 1810 ) 1811 return f"{values} AS {alias}" if alias else values 1812 1813 # Converts `VALUES...` expression into a series of select unions. 1814 alias_node = expression.args.get("alias") 1815 column_names = alias_node and alias_node.columns 1816 1817 selects: t.List[exp.Query] = [] 1818 1819 for i, tup in enumerate(expression.expressions): 1820 row = tup.expressions 1821 1822 if i == 0 and column_names: 1823 row = [ 1824 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1825 ] 1826 1827 selects.append(exp.Select(expressions=row)) 1828 1829 if self.pretty: 1830 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1831 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1832 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1833 query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1834 return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False)) 1835 1836 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1837 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1838 return f"({unions}){alias}"
1851 def group_sql(self, expression: exp.Group) -> str: 1852 group_by_all = expression.args.get("all") 1853 if group_by_all is True: 1854 modifier = " ALL" 1855 elif group_by_all is False: 1856 modifier = " DISTINCT" 1857 else: 1858 modifier = "" 1859 1860 group_by = self.op_expressions(f"GROUP BY{modifier}", expression) 1861 1862 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1863 grouping_sets = ( 1864 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1865 ) 1866 1867 cube = expression.args.get("cube", []) 1868 if seq_get(cube, 0) is True: 1869 return f"{group_by}{self.seg('WITH CUBE')}" 1870 else: 1871 cube_sql = self.expressions(expression, key="cube", indent=False) 1872 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1873 1874 rollup = expression.args.get("rollup", []) 1875 if seq_get(rollup, 0) is True: 1876 return f"{group_by}{self.seg('WITH ROLLUP')}" 1877 else: 1878 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1879 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1880 1881 groupings = csv( 1882 grouping_sets, 1883 cube_sql, 1884 rollup_sql, 1885 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1886 sep=self.GROUPINGS_SEP, 1887 ) 1888 1889 if expression.args.get("expressions") and groupings: 1890 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1891 1892 return f"{group_by}{groupings}"
1898 def connect_sql(self, expression: exp.Connect) -> str: 1899 start = self.sql(expression, "start") 1900 start = self.seg(f"START WITH {start}") if start else "" 1901 nocycle = " NOCYCLE" if expression.args.get("nocycle") else "" 1902 connect = self.sql(expression, "connect") 1903 connect = self.seg(f"CONNECT BY{nocycle} {connect}") 1904 return start + connect
1909 def join_sql(self, expression: exp.Join) -> str: 1910 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1911 side = None 1912 else: 1913 side = expression.side 1914 1915 op_sql = " ".join( 1916 op 1917 for op in ( 1918 expression.method, 1919 "GLOBAL" if expression.args.get("global") else None, 1920 side, 1921 expression.kind, 1922 expression.hint if self.JOIN_HINTS else None, 1923 ) 1924 if op 1925 ) 1926 match_cond = self.sql(expression, "match_condition") 1927 match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else "" 1928 on_sql = self.sql(expression, "on") 1929 using = expression.args.get("using") 1930 1931 if not on_sql and using: 1932 on_sql = csv(*(self.sql(column) for column in using)) 1933 1934 this = expression.this 1935 this_sql = self.sql(this) 1936 1937 if on_sql: 1938 on_sql = self.indent(on_sql, skip_first=True) 1939 space = self.seg(" " * self.pad) if self.pretty else " " 1940 if using: 1941 on_sql = f"{space}USING ({on_sql})" 1942 else: 1943 on_sql = f"{space}ON {on_sql}" 1944 elif not op_sql: 1945 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 1946 return f" {this_sql}" 1947 1948 return f", {this_sql}" 1949 1950 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1951 return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}"
1958 def lateral_op(self, expression: exp.Lateral) -> str: 1959 cross_apply = expression.args.get("cross_apply") 1960 1961 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 1962 if cross_apply is True: 1963 op = "INNER JOIN " 1964 elif cross_apply is False: 1965 op = "LEFT JOIN " 1966 else: 1967 op = "" 1968 1969 return f"{op}LATERAL"
1971 def lateral_sql(self, expression: exp.Lateral) -> str: 1972 this = self.sql(expression, "this") 1973 1974 if expression.args.get("view"): 1975 alias = expression.args["alias"] 1976 columns = self.expressions(alias, key="columns", flat=True) 1977 table = f" {alias.name}" if alias.name else "" 1978 columns = f" AS {columns}" if columns else "" 1979 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1980 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1981 1982 alias = self.sql(expression, "alias") 1983 alias = f" AS {alias}" if alias else "" 1984 return f"{self.lateral_op(expression)} {this}{alias}"
1986 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1987 this = self.sql(expression, "this") 1988 1989 args = [ 1990 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1991 for e in (expression.args.get(k) for k in ("offset", "expression")) 1992 if e 1993 ] 1994 1995 args_sql = ", ".join(self.sql(e) for e in args) 1996 args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql 1997 expressions = self.expressions(expression, flat=True) 1998 expressions = f" BY {expressions}" if expressions else "" 1999 2000 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"
2002 def offset_sql(self, expression: exp.Offset) -> str: 2003 this = self.sql(expression, "this") 2004 value = expression.expression 2005 value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value 2006 expressions = self.expressions(expression, flat=True) 2007 expressions = f" BY {expressions}" if expressions else "" 2008 return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
2010 def setitem_sql(self, expression: exp.SetItem) -> str: 2011 kind = self.sql(expression, "kind") 2012 kind = f"{kind} " if kind else "" 2013 this = self.sql(expression, "this") 2014 expressions = self.expressions(expression) 2015 collate = self.sql(expression, "collate") 2016 collate = f" COLLATE {collate}" if collate else "" 2017 global_ = "GLOBAL " if expression.args.get("global") else "" 2018 return f"{global_}{kind}{this}{expressions}{collate}"
2020 def set_sql(self, expression: exp.Set) -> str: 2021 expressions = ( 2022 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 2023 ) 2024 tag = " TAG" if expression.args.get("tag") else "" 2025 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
2030 def lock_sql(self, expression: exp.Lock) -> str: 2031 if not self.LOCKING_READS_SUPPORTED: 2032 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 2033 return "" 2034 2035 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 2036 expressions = self.expressions(expression, flat=True) 2037 expressions = f" OF {expressions}" if expressions else "" 2038 wait = expression.args.get("wait") 2039 2040 if wait is not None: 2041 if isinstance(wait, exp.Literal): 2042 wait = f" WAIT {self.sql(wait)}" 2043 else: 2044 wait = " NOWAIT" if wait else " SKIP LOCKED" 2045 2046 return f"{lock_type}{expressions}{wait or ''}"
def
escape_str(self, text: str, escape_backslash: bool = True) -> str:
2054 def escape_str(self, text: str, escape_backslash: bool = True) -> str: 2055 if self.dialect.ESCAPED_SEQUENCES: 2056 to_escaped = self.dialect.ESCAPED_SEQUENCES 2057 text = "".join( 2058 to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text 2059 ) 2060 2061 return self._replace_line_breaks(text).replace( 2062 self.dialect.QUOTE_END, self._escaped_quote_end 2063 )
2065 def loaddata_sql(self, expression: exp.LoadData) -> str: 2066 local = " LOCAL" if expression.args.get("local") else "" 2067 inpath = f" INPATH {self.sql(expression, 'inpath')}" 2068 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 2069 this = f" INTO TABLE {self.sql(expression, 'this')}" 2070 partition = self.sql(expression, "partition") 2071 partition = f" {partition}" if partition else "" 2072 input_format = self.sql(expression, "input_format") 2073 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 2074 serde = self.sql(expression, "serde") 2075 serde = f" SERDE {serde}" if serde else "" 2076 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
2084 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 2085 this = self.sql(expression, "this") 2086 this = f"{this} " if this else this 2087 siblings = "SIBLINGS " if expression.args.get("siblings") else "" 2088 order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore 2089 interpolated_values = [ 2090 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 2091 for named_expression in expression.args.get("interpolate") or [] 2092 ] 2093 interpolate = ( 2094 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 2095 ) 2096 return f"{order}{interpolate}"
2098 def withfill_sql(self, expression: exp.WithFill) -> str: 2099 from_sql = self.sql(expression, "from") 2100 from_sql = f" FROM {from_sql}" if from_sql else "" 2101 to_sql = self.sql(expression, "to") 2102 to_sql = f" TO {to_sql}" if to_sql else "" 2103 step_sql = self.sql(expression, "step") 2104 step_sql = f" STEP {step_sql}" if step_sql else "" 2105 return f"WITH FILL{from_sql}{to_sql}{step_sql}"
2116 def ordered_sql(self, expression: exp.Ordered) -> str: 2117 desc = expression.args.get("desc") 2118 asc = not desc 2119 2120 nulls_first = expression.args.get("nulls_first") 2121 nulls_last = not nulls_first 2122 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 2123 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 2124 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 2125 2126 this = self.sql(expression, "this") 2127 2128 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 2129 nulls_sort_change = "" 2130 if nulls_first and ( 2131 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 2132 ): 2133 nulls_sort_change = " NULLS FIRST" 2134 elif ( 2135 nulls_last 2136 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 2137 and not nulls_are_last 2138 ): 2139 nulls_sort_change = " NULLS LAST" 2140 2141 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 2142 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 2143 window = expression.find_ancestor(exp.Window, exp.Select) 2144 if isinstance(window, exp.Window) and window.args.get("spec"): 2145 self.unsupported( 2146 f"'{nulls_sort_change.strip()}' translation not supported in window functions" 2147 ) 2148 nulls_sort_change = "" 2149 elif self.NULL_ORDERING_SUPPORTED is None: 2150 if expression.this.is_int: 2151 self.unsupported( 2152 f"'{nulls_sort_change.strip()}' translation not supported with positional ordering" 2153 ) 2154 elif not isinstance(expression.this, exp.Rand): 2155 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 2156 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 2157 nulls_sort_change = "" 2158 2159 with_fill = self.sql(expression, "with_fill") 2160 with_fill = f" {with_fill}" if with_fill else "" 2161 2162 return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
2172 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 2173 partition = self.partition_by_sql(expression) 2174 order = self.sql(expression, "order") 2175 measures = self.expressions(expression, key="measures") 2176 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 2177 rows = self.sql(expression, "rows") 2178 rows = self.seg(rows) if rows else "" 2179 after = self.sql(expression, "after") 2180 after = self.seg(after) if after else "" 2181 pattern = self.sql(expression, "pattern") 2182 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 2183 definition_sqls = [ 2184 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 2185 for definition in expression.args.get("define", []) 2186 ] 2187 definitions = self.expressions(sqls=definition_sqls) 2188 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 2189 body = "".join( 2190 ( 2191 partition, 2192 order, 2193 measures, 2194 rows, 2195 after, 2196 pattern, 2197 define, 2198 ) 2199 ) 2200 alias = self.sql(expression, "alias") 2201 alias = f" {alias}" if alias else "" 2202 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
2204 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 2205 limit = expression.args.get("limit") 2206 2207 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 2208 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 2209 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 2210 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 2211 2212 options = self.expressions(expression, key="options") 2213 if options: 2214 options = f" OPTION{self.wrap(options)}" 2215 2216 return csv( 2217 *sqls, 2218 *[self.sql(join) for join in expression.args.get("joins") or []], 2219 self.sql(expression, "connect"), 2220 self.sql(expression, "match"), 2221 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 2222 self.sql(expression, "prewhere"), 2223 self.sql(expression, "where"), 2224 self.sql(expression, "group"), 2225 self.sql(expression, "having"), 2226 *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()], 2227 self.sql(expression, "order"), 2228 *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit), 2229 *self.after_limit_modifiers(expression), 2230 options, 2231 sep="", 2232 )
def
offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]:
2237 def offset_limit_modifiers( 2238 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 2239 ) -> t.List[str]: 2240 return [ 2241 self.sql(expression, "offset") if fetch else self.sql(limit), 2242 self.sql(limit) if fetch else self.sql(expression, "offset"), 2243 ]
2250 def select_sql(self, expression: exp.Select) -> str: 2251 into = expression.args.get("into") 2252 if not self.SUPPORTS_SELECT_INTO and into: 2253 into.pop() 2254 2255 hint = self.sql(expression, "hint") 2256 distinct = self.sql(expression, "distinct") 2257 distinct = f" {distinct}" if distinct else "" 2258 kind = self.sql(expression, "kind") 2259 2260 limit = expression.args.get("limit") 2261 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP: 2262 top = self.limit_sql(limit, top=True) 2263 limit.pop() 2264 else: 2265 top = "" 2266 2267 expressions = self.expressions(expression) 2268 2269 if kind: 2270 if kind in self.SELECT_KINDS: 2271 kind = f" AS {kind}" 2272 else: 2273 if kind == "STRUCT": 2274 expressions = self.expressions( 2275 sqls=[ 2276 self.sql( 2277 exp.Struct( 2278 expressions=[ 2279 exp.PropertyEQ(this=e.args.get("alias"), expression=e.this) 2280 if isinstance(e, exp.Alias) 2281 else e 2282 for e in expression.expressions 2283 ] 2284 ) 2285 ) 2286 ] 2287 ) 2288 kind = "" 2289 2290 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2291 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2292 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2293 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2294 sql = self.query_modifiers( 2295 expression, 2296 f"SELECT{top_distinct}{kind}{expressions}", 2297 self.sql(expression, "into", comment=False), 2298 self.sql(expression, "from", comment=False), 2299 ) 2300 2301 sql = self.prepend_ctes(expression, sql) 2302 2303 if not self.SUPPORTS_SELECT_INTO and into: 2304 if into.args.get("temporary"): 2305 table_kind = " TEMPORARY" 2306 elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"): 2307 table_kind = " UNLOGGED" 2308 else: 2309 table_kind = "" 2310 sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}" 2311 2312 return sql
2324 def star_sql(self, expression: exp.Star) -> str: 2325 except_ = self.expressions(expression, key="except", flat=True) 2326 except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else "" 2327 replace = self.expressions(expression, key="replace", flat=True) 2328 replace = f"{self.seg('REPLACE')} ({replace})" if replace else "" 2329 rename = self.expressions(expression, key="rename", flat=True) 2330 rename = f"{self.seg('RENAME')} ({rename})" if rename else "" 2331 return f"*{except_}{replace}{rename}"
2347 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2348 alias = self.sql(expression, "alias") 2349 alias = f"{sep}{alias}" if alias else "" 2350 2351 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2352 pivots = f" {pivots}" if pivots else "" 2353 2354 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2355 return self.prepend_ctes(expression, sql)
2361 def set_operations(self, expression: exp.Union) -> str: 2362 if not self.OUTER_UNION_MODIFIERS: 2363 limit = expression.args.get("limit") 2364 order = expression.args.get("order") 2365 2366 if limit or order: 2367 select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False) 2368 2369 if limit: 2370 select = select.limit(limit.pop(), copy=False) 2371 if order: 2372 select = select.order_by(order.pop(), copy=False) 2373 return self.sql(select) 2374 2375 sqls: t.List[str] = [] 2376 stack: t.List[t.Union[str, exp.Expression]] = [expression] 2377 2378 while stack: 2379 node = stack.pop() 2380 2381 if isinstance(node, exp.Union): 2382 stack.append(node.expression) 2383 stack.append( 2384 self.maybe_comment( 2385 getattr(self, f"{node.key}_op")(node), 2386 comments=node.comments, 2387 separated=True, 2388 ) 2389 ) 2390 stack.append(node.this) 2391 else: 2392 sqls.append(self.sql(node)) 2393 2394 this = self.sep().join(sqls) 2395 this = self.query_modifiers(expression, this) 2396 return self.prepend_ctes(expression, this)
2407 def unnest_sql(self, expression: exp.Unnest) -> str: 2408 args = self.expressions(expression, flat=True) 2409 2410 alias = expression.args.get("alias") 2411 offset = expression.args.get("offset") 2412 2413 if self.UNNEST_WITH_ORDINALITY: 2414 if alias and isinstance(offset, exp.Expression): 2415 alias.append("columns", offset) 2416 2417 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2418 columns = alias.columns 2419 alias = self.sql(columns[0]) if columns else "" 2420 else: 2421 alias = self.sql(alias) 2422 2423 alias = f" AS {alias}" if alias else alias 2424 if self.UNNEST_WITH_ORDINALITY: 2425 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2426 else: 2427 if isinstance(offset, exp.Expression): 2428 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2429 elif offset: 2430 suffix = f"{alias} WITH OFFSET" 2431 else: 2432 suffix = alias 2433 2434 return f"UNNEST({args}){suffix}"
2443 def window_sql(self, expression: exp.Window) -> str: 2444 this = self.sql(expression, "this") 2445 partition = self.partition_by_sql(expression) 2446 order = expression.args.get("order") 2447 order = self.order_sql(order, flat=True) if order else "" 2448 spec = self.sql(expression, "spec") 2449 alias = self.sql(expression, "alias") 2450 over = self.sql(expression, "over") or "OVER" 2451 2452 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2453 2454 first = expression.args.get("first") 2455 if first is None: 2456 first = "" 2457 else: 2458 first = "FIRST" if first else "LAST" 2459 2460 if not partition and not order and not spec and alias: 2461 return f"{this} {alias}" 2462 2463 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2464 return f"{this} ({args})"
def
partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
2470 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2471 kind = self.sql(expression, "kind") 2472 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2473 end = ( 2474 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2475 or "CURRENT ROW" 2476 ) 2477 return f"{kind} BETWEEN {start} AND {end}"
def
bracket_offset_expressions( self, expression: sqlglot.expressions.Bracket) -> List[sqlglot.expressions.Expression]:
2505 def any_sql(self, expression: exp.Any) -> str: 2506 this = self.sql(expression, "this") 2507 if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)): 2508 if isinstance(expression.this, exp.UNWRAPPED_QUERIES): 2509 this = self.wrap(this) 2510 return f"ANY{this}" 2511 return f"ANY {this}"
2516 def case_sql(self, expression: exp.Case) -> str: 2517 this = self.sql(expression, "this") 2518 statements = [f"CASE {this}" if this else "CASE"] 2519 2520 for e in expression.args["ifs"]: 2521 statements.append(f"WHEN {self.sql(e, 'this')}") 2522 statements.append(f"THEN {self.sql(e, 'true')}") 2523 2524 default = self.sql(expression, "default") 2525 2526 if default: 2527 statements.append(f"ELSE {default}") 2528 2529 statements.append("END") 2530 2531 if self.pretty and self.too_wide(statements): 2532 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2533 2534 return " ".join(statements)
2551 def trim_sql(self, expression: exp.Trim) -> str: 2552 trim_type = self.sql(expression, "position") 2553 2554 if trim_type == "LEADING": 2555 return self.func("LTRIM", expression.this) 2556 elif trim_type == "TRAILING": 2557 return self.func("RTRIM", expression.this) 2558 else: 2559 return self.func("TRIM", expression.this, expression.expression)
def
convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]:
2561 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2562 args = expression.expressions 2563 if isinstance(expression, exp.ConcatWs): 2564 args = args[1:] # Skip the delimiter 2565 2566 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2567 args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args] 2568 2569 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2570 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2571 2572 return args
2574 def concat_sql(self, expression: exp.Concat) -> str: 2575 expressions = self.convert_concat_args(expression) 2576 2577 # Some dialects don't allow a single-argument CONCAT call 2578 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2579 return self.sql(expressions[0]) 2580 2581 return self.func("CONCAT", *expressions)
2592 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2593 expressions = self.expressions(expression, flat=True) 2594 reference = self.sql(expression, "reference") 2595 reference = f" {reference}" if reference else "" 2596 delete = self.sql(expression, "delete") 2597 delete = f" ON DELETE {delete}" if delete else "" 2598 update = self.sql(expression, "update") 2599 update = f" ON UPDATE {update}" if update else "" 2600 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
2602 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2603 expressions = self.expressions(expression, flat=True) 2604 options = self.expressions(expression, key="options", flat=True, sep=" ") 2605 options = f" {options}" if options else "" 2606 return f"PRIMARY KEY ({expressions}){options}"
2623 def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str: 2624 if isinstance(expression, exp.JSONPathPart): 2625 transform = self.TRANSFORMS.get(expression.__class__) 2626 if not callable(transform): 2627 self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}") 2628 return "" 2629 2630 return transform(self, expression) 2631 2632 if isinstance(expression, int): 2633 return str(expression) 2634 2635 if self.JSON_PATH_SINGLE_QUOTE_ESCAPE: 2636 escaped = expression.replace("'", "\\'") 2637 escaped = f"\\'{expression}\\'" 2638 else: 2639 escaped = expression.replace('"', '\\"') 2640 escaped = f'"{escaped}"' 2641 2642 return escaped
def
jsonobject_sql( self, expression: sqlglot.expressions.JSONObject | sqlglot.expressions.JSONObjectAgg) -> str:
2647 def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str: 2648 null_handling = expression.args.get("null_handling") 2649 null_handling = f" {null_handling}" if null_handling else "" 2650 2651 unique_keys = expression.args.get("unique_keys") 2652 if unique_keys is not None: 2653 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2654 else: 2655 unique_keys = "" 2656 2657 return_type = self.sql(expression, "return_type") 2658 return_type = f" RETURNING {return_type}" if return_type else "" 2659 encoding = self.sql(expression, "encoding") 2660 encoding = f" ENCODING {encoding}" if encoding else "" 2661 2662 return self.func( 2663 "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG", 2664 *expression.expressions, 2665 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2666 )
2671 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2672 null_handling = expression.args.get("null_handling") 2673 null_handling = f" {null_handling}" if null_handling else "" 2674 return_type = self.sql(expression, "return_type") 2675 return_type = f" RETURNING {return_type}" if return_type else "" 2676 strict = " STRICT" if expression.args.get("strict") else "" 2677 return self.func( 2678 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2679 )
2681 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2682 this = self.sql(expression, "this") 2683 order = self.sql(expression, "order") 2684 null_handling = expression.args.get("null_handling") 2685 null_handling = f" {null_handling}" if null_handling else "" 2686 return_type = self.sql(expression, "return_type") 2687 return_type = f" RETURNING {return_type}" if return_type else "" 2688 strict = " STRICT" if expression.args.get("strict") else "" 2689 return self.func( 2690 "JSON_ARRAYAGG", 2691 this, 2692 suffix=f"{order}{null_handling}{return_type}{strict})", 2693 )
2695 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2696 path = self.sql(expression, "path") 2697 path = f" PATH {path}" if path else "" 2698 nested_schema = self.sql(expression, "nested_schema") 2699 2700 if nested_schema: 2701 return f"NESTED{path} {nested_schema}" 2702 2703 this = self.sql(expression, "this") 2704 kind = self.sql(expression, "kind") 2705 kind = f" {kind}" if kind else "" 2706 return f"{this}{kind}{path}"
2711 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2712 this = self.sql(expression, "this") 2713 path = self.sql(expression, "path") 2714 path = f", {path}" if path else "" 2715 error_handling = expression.args.get("error_handling") 2716 error_handling = f" {error_handling}" if error_handling else "" 2717 empty_handling = expression.args.get("empty_handling") 2718 empty_handling = f" {empty_handling}" if empty_handling else "" 2719 schema = self.sql(expression, "schema") 2720 return self.func( 2721 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2722 )
2724 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2725 this = self.sql(expression, "this") 2726 kind = self.sql(expression, "kind") 2727 path = self.sql(expression, "path") 2728 path = f" {path}" if path else "" 2729 as_json = " AS JSON" if expression.args.get("as_json") else "" 2730 return f"{this} {kind}{path}{as_json}"
2732 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2733 this = self.sql(expression, "this") 2734 path = self.sql(expression, "path") 2735 path = f", {path}" if path else "" 2736 expressions = self.expressions(expression) 2737 with_ = ( 2738 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2739 if expressions 2740 else "" 2741 ) 2742 return f"OPENJSON({this}{path}){with_}"
2744 def in_sql(self, expression: exp.In) -> str: 2745 query = expression.args.get("query") 2746 unnest = expression.args.get("unnest") 2747 field = expression.args.get("field") 2748 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2749 2750 if query: 2751 in_sql = self.sql(query) 2752 elif unnest: 2753 in_sql = self.in_unnest_op(unnest) 2754 elif field: 2755 in_sql = self.sql(field) 2756 else: 2757 in_sql = f"({self.expressions(expression, flat=True)})" 2758 2759 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
2764 def interval_sql(self, expression: exp.Interval) -> str: 2765 unit = self.sql(expression, "unit") 2766 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2767 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2768 unit = f" {unit}" if unit else "" 2769 2770 if self.SINGLE_STRING_INTERVAL: 2771 this = expression.this.name if expression.this else "" 2772 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2773 2774 this = self.sql(expression, "this") 2775 if this: 2776 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2777 this = f" {this}" if unwrapped else f" ({this})" 2778 2779 return f"INTERVAL{this}{unit}"
2784 def reference_sql(self, expression: exp.Reference) -> str: 2785 this = self.sql(expression, "this") 2786 expressions = self.expressions(expression, flat=True) 2787 expressions = f"({expressions})" if expressions else "" 2788 options = self.expressions(expression, key="options", flat=True, sep=" ") 2789 options = f" {options}" if options else "" 2790 return f"REFERENCES {this}{expressions}{options}"
2813 def pivotalias_sql(self, expression: exp.PivotAlias) -> str: 2814 alias = expression.args["alias"] 2815 identifier_alias = isinstance(alias, exp.Identifier) 2816 2817 if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2818 alias.replace(exp.Literal.string(alias.output_name)) 2819 elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2820 alias.replace(exp.to_identifier(alias.output_name)) 2821 2822 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:
2860 def connector_sql( 2861 self, 2862 expression: exp.Connector, 2863 op: str, 2864 stack: t.Optional[t.List[str | exp.Expression]] = None, 2865 ) -> str: 2866 if stack is not None: 2867 if expression.expressions: 2868 stack.append(self.expressions(expression, sep=f" {op} ")) 2869 else: 2870 stack.append(expression.right) 2871 if expression.comments and self.comments: 2872 for comment in expression.comments: 2873 if comment: 2874 op += f" /*{self.pad_comment(comment)}*/" 2875 stack.extend((op, expression.left)) 2876 return op 2877 2878 stack = [expression] 2879 sqls: t.List[str] = [] 2880 ops = set() 2881 2882 while stack: 2883 node = stack.pop() 2884 if isinstance(node, exp.Connector): 2885 ops.add(getattr(self, f"{node.key}_sql")(node, stack)) 2886 else: 2887 sql = self.sql(node) 2888 if sqls and sqls[-1] in ops: 2889 sqls[-1] += f" {sql}" 2890 else: 2891 sqls.append(sql) 2892 2893 sep = "\n" if self.pretty and self.too_wide(sqls) else " " 2894 return sep.join(sqls)
def
cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str:
2914 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2915 format_sql = self.sql(expression, "format") 2916 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2917 to_sql = self.sql(expression, "to") 2918 to_sql = f" {to_sql}" if to_sql else "" 2919 action = self.sql(expression, "action") 2920 action = f" {action}" if action else "" 2921 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql}{action})"
2938 def comment_sql(self, expression: exp.Comment) -> str: 2939 this = self.sql(expression, "this") 2940 kind = expression.args["kind"] 2941 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 2942 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2943 expression_sql = self.sql(expression, "expression") 2944 return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"
2946 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2947 this = self.sql(expression, "this") 2948 delete = " DELETE" if expression.args.get("delete") else "" 2949 recompress = self.sql(expression, "recompress") 2950 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2951 to_disk = self.sql(expression, "to_disk") 2952 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2953 to_volume = self.sql(expression, "to_volume") 2954 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2955 return f"{this}{delete}{recompress}{to_disk}{to_volume}"
2957 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2958 where = self.sql(expression, "where") 2959 group = self.sql(expression, "group") 2960 aggregates = self.expressions(expression, key="aggregates") 2961 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2962 2963 if not (where or group or aggregates) and len(expression.expressions) == 1: 2964 return f"TTL {self.expressions(expression, flat=True)}" 2965 2966 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
2983 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2984 this = self.sql(expression, "this") 2985 2986 dtype = self.sql(expression, "dtype") 2987 if dtype: 2988 collate = self.sql(expression, "collate") 2989 collate = f" COLLATE {collate}" if collate else "" 2990 using = self.sql(expression, "using") 2991 using = f" USING {using}" if using else "" 2992 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2993 2994 default = self.sql(expression, "default") 2995 if default: 2996 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2997 2998 comment = self.sql(expression, "comment") 2999 if comment: 3000 return f"ALTER COLUMN {this} COMMENT {comment}" 3001 3002 if not expression.args.get("drop"): 3003 self.unsupported("Unsupported ALTER COLUMN syntax") 3004 3005 return f"ALTER COLUMN {this} DROP DEFAULT"
3013 def altersortkey_sql(self, expression: exp.AlterSortKey) -> str: 3014 compound = " COMPOUND" if expression.args.get("compound") else "" 3015 this = self.sql(expression, "this") 3016 expressions = self.expressions(expression, flat=True) 3017 expressions = f"({expressions})" if expressions else "" 3018 return f"ALTER{compound} SORTKEY {this or expressions}"
3020 def renametable_sql(self, expression: exp.RenameTable) -> str: 3021 if not self.RENAME_TABLE_WITH_DB: 3022 # Remove db from tables 3023 expression = expression.transform( 3024 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 3025 ).assert_is(exp.RenameTable) 3026 this = self.sql(expression, "this") 3027 return f"RENAME TO {this}"
3035 def altertable_sql(self, expression: exp.AlterTable) -> str: 3036 actions = expression.args["actions"] 3037 3038 if isinstance(actions[0], exp.ColumnDef): 3039 actions = self.add_column_sql(expression) 3040 elif isinstance(actions[0], exp.Schema): 3041 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 3042 elif isinstance(actions[0], exp.Delete): 3043 actions = self.expressions(expression, key="actions", flat=True) 3044 else: 3045 actions = self.expressions(expression, key="actions", flat=True) 3046 3047 exists = " IF EXISTS" if expression.args.get("exists") else "" 3048 on_cluster = self.sql(expression, "cluster") 3049 on_cluster = f" {on_cluster}" if on_cluster else "" 3050 only = " ONLY" if expression.args.get("only") else "" 3051 options = self.expressions(expression, key="options") 3052 options = f", {options}" if options else "" 3053 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{options}"
3072 def distinct_sql(self, expression: exp.Distinct) -> str: 3073 this = self.expressions(expression, flat=True) 3074 3075 if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1: 3076 case = exp.case() 3077 for arg in expression.expressions: 3078 case = case.when(arg.is_(exp.null()), exp.null()) 3079 this = self.sql(case.else_(f"({this})")) 3080 3081 this = f" {this}" if this else "" 3082 3083 on = self.sql(expression, "on") 3084 on = f" ON {on}" if on else "" 3085 return f"DISTINCT{this}{on}"
3114 def div_sql(self, expression: exp.Div) -> str: 3115 l, r = expression.left, expression.right 3116 3117 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 3118 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 3119 3120 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 3121 if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES): 3122 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 3123 3124 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 3125 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 3126 return self.sql( 3127 exp.cast( 3128 l / r, 3129 to=exp.DataType.Type.BIGINT, 3130 ) 3131 ) 3132 3133 return self.binary(expression, "/")
3221 def log_sql(self, expression: exp.Log) -> str: 3222 this = expression.this 3223 expr = expression.expression 3224 3225 if self.dialect.LOG_BASE_FIRST is False: 3226 this, expr = expr, this 3227 elif self.dialect.LOG_BASE_FIRST is None and expr: 3228 if this.name in ("2", "10"): 3229 return self.func(f"LOG{this.name}", expr) 3230 3231 self.unsupported(f"Unsupported logarithm with base {self.sql(this)}") 3232 3233 return self.func("LOG", this, expr)
3246 def function_fallback_sql(self, expression: exp.Func) -> str: 3247 args = [] 3248 3249 for key in expression.arg_types: 3250 arg_value = expression.args.get(key) 3251 3252 if isinstance(arg_value, list): 3253 for value in arg_value: 3254 args.append(value) 3255 elif arg_value is not None: 3256 args.append(arg_value) 3257 3258 if self.normalize_functions: 3259 name = expression.sql_name() 3260 else: 3261 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 3262 3263 return self.func(name, *args)
def
func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')') -> str:
3274 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 3275 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 3276 if self.pretty and self.too_wide(arg_sqls): 3277 return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 3278 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]:
3283 def format_time( 3284 self, 3285 expression: exp.Expression, 3286 inverse_time_mapping: t.Optional[t.Dict[str, str]] = None, 3287 inverse_time_trie: t.Optional[t.Dict] = None, 3288 ) -> t.Optional[str]: 3289 return format_time( 3290 self.sql(expression, "format"), 3291 inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING, 3292 inverse_time_trie or self.dialect.INVERSE_TIME_TRIE, 3293 )
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:
3295 def expressions( 3296 self, 3297 expression: t.Optional[exp.Expression] = None, 3298 key: t.Optional[str] = None, 3299 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 3300 flat: bool = False, 3301 indent: bool = True, 3302 skip_first: bool = False, 3303 skip_last: bool = False, 3304 sep: str = ", ", 3305 prefix: str = "", 3306 dynamic: bool = False, 3307 new_line: bool = False, 3308 ) -> str: 3309 expressions = expression.args.get(key or "expressions") if expression else sqls 3310 3311 if not expressions: 3312 return "" 3313 3314 if flat: 3315 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 3316 3317 num_sqls = len(expressions) 3318 3319 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 3320 if self.pretty and not self.leading_comma: 3321 stripped_sep = sep.strip() 3322 3323 result_sqls = [] 3324 for i, e in enumerate(expressions): 3325 sql = self.sql(e, comment=False) 3326 if not sql: 3327 continue 3328 3329 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 3330 3331 if self.pretty: 3332 if self.leading_comma: 3333 result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}") 3334 else: 3335 result_sqls.append( 3336 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 3337 ) 3338 else: 3339 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 3340 3341 if self.pretty and (not dynamic or self.too_wide(result_sqls)): 3342 if new_line: 3343 result_sqls.insert(0, "") 3344 result_sqls.append("") 3345 result_sql = "\n".join(result_sqls) 3346 else: 3347 result_sql = "".join(result_sqls) 3348 return ( 3349 self.indent(result_sql, skip_first=skip_first, skip_last=skip_last) 3350 if indent 3351 else result_sql 3352 )
def
op_expressions( self, op: str, expression: sqlglot.expressions.Expression, flat: bool = False) -> str:
3354 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 3355 flat = flat or isinstance(expression.parent, exp.Properties) 3356 expressions_sql = self.expressions(expression, flat=flat) 3357 if flat: 3358 return f"{op} {expressions_sql}" 3359 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
3361 def naked_property(self, expression: exp.Property) -> str: 3362 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 3363 if not property_name: 3364 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 3365 return f"{property_name} {self.sql(expression, 'this')}"
3373 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 3374 this = self.sql(expression, "this") 3375 expressions = self.no_identify(self.expressions, expression) 3376 expressions = ( 3377 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 3378 ) 3379 return f"{this}{expressions}"
3389 def when_sql(self, expression: exp.When) -> str: 3390 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 3391 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 3392 condition = self.sql(expression, "condition") 3393 condition = f" AND {condition}" if condition else "" 3394 3395 then_expression = expression.args.get("then") 3396 if isinstance(then_expression, exp.Insert): 3397 this = self.sql(then_expression, "this") 3398 this = f"INSERT {this}" if this else "INSERT" 3399 then = self.sql(then_expression, "expression") 3400 then = f"{this} VALUES {then}" if then else this 3401 elif isinstance(then_expression, exp.Update): 3402 if isinstance(then_expression.args.get("expressions"), exp.Star): 3403 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 3404 else: 3405 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 3406 else: 3407 then = self.sql(then_expression) 3408 return f"WHEN {matched}{source}{condition} THEN {then}"
3410 def merge_sql(self, expression: exp.Merge) -> str: 3411 table = expression.this 3412 table_alias = "" 3413 3414 hints = table.args.get("hints") 3415 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 3416 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 3417 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 3418 3419 this = self.sql(table) 3420 using = f"USING {self.sql(expression, 'using')}" 3421 on = f"ON {self.sql(expression, 'on')}" 3422 expressions = self.expressions(expression, sep=" ", indent=False) 3423 sep = self.sep() 3424 3425 return self.prepend_ctes( 3426 expression, f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{expressions}" 3427 )
3435 def tonumber_sql(self, expression: exp.ToNumber) -> str: 3436 if not self.SUPPORTS_TO_NUMBER: 3437 self.unsupported("Unsupported TO_NUMBER function") 3438 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3439 3440 fmt = expression.args.get("format") 3441 if not fmt: 3442 self.unsupported("Conversion format is required for TO_NUMBER") 3443 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3444 3445 return self.func("TO_NUMBER", expression.this, fmt)
3447 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 3448 this = self.sql(expression, "this") 3449 kind = self.sql(expression, "kind") 3450 settings_sql = self.expressions(expression, key="settings", sep=" ") 3451 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 3452 return f"{this}({kind}{args})"
3466 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3467 expressions = self.expressions(expression, key="expressions", flat=True) 3468 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3469 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3470 buckets = self.sql(expression, "buckets") 3471 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
3473 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3474 this = self.sql(expression, "this") 3475 having = self.sql(expression, "having") 3476 3477 if having: 3478 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3479 3480 return self.func("ANY_VALUE", this)
3482 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3483 transform = self.func("TRANSFORM", *expression.expressions) 3484 row_format_before = self.sql(expression, "row_format_before") 3485 row_format_before = f" {row_format_before}" if row_format_before else "" 3486 record_writer = self.sql(expression, "record_writer") 3487 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3488 using = f" USING {self.sql(expression, 'command_script')}" 3489 schema = self.sql(expression, "schema") 3490 schema = f" AS {schema}" if schema else "" 3491 row_format_after = self.sql(expression, "row_format_after") 3492 row_format_after = f" {row_format_after}" if row_format_after else "" 3493 record_reader = self.sql(expression, "record_reader") 3494 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3495 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
3497 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3498 key_block_size = self.sql(expression, "key_block_size") 3499 if key_block_size: 3500 return f"KEY_BLOCK_SIZE = {key_block_size}" 3501 3502 using = self.sql(expression, "using") 3503 if using: 3504 return f"USING {using}" 3505 3506 parser = self.sql(expression, "parser") 3507 if parser: 3508 return f"WITH PARSER {parser}" 3509 3510 comment = self.sql(expression, "comment") 3511 if comment: 3512 return f"COMMENT {comment}" 3513 3514 visible = expression.args.get("visible") 3515 if visible is not None: 3516 return "VISIBLE" if visible else "INVISIBLE" 3517 3518 engine_attr = self.sql(expression, "engine_attr") 3519 if engine_attr: 3520 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3521 3522 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3523 if secondary_engine_attr: 3524 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3525 3526 self.unsupported("Unsupported index constraint option.") 3527 return ""
3533 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3534 kind = self.sql(expression, "kind") 3535 kind = f"{kind} INDEX" if kind else "INDEX" 3536 this = self.sql(expression, "this") 3537 this = f" {this}" if this else "" 3538 index_type = self.sql(expression, "index_type") 3539 index_type = f" USING {index_type}" if index_type else "" 3540 expressions = self.expressions(expression, flat=True) 3541 expressions = f" ({expressions})" if expressions else "" 3542 options = self.expressions(expression, key="options", sep=" ") 3543 options = f" {options}" if options else "" 3544 return f"{kind}{this}{index_type}{expressions}{options}"
3546 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3547 if self.NVL2_SUPPORTED: 3548 return self.function_fallback_sql(expression) 3549 3550 case = exp.Case().when( 3551 expression.this.is_(exp.null()).not_(copy=False), 3552 expression.args["true"], 3553 copy=False, 3554 ) 3555 else_cond = expression.args.get("false") 3556 if else_cond: 3557 case.else_(else_cond, copy=False) 3558 3559 return self.sql(case)
3561 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3562 this = self.sql(expression, "this") 3563 expr = self.sql(expression, "expression") 3564 iterator = self.sql(expression, "iterator") 3565 condition = self.sql(expression, "condition") 3566 condition = f" IF {condition}" if condition else "" 3567 return f"{this} FOR {expr} IN {iterator}{condition}"
3575 def predict_sql(self, expression: exp.Predict) -> str: 3576 model = self.sql(expression, "this") 3577 model = f"MODEL {model}" 3578 table = self.sql(expression, "expression") 3579 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3580 parameters = self.sql(expression, "params_struct") 3581 return self.func("PREDICT", model, table, parameters or None)
3596 def toarray_sql(self, expression: exp.ToArray) -> str: 3597 arg = expression.this 3598 if not arg.type: 3599 from sqlglot.optimizer.annotate_types import annotate_types 3600 3601 arg = annotate_types(arg) 3602 3603 if arg.is_type(exp.DataType.Type.ARRAY): 3604 return self.sql(arg) 3605 3606 cond_for_null = arg.is_(exp.null()) 3607 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
3623 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3624 this = expression.this 3625 time_format = self.format_time(expression) 3626 3627 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3628 return self.sql( 3629 exp.cast( 3630 exp.StrToTime(this=this, format=expression.args["format"]), 3631 exp.DataType.Type.DATE, 3632 ) 3633 ) 3634 3635 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3636 return self.sql(this) 3637 3638 return self.sql(exp.cast(this, exp.DataType.Type.DATE))
3650 def lastday_sql(self, expression: exp.LastDay) -> str: 3651 if self.LAST_DAY_SUPPORTS_DATE_PART: 3652 return self.function_fallback_sql(expression) 3653 3654 unit = expression.text("unit") 3655 if unit and unit != "MONTH": 3656 self.unsupported("Date parts are not supported in LAST_DAY.") 3657 3658 return self.func("LAST_DAY", expression.this)
3667 def arrayany_sql(self, expression: exp.ArrayAny) -> str: 3668 if self.CAN_IMPLEMENT_ARRAY_ANY: 3669 filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression) 3670 filtered_not_empty = exp.ArraySize(this=filtered).neq(0) 3671 original_is_empty = exp.ArraySize(this=expression.this).eq(0) 3672 return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty))) 3673 3674 from sqlglot.dialects import Dialect 3675 3676 # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect 3677 if self.dialect.__class__ != Dialect: 3678 self.unsupported("ARRAY_ANY is unsupported") 3679 3680 return self.function_fallback_sql(expression)
3686 def struct_sql(self, expression: exp.Struct) -> str: 3687 expression.set( 3688 "expressions", 3689 [ 3690 exp.alias_(e.expression, e.name if e.this.is_string else e.this) 3691 if isinstance(e, exp.PropertyEQ) 3692 else e 3693 for e in expression.expressions 3694 ], 3695 ) 3696 3697 return self.function_fallback_sql(expression)
3705 def truncatetable_sql(self, expression: exp.TruncateTable) -> str: 3706 target = "DATABASE" if expression.args.get("is_database") else "TABLE" 3707 tables = f" {self.expressions(expression)}" 3708 3709 exists = " IF EXISTS" if expression.args.get("exists") else "" 3710 3711 on_cluster = self.sql(expression, "cluster") 3712 on_cluster = f" {on_cluster}" if on_cluster else "" 3713 3714 identity = self.sql(expression, "identity") 3715 identity = f" {identity} IDENTITY" if identity else "" 3716 3717 option = self.sql(expression, "option") 3718 option = f" {option}" if option else "" 3719 3720 partition = self.sql(expression, "partition") 3721 partition = f" {partition}" if partition else "" 3722 3723 return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
3727 def convert_sql(self, expression: exp.Convert) -> str: 3728 to = expression.this 3729 value = expression.expression 3730 style = expression.args.get("style") 3731 safe = expression.args.get("safe") 3732 strict = expression.args.get("strict") 3733 3734 if not to or not value: 3735 return "" 3736 3737 # Retrieve length of datatype and override to default if not specified 3738 if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3739 to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False) 3740 3741 transformed: t.Optional[exp.Expression] = None 3742 cast = exp.Cast if strict else exp.TryCast 3743 3744 # Check whether a conversion with format (T-SQL calls this 'style') is applicable 3745 if isinstance(style, exp.Literal) and style.is_int: 3746 from sqlglot.dialects.tsql import TSQL 3747 3748 style_value = style.name 3749 converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value) 3750 if not converted_style: 3751 self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}") 3752 3753 fmt = exp.Literal.string(converted_style) 3754 3755 if to.this == exp.DataType.Type.DATE: 3756 transformed = exp.StrToDate(this=value, format=fmt) 3757 elif to.this == exp.DataType.Type.DATETIME: 3758 transformed = exp.StrToTime(this=value, format=fmt) 3759 elif to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3760 transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe) 3761 elif to.this == exp.DataType.Type.TEXT: 3762 transformed = exp.TimeToStr(this=value, format=fmt) 3763 3764 if not transformed: 3765 transformed = cast(this=value, to=to, safe=safe) 3766 3767 return self.sql(transformed)
3823 def copyparameter_sql(self, expression: exp.CopyParameter) -> str: 3824 option = self.sql(expression, "this") 3825 3826 if option.upper() == "FILE_FORMAT": 3827 values = self.expressions(expression, key="expression", flat=True, sep=" ") 3828 return f"{option} = ({values})" 3829 3830 value = self.sql(expression, "expression") 3831 3832 if not value: 3833 return option 3834 3835 op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " " 3836 3837 return f"{option}{op}{value}"
3839 def credentials_sql(self, expression: exp.Credentials) -> str: 3840 cred_expr = expression.args.get("credentials") 3841 if isinstance(cred_expr, exp.Literal): 3842 # Redshift case: CREDENTIALS <string> 3843 credentials = self.sql(expression, "credentials") 3844 credentials = f"CREDENTIALS {credentials}" if credentials else "" 3845 else: 3846 # Snowflake case: CREDENTIALS = (...) 3847 credentials = self.expressions(expression, key="credentials", flat=True, sep=" ") 3848 credentials = f"CREDENTIALS = ({credentials})" if credentials else "" 3849 3850 storage = self.sql(expression, "storage") 3851 3852 encryption = self.expressions(expression, key="encryption", flat=True, sep=" ") 3853 encryption = f" ENCRYPTION = ({encryption})" if encryption else "" 3854 3855 iam_role = self.sql(expression, "iam_role") 3856 iam_role = f"IAM_ROLE {iam_role}" if iam_role else "" 3857 3858 region = self.sql(expression, "region") 3859 region = f" REGION {region}" if region else "" 3860 3861 return f"{credentials}{storage}{encryption}{iam_role}{region}"
3863 def copy_sql(self, expression: exp.Copy) -> str: 3864 this = self.sql(expression, "this") 3865 this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}" 3866 3867 credentials = self.sql(expression, "credentials") 3868 credentials = self.seg(credentials) if credentials else "" 3869 kind = self.seg("FROM" if expression.args.get("kind") else "TO") 3870 files = self.expressions(expression, key="files", flat=True) 3871 3872 sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " " 3873 params = self.expressions( 3874 expression, 3875 key="params", 3876 sep=sep, 3877 new_line=True, 3878 skip_last=True, 3879 skip_first=True, 3880 indent=self.COPY_PARAMS_ARE_WRAPPED, 3881 ) 3882 3883 if params: 3884 if self.COPY_PARAMS_ARE_WRAPPED: 3885 params = f" WITH ({params})" 3886 elif not self.pretty: 3887 params = f" {params}" 3888 3889 return f"COPY{this}{kind} {files}{credentials}{params}"