sqlglot.generator
1from __future__ import annotations 2 3import logging 4import re 5import typing as t 6from collections import defaultdict 7from functools import reduce 8 9from sqlglot import exp 10from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages 11from sqlglot.helper import apply_index_offset, csv, seq_get 12from sqlglot.time import format_time 13from sqlglot.tokens import TokenType 14 15if t.TYPE_CHECKING: 16 from sqlglot._typing import E 17 from sqlglot.dialects.dialect import DialectType 18 19logger = logging.getLogger("sqlglot") 20 21ESCAPED_UNICODE_RE = re.compile(r"\\(\d+)") 22 23 24class Generator: 25 """ 26 Generator converts a given syntax tree to the corresponding SQL string. 27 28 Args: 29 pretty: Whether or not to format the produced SQL string. 30 Default: False. 31 identify: Determines when an identifier should be quoted. Possible values are: 32 False (default): Never quote, except in cases where it's mandatory by the dialect. 33 True or 'always': Always quote. 34 'safe': Only quote identifiers that are case insensitive. 35 normalize: Whether or not to normalize identifiers to lowercase. 36 Default: False. 37 pad: Determines the pad size in a formatted string. 38 Default: 2. 39 indent: Determines the indentation size in a formatted string. 40 Default: 2. 41 normalize_functions: Whether or not to normalize all function names. Possible values are: 42 "upper" or True (default): Convert names to uppercase. 43 "lower": Convert names to lowercase. 44 False: Disables function name normalization. 45 unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. 46 Default ErrorLevel.WARN. 47 max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. 48 This is only relevant if unsupported_level is ErrorLevel.RAISE. 49 Default: 3 50 leading_comma: Determines whether or not the comma is leading or trailing in select expressions. 51 This is only relevant when generating in pretty mode. 52 Default: False 53 max_text_width: The max number of characters in a segment before creating new lines in pretty mode. 54 The default is on the smaller end because the length only represents a segment and not the true 55 line length. 56 Default: 80 57 comments: Whether or not to preserve comments in the output SQL code. 58 Default: True 59 """ 60 61 TRANSFORMS = { 62 exp.DateAdd: lambda self, e: self.func( 63 "DATE_ADD", e.this, e.expression, exp.Literal.string(e.text("unit")) 64 ), 65 exp.CaseSpecificColumnConstraint: lambda self, e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC", 66 exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}", 67 exp.CharacterSetProperty: lambda self, e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}", 68 exp.CheckColumnConstraint: lambda self, e: f"CHECK ({self.sql(e, 'this')})", 69 exp.ClusteredColumnConstraint: lambda self, e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})", 70 exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}", 71 exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}", 72 exp.CopyGrantsProperty: lambda self, e: "COPY GRANTS", 73 exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}", 74 exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}", 75 exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}", 76 exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}", 77 exp.ExecuteAsProperty: lambda self, e: self.naked_property(e), 78 exp.ExternalProperty: lambda self, e: "EXTERNAL", 79 exp.HeapProperty: lambda self, e: "HEAP", 80 exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}", 81 exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}", 82 exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}", 83 exp.LanguageProperty: lambda self, e: self.naked_property(e), 84 exp.LocationProperty: lambda self, e: self.naked_property(e), 85 exp.LogProperty: lambda self, e: f"{'NO ' if e.args.get('no') else ''}LOG", 86 exp.MaterializedProperty: lambda self, e: "MATERIALIZED", 87 exp.NoPrimaryIndexProperty: lambda self, e: "NO PRIMARY INDEX", 88 exp.NonClusteredColumnConstraint: lambda self, e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})", 89 exp.NotForReplicationColumnConstraint: lambda self, e: "NOT FOR REPLICATION", 90 exp.OnCommitProperty: lambda self, e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS", 91 exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}", 92 exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}", 93 exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}", 94 exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}", 95 exp.RemoteWithConnectionModelProperty: lambda self, e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}", 96 exp.ReturnsProperty: lambda self, e: self.naked_property(e), 97 exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}", 98 exp.SetProperty: lambda self, e: f"{'MULTI' if e.args.get('multi') else ''}SET", 99 exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}", 100 exp.SqlSecurityProperty: lambda self, e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}", 101 exp.StabilityProperty: lambda self, e: e.name, 102 exp.TemporaryProperty: lambda self, e: f"TEMPORARY", 103 exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}", 104 exp.TransientProperty: lambda self, e: "TRANSIENT", 105 exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions), 106 exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}", 107 exp.UppercaseColumnConstraint: lambda self, e: f"UPPERCASE", 108 exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]), 109 exp.VolatileProperty: lambda self, e: "VOLATILE", 110 exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}", 111 } 112 113 # Whether or not null ordering is supported in order by 114 NULL_ORDERING_SUPPORTED = True 115 116 # Whether or not locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported 117 LOCKING_READS_SUPPORTED = False 118 119 # Always do union distinct or union all 120 EXPLICIT_UNION = False 121 122 # Wrap derived values in parens, usually standard but spark doesn't support it 123 WRAP_DERIVED_VALUES = True 124 125 # Whether or not create function uses an AS before the RETURN 126 CREATE_FUNCTION_RETURN_AS = True 127 128 # Whether or not MERGE ... WHEN MATCHED BY SOURCE is allowed 129 MATCHED_BY_SOURCE = True 130 131 # Whether or not the INTERVAL expression works only with values like '1 day' 132 SINGLE_STRING_INTERVAL = False 133 134 # Whether or not the plural form of date parts like day (i.e. "days") is supported in INTERVALs 135 INTERVAL_ALLOWS_PLURAL_FORM = True 136 137 # Whether or not limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH") 138 LIMIT_FETCH = "ALL" 139 140 # Whether or not limit and fetch allows expresions or just limits 141 LIMIT_ONLY_LITERALS = False 142 143 # Whether or not a table is allowed to be renamed with a db 144 RENAME_TABLE_WITH_DB = True 145 146 # The separator for grouping sets and rollups 147 GROUPINGS_SEP = "," 148 149 # The string used for creating an index on a table 150 INDEX_ON = "ON" 151 152 # Whether or not join hints should be generated 153 JOIN_HINTS = True 154 155 # Whether or not table hints should be generated 156 TABLE_HINTS = True 157 158 # Whether or not query hints should be generated 159 QUERY_HINTS = True 160 161 # What kind of separator to use for query hints 162 QUERY_HINT_SEP = ", " 163 164 # Whether or not comparing against booleans (e.g. x IS TRUE) is supported 165 IS_BOOL_ALLOWED = True 166 167 # Whether or not to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement 168 DUPLICATE_KEY_UPDATE_WITH_SET = True 169 170 # Whether or not to generate the limit as TOP <value> instead of LIMIT <value> 171 LIMIT_IS_TOP = False 172 173 # Whether or not to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ... 174 RETURNING_END = True 175 176 # Whether or not to generate the (+) suffix for columns used in old-style join conditions 177 COLUMN_JOIN_MARKS_SUPPORTED = False 178 179 # Whether or not to generate an unquoted value for EXTRACT's date part argument 180 EXTRACT_ALLOWS_QUOTES = True 181 182 # Whether or not TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax 183 TZ_TO_WITH_TIME_ZONE = False 184 185 # Whether or not the NVL2 function is supported 186 NVL2_SUPPORTED = True 187 188 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax 189 SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE") 190 191 # Whether or not VALUES statements can be used as derived tables. 192 # MySQL 5 and Redshift do not allow this, so when False, it will convert 193 # SELECT * VALUES into SELECT UNION 194 VALUES_AS_TABLE = True 195 196 # Whether or not the word COLUMN is included when adding a column with ALTER TABLE 197 ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True 198 199 # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery) 200 UNNEST_WITH_ORDINALITY = True 201 202 # Whether or not FILTER (WHERE cond) can be used for conditional aggregation 203 AGGREGATE_FILTER_SUPPORTED = True 204 205 # Whether or not JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds 206 SEMI_ANTI_JOIN_WITH_SIDE = True 207 208 # Whether or not to include the type of a computed column in the CREATE DDL 209 COMPUTED_COLUMN_WITH_TYPE = True 210 211 # Whether or not CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY 212 SUPPORTS_TABLE_COPY = True 213 214 # Whether or not parentheses are required around the table sample's expression 215 TABLESAMPLE_REQUIRES_PARENS = True 216 217 # Whether or not a table sample clause's size needs to be followed by the ROWS keyword 218 TABLESAMPLE_SIZE_IS_ROWS = True 219 220 # The keyword(s) to use when generating a sample clause 221 TABLESAMPLE_KEYWORDS = "TABLESAMPLE" 222 223 # Whether or not the TABLESAMPLE clause supports a method name, like BERNOULLI 224 TABLESAMPLE_WITH_METHOD = True 225 226 # The keyword to use when specifying the seed of a sample clause 227 TABLESAMPLE_SEED_KEYWORD = "SEED" 228 229 # Whether or not COLLATE is a function instead of a binary operator 230 COLLATE_IS_FUNC = False 231 232 # Whether or not data types support additional specifiers like e.g. CHAR or BYTE (oracle) 233 DATA_TYPE_SPECIFIERS_ALLOWED = False 234 235 # Whether or not conditions require booleans WHERE x = 0 vs WHERE x 236 ENSURE_BOOLS = False 237 238 # Whether or not the "RECURSIVE" keyword is required when defining recursive CTEs 239 CTE_RECURSIVE_KEYWORD_REQUIRED = True 240 241 # Whether or not CONCAT requires >1 arguments 242 SUPPORTS_SINGLE_ARG_CONCAT = True 243 244 # Whether or not LAST_DAY function supports a date part argument 245 LAST_DAY_SUPPORTS_DATE_PART = True 246 247 TYPE_MAPPING = { 248 exp.DataType.Type.NCHAR: "CHAR", 249 exp.DataType.Type.NVARCHAR: "VARCHAR", 250 exp.DataType.Type.MEDIUMTEXT: "TEXT", 251 exp.DataType.Type.LONGTEXT: "TEXT", 252 exp.DataType.Type.TINYTEXT: "TEXT", 253 exp.DataType.Type.MEDIUMBLOB: "BLOB", 254 exp.DataType.Type.LONGBLOB: "BLOB", 255 exp.DataType.Type.TINYBLOB: "BLOB", 256 exp.DataType.Type.INET: "INET", 257 } 258 259 STAR_MAPPING = { 260 "except": "EXCEPT", 261 "replace": "REPLACE", 262 } 263 264 TIME_PART_SINGULARS = { 265 "MICROSECONDS": "MICROSECOND", 266 "SECONDS": "SECOND", 267 "MINUTES": "MINUTE", 268 "HOURS": "HOUR", 269 "DAYS": "DAY", 270 "WEEKS": "WEEK", 271 "MONTHS": "MONTH", 272 "QUARTERS": "QUARTER", 273 "YEARS": "YEAR", 274 } 275 276 TOKEN_MAPPING: t.Dict[TokenType, str] = {} 277 278 STRUCT_DELIMITER = ("<", ">") 279 280 PARAMETER_TOKEN = "@" 281 282 PROPERTIES_LOCATION = { 283 exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE, 284 exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA, 285 exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA, 286 exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME, 287 exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA, 288 exp.ChecksumProperty: exp.Properties.Location.POST_NAME, 289 exp.CollateProperty: exp.Properties.Location.POST_SCHEMA, 290 exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA, 291 exp.Cluster: exp.Properties.Location.POST_SCHEMA, 292 exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA, 293 exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME, 294 exp.DefinerProperty: exp.Properties.Location.POST_CREATE, 295 exp.DictRange: exp.Properties.Location.POST_SCHEMA, 296 exp.DictProperty: exp.Properties.Location.POST_SCHEMA, 297 exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA, 298 exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA, 299 exp.EngineProperty: exp.Properties.Location.POST_SCHEMA, 300 exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA, 301 exp.ExternalProperty: exp.Properties.Location.POST_CREATE, 302 exp.FallbackProperty: exp.Properties.Location.POST_NAME, 303 exp.FileFormatProperty: exp.Properties.Location.POST_WITH, 304 exp.FreespaceProperty: exp.Properties.Location.POST_NAME, 305 exp.HeapProperty: exp.Properties.Location.POST_WITH, 306 exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA, 307 exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME, 308 exp.JournalProperty: exp.Properties.Location.POST_NAME, 309 exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA, 310 exp.LikeProperty: exp.Properties.Location.POST_SCHEMA, 311 exp.LocationProperty: exp.Properties.Location.POST_SCHEMA, 312 exp.LockingProperty: exp.Properties.Location.POST_ALIAS, 313 exp.LogProperty: exp.Properties.Location.POST_NAME, 314 exp.MaterializedProperty: exp.Properties.Location.POST_CREATE, 315 exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME, 316 exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION, 317 exp.OnProperty: exp.Properties.Location.POST_SCHEMA, 318 exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION, 319 exp.Order: exp.Properties.Location.POST_SCHEMA, 320 exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA, 321 exp.PartitionedByProperty: exp.Properties.Location.POST_WITH, 322 exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA, 323 exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA, 324 exp.Property: exp.Properties.Location.POST_WITH, 325 exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA, 326 exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA, 327 exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA, 328 exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA, 329 exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA, 330 exp.SampleProperty: exp.Properties.Location.POST_SCHEMA, 331 exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA, 332 exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA, 333 exp.Set: exp.Properties.Location.POST_SCHEMA, 334 exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA, 335 exp.SetProperty: exp.Properties.Location.POST_CREATE, 336 exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA, 337 exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE, 338 exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA, 339 exp.TemporaryProperty: exp.Properties.Location.POST_CREATE, 340 exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA, 341 exp.TransientProperty: exp.Properties.Location.POST_CREATE, 342 exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA, 343 exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA, 344 exp.VolatileProperty: exp.Properties.Location.POST_CREATE, 345 exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION, 346 exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME, 347 exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA, 348 } 349 350 # Keywords that can't be used as unquoted identifier names 351 RESERVED_KEYWORDS: t.Set[str] = set() 352 353 # Expressions whose comments are separated from them for better formatting 354 WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 355 exp.Create, 356 exp.Delete, 357 exp.Drop, 358 exp.From, 359 exp.Insert, 360 exp.Join, 361 exp.Select, 362 exp.Update, 363 exp.Where, 364 exp.With, 365 ) 366 367 # Expressions that should not have their comments generated in maybe_comment 368 EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 369 exp.Binary, 370 exp.Union, 371 ) 372 373 # Expressions that can remain unwrapped when appearing in the context of an INTERVAL 374 UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = ( 375 exp.Column, 376 exp.Literal, 377 exp.Neg, 378 exp.Paren, 379 ) 380 381 # Expressions that need to have all CTEs under them bubbled up to them 382 EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set() 383 384 KEY_VALUE_DEFINITIONS = (exp.Bracket, exp.EQ, exp.PropertyEQ, exp.Slice) 385 386 SENTINEL_LINE_BREAK = "__SQLGLOT__LB__" 387 388 __slots__ = ( 389 "pretty", 390 "identify", 391 "normalize", 392 "pad", 393 "_indent", 394 "normalize_functions", 395 "unsupported_level", 396 "max_unsupported", 397 "leading_comma", 398 "max_text_width", 399 "comments", 400 "dialect", 401 "unsupported_messages", 402 "_escaped_quote_end", 403 "_escaped_identifier_end", 404 ) 405 406 def __init__( 407 self, 408 pretty: t.Optional[bool] = None, 409 identify: str | bool = False, 410 normalize: bool = False, 411 pad: int = 2, 412 indent: int = 2, 413 normalize_functions: t.Optional[str | bool] = None, 414 unsupported_level: ErrorLevel = ErrorLevel.WARN, 415 max_unsupported: int = 3, 416 leading_comma: bool = False, 417 max_text_width: int = 80, 418 comments: bool = True, 419 dialect: DialectType = None, 420 ): 421 import sqlglot 422 from sqlglot.dialects import Dialect 423 424 self.pretty = pretty if pretty is not None else sqlglot.pretty 425 self.identify = identify 426 self.normalize = normalize 427 self.pad = pad 428 self._indent = indent 429 self.unsupported_level = unsupported_level 430 self.max_unsupported = max_unsupported 431 self.leading_comma = leading_comma 432 self.max_text_width = max_text_width 433 self.comments = comments 434 self.dialect = Dialect.get_or_raise(dialect) 435 436 # This is both a Dialect property and a Generator argument, so we prioritize the latter 437 self.normalize_functions = ( 438 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 439 ) 440 441 self.unsupported_messages: t.List[str] = [] 442 self._escaped_quote_end: str = ( 443 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 444 ) 445 self._escaped_identifier_end: str = ( 446 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 447 ) 448 449 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 450 """ 451 Generates the SQL string corresponding to the given syntax tree. 452 453 Args: 454 expression: The syntax tree. 455 copy: Whether or not to copy the expression. The generator performs mutations so 456 it is safer to copy. 457 458 Returns: 459 The SQL string corresponding to `expression`. 460 """ 461 if copy: 462 expression = expression.copy() 463 464 expression = self.preprocess(expression) 465 466 self.unsupported_messages = [] 467 sql = self.sql(expression).strip() 468 469 if self.pretty: 470 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 471 472 if self.unsupported_level == ErrorLevel.IGNORE: 473 return sql 474 475 if self.unsupported_level == ErrorLevel.WARN: 476 for msg in self.unsupported_messages: 477 logger.warning(msg) 478 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 479 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 480 481 return sql 482 483 def preprocess(self, expression: exp.Expression) -> exp.Expression: 484 """Apply generic preprocessing transformations to a given expression.""" 485 if ( 486 not expression.parent 487 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 488 and any(node.parent is not expression for node in expression.find_all(exp.With)) 489 ): 490 from sqlglot.transforms import move_ctes_to_top_level 491 492 expression = move_ctes_to_top_level(expression) 493 494 if self.ENSURE_BOOLS: 495 from sqlglot.transforms import ensure_bools 496 497 expression = ensure_bools(expression) 498 499 return expression 500 501 def unsupported(self, message: str) -> None: 502 if self.unsupported_level == ErrorLevel.IMMEDIATE: 503 raise UnsupportedError(message) 504 self.unsupported_messages.append(message) 505 506 def sep(self, sep: str = " ") -> str: 507 return f"{sep.strip()}\n" if self.pretty else sep 508 509 def seg(self, sql: str, sep: str = " ") -> str: 510 return f"{self.sep(sep)}{sql}" 511 512 def pad_comment(self, comment: str) -> str: 513 comment = " " + comment if comment[0].strip() else comment 514 comment = comment + " " if comment[-1].strip() else comment 515 return comment 516 517 def maybe_comment( 518 self, 519 sql: str, 520 expression: t.Optional[exp.Expression] = None, 521 comments: t.Optional[t.List[str]] = None, 522 ) -> str: 523 comments = ( 524 ((expression and expression.comments) if comments is None else comments) # type: ignore 525 if self.comments 526 else None 527 ) 528 529 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 530 return sql 531 532 comments_sql = " ".join( 533 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 534 ) 535 536 if not comments_sql: 537 return sql 538 539 if isinstance(expression, self.WITH_SEPARATED_COMMENTS): 540 return ( 541 f"{self.sep()}{comments_sql}{sql}" 542 if sql[0].isspace() 543 else f"{comments_sql}{self.sep()}{sql}" 544 ) 545 546 return f"{sql} {comments_sql}" 547 548 def wrap(self, expression: exp.Expression | str) -> str: 549 this_sql = self.indent( 550 self.sql(expression) 551 if isinstance(expression, (exp.Select, exp.Union)) 552 else self.sql(expression, "this"), 553 level=1, 554 pad=0, 555 ) 556 return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}" 557 558 def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str: 559 original = self.identify 560 self.identify = False 561 result = func(*args, **kwargs) 562 self.identify = original 563 return result 564 565 def normalize_func(self, name: str) -> str: 566 if self.normalize_functions == "upper" or self.normalize_functions is True: 567 return name.upper() 568 if self.normalize_functions == "lower": 569 return name.lower() 570 return name 571 572 def indent( 573 self, 574 sql: str, 575 level: int = 0, 576 pad: t.Optional[int] = None, 577 skip_first: bool = False, 578 skip_last: bool = False, 579 ) -> str: 580 if not self.pretty: 581 return sql 582 583 pad = self.pad if pad is None else pad 584 lines = sql.split("\n") 585 586 return "\n".join( 587 line 588 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 589 else f"{' ' * (level * self._indent + pad)}{line}" 590 for i, line in enumerate(lines) 591 ) 592 593 def sql( 594 self, 595 expression: t.Optional[str | exp.Expression], 596 key: t.Optional[str] = None, 597 comment: bool = True, 598 ) -> str: 599 if not expression: 600 return "" 601 602 if isinstance(expression, str): 603 return expression 604 605 if key: 606 value = expression.args.get(key) 607 if value: 608 return self.sql(value) 609 return "" 610 611 transform = self.TRANSFORMS.get(expression.__class__) 612 613 if callable(transform): 614 sql = transform(self, expression) 615 elif transform: 616 sql = transform 617 elif isinstance(expression, exp.Expression): 618 exp_handler_name = f"{expression.key}_sql" 619 620 if hasattr(self, exp_handler_name): 621 sql = getattr(self, exp_handler_name)(expression) 622 elif isinstance(expression, exp.Func): 623 sql = self.function_fallback_sql(expression) 624 elif isinstance(expression, exp.Property): 625 sql = self.property_sql(expression) 626 else: 627 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 628 else: 629 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 630 631 return self.maybe_comment(sql, expression) if self.comments and comment else sql 632 633 def uncache_sql(self, expression: exp.Uncache) -> str: 634 table = self.sql(expression, "this") 635 exists_sql = " IF EXISTS" if expression.args.get("exists") else "" 636 return f"UNCACHE TABLE{exists_sql} {table}" 637 638 def cache_sql(self, expression: exp.Cache) -> str: 639 lazy = " LAZY" if expression.args.get("lazy") else "" 640 table = self.sql(expression, "this") 641 options = expression.args.get("options") 642 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 643 sql = self.sql(expression, "expression") 644 sql = f" AS{self.sep()}{sql}" if sql else "" 645 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 646 return self.prepend_ctes(expression, sql) 647 648 def characterset_sql(self, expression: exp.CharacterSet) -> str: 649 if isinstance(expression.parent, exp.Cast): 650 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 651 default = "DEFAULT " if expression.args.get("default") else "" 652 return f"{default}CHARACTER SET={self.sql(expression, 'this')}" 653 654 def column_sql(self, expression: exp.Column) -> str: 655 join_mark = " (+)" if expression.args.get("join_mark") else "" 656 657 if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED: 658 join_mark = "" 659 self.unsupported("Outer join syntax using the (+) operator is not supported.") 660 661 column = ".".join( 662 self.sql(part) 663 for part in ( 664 expression.args.get("catalog"), 665 expression.args.get("db"), 666 expression.args.get("table"), 667 expression.args.get("this"), 668 ) 669 if part 670 ) 671 672 return f"{column}{join_mark}" 673 674 def columnposition_sql(self, expression: exp.ColumnPosition) -> str: 675 this = self.sql(expression, "this") 676 this = f" {this}" if this else "" 677 position = self.sql(expression, "position") 678 return f"{position}{this}" 679 680 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 681 column = self.sql(expression, "this") 682 kind = self.sql(expression, "kind") 683 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 684 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 685 kind = f"{sep}{kind}" if kind else "" 686 constraints = f" {constraints}" if constraints else "" 687 position = self.sql(expression, "position") 688 position = f" {position}" if position else "" 689 690 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 691 kind = "" 692 693 return f"{exists}{column}{kind}{constraints}{position}" 694 695 def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str: 696 this = self.sql(expression, "this") 697 kind_sql = self.sql(expression, "kind").strip() 698 return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql 699 700 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 701 this = self.sql(expression, "this") 702 if expression.args.get("not_null"): 703 persisted = " PERSISTED NOT NULL" 704 elif expression.args.get("persisted"): 705 persisted = " PERSISTED" 706 else: 707 persisted = "" 708 return f"AS {this}{persisted}" 709 710 def autoincrementcolumnconstraint_sql(self, _) -> str: 711 return self.token_sql(TokenType.AUTO_INCREMENT) 712 713 def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str: 714 if isinstance(expression.this, list): 715 this = self.wrap(self.expressions(expression, key="this", flat=True)) 716 else: 717 this = self.sql(expression, "this") 718 719 return f"COMPRESS {this}" 720 721 def generatedasidentitycolumnconstraint_sql( 722 self, expression: exp.GeneratedAsIdentityColumnConstraint 723 ) -> str: 724 this = "" 725 if expression.this is not None: 726 on_null = " ON NULL" if expression.args.get("on_null") else "" 727 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 728 729 start = expression.args.get("start") 730 start = f"START WITH {start}" if start else "" 731 increment = expression.args.get("increment") 732 increment = f" INCREMENT BY {increment}" if increment else "" 733 minvalue = expression.args.get("minvalue") 734 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 735 maxvalue = expression.args.get("maxvalue") 736 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 737 cycle = expression.args.get("cycle") 738 cycle_sql = "" 739 740 if cycle is not None: 741 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 742 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 743 744 sequence_opts = "" 745 if start or increment or cycle_sql: 746 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 747 sequence_opts = f" ({sequence_opts.strip()})" 748 749 expr = self.sql(expression, "expression") 750 expr = f"({expr})" if expr else "IDENTITY" 751 752 return f"GENERATED{this} AS {expr}{sequence_opts}" 753 754 def generatedasrowcolumnconstraint_sql( 755 self, expression: exp.GeneratedAsRowColumnConstraint 756 ) -> str: 757 start = "START" if expression.args["start"] else "END" 758 hidden = " HIDDEN" if expression.args.get("hidden") else "" 759 return f"GENERATED ALWAYS AS ROW {start}{hidden}" 760 761 def periodforsystemtimeconstraint_sql( 762 self, expression: exp.PeriodForSystemTimeConstraint 763 ) -> str: 764 return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})" 765 766 def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str: 767 return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL" 768 769 def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str: 770 return f"AS {self.sql(expression, 'this')}" 771 772 def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str: 773 desc = expression.args.get("desc") 774 if desc is not None: 775 return f"PRIMARY KEY{' DESC' if desc else ' ASC'}" 776 return f"PRIMARY KEY" 777 778 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 779 this = self.sql(expression, "this") 780 this = f" {this}" if this else "" 781 index_type = expression.args.get("index_type") 782 index_type = f" USING {index_type}" if index_type else "" 783 return f"UNIQUE{this}{index_type}" 784 785 def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str: 786 return self.sql(expression, "this") 787 788 def create_sql(self, expression: exp.Create) -> str: 789 kind = self.sql(expression, "kind") 790 properties = expression.args.get("properties") 791 properties_locs = self.locate_properties(properties) if properties else defaultdict() 792 793 this = self.createable_sql(expression, properties_locs) 794 795 properties_sql = "" 796 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 797 exp.Properties.Location.POST_WITH 798 ): 799 properties_sql = self.sql( 800 exp.Properties( 801 expressions=[ 802 *properties_locs[exp.Properties.Location.POST_SCHEMA], 803 *properties_locs[exp.Properties.Location.POST_WITH], 804 ] 805 ) 806 ) 807 808 begin = " BEGIN" if expression.args.get("begin") else "" 809 end = " END" if expression.args.get("end") else "" 810 811 expression_sql = self.sql(expression, "expression") 812 if expression_sql: 813 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 814 815 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 816 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 817 postalias_props_sql = self.properties( 818 exp.Properties( 819 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 820 ), 821 wrapped=False, 822 ) 823 expression_sql = f" AS {postalias_props_sql}{expression_sql}" 824 else: 825 expression_sql = f" AS{expression_sql}" 826 827 postindex_props_sql = "" 828 if properties_locs.get(exp.Properties.Location.POST_INDEX): 829 postindex_props_sql = self.properties( 830 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 831 wrapped=False, 832 prefix=" ", 833 ) 834 835 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 836 indexes = f" {indexes}" if indexes else "" 837 index_sql = indexes + postindex_props_sql 838 839 replace = " OR REPLACE" if expression.args.get("replace") else "" 840 unique = " UNIQUE" if expression.args.get("unique") else "" 841 842 postcreate_props_sql = "" 843 if properties_locs.get(exp.Properties.Location.POST_CREATE): 844 postcreate_props_sql = self.properties( 845 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 846 sep=" ", 847 prefix=" ", 848 wrapped=False, 849 ) 850 851 modifiers = "".join((replace, unique, postcreate_props_sql)) 852 853 postexpression_props_sql = "" 854 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 855 postexpression_props_sql = self.properties( 856 exp.Properties( 857 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 858 ), 859 sep=" ", 860 prefix=" ", 861 wrapped=False, 862 ) 863 864 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 865 no_schema_binding = ( 866 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 867 ) 868 869 clone = self.sql(expression, "clone") 870 clone = f" {clone}" if clone else "" 871 872 expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 873 return self.prepend_ctes(expression, expression_sql) 874 875 def clone_sql(self, expression: exp.Clone) -> str: 876 this = self.sql(expression, "this") 877 shallow = "SHALLOW " if expression.args.get("shallow") else "" 878 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 879 return f"{shallow}{keyword} {this}" 880 881 def describe_sql(self, expression: exp.Describe) -> str: 882 return f"DESCRIBE {self.sql(expression, 'this')}" 883 884 def prepend_ctes(self, expression: exp.Expression, sql: str) -> str: 885 with_ = self.sql(expression, "with") 886 if with_: 887 sql = f"{with_}{self.sep()}{sql}" 888 return sql 889 890 def with_sql(self, expression: exp.With) -> str: 891 sql = self.expressions(expression, flat=True) 892 recursive = ( 893 "RECURSIVE " 894 if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive") 895 else "" 896 ) 897 898 return f"WITH {recursive}{sql}" 899 900 def cte_sql(self, expression: exp.CTE) -> str: 901 alias = self.sql(expression, "alias") 902 return f"{alias} AS {self.wrap(expression)}" 903 904 def tablealias_sql(self, expression: exp.TableAlias) -> str: 905 alias = self.sql(expression, "this") 906 columns = self.expressions(expression, key="columns", flat=True) 907 columns = f"({columns})" if columns else "" 908 909 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 910 alias = "_t" 911 912 return f"{alias}{columns}" 913 914 def bitstring_sql(self, expression: exp.BitString) -> str: 915 this = self.sql(expression, "this") 916 if self.dialect.BIT_START: 917 return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}" 918 return f"{int(this, 2)}" 919 920 def hexstring_sql(self, expression: exp.HexString) -> str: 921 this = self.sql(expression, "this") 922 if self.dialect.HEX_START: 923 return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}" 924 return f"{int(this, 16)}" 925 926 def bytestring_sql(self, expression: exp.ByteString) -> str: 927 this = self.sql(expression, "this") 928 if self.dialect.BYTE_START: 929 return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}" 930 return this 931 932 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 933 this = self.sql(expression, "this") 934 escape = expression.args.get("escape") 935 936 if self.dialect.UNICODE_START: 937 escape = f" UESCAPE {self.sql(escape)}" if escape else "" 938 return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}" 939 940 if escape: 941 pattern = re.compile(rf"{escape.name}(\d+)") 942 else: 943 pattern = ESCAPED_UNICODE_RE 944 945 this = pattern.sub(r"\\u\1", this) 946 return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}" 947 948 def rawstring_sql(self, expression: exp.RawString) -> str: 949 string = self.escape_str(expression.this.replace("\\", "\\\\")) 950 return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}" 951 952 def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str: 953 this = self.sql(expression, "this") 954 specifier = self.sql(expression, "expression") 955 specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else "" 956 return f"{this}{specifier}" 957 958 def datatype_sql(self, expression: exp.DataType) -> str: 959 type_value = expression.this 960 961 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 962 type_sql = self.sql(expression, "kind") 963 else: 964 type_sql = ( 965 self.TYPE_MAPPING.get(type_value, type_value.value) 966 if isinstance(type_value, exp.DataType.Type) 967 else type_value 968 ) 969 970 nested = "" 971 interior = self.expressions(expression, flat=True) 972 values = "" 973 974 if interior: 975 if expression.args.get("nested"): 976 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 977 if expression.args.get("values") is not None: 978 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 979 values = self.expressions(expression, key="values", flat=True) 980 values = f"{delimiters[0]}{values}{delimiters[1]}" 981 elif type_value == exp.DataType.Type.INTERVAL: 982 nested = f" {interior}" 983 else: 984 nested = f"({interior})" 985 986 type_sql = f"{type_sql}{nested}{values}" 987 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 988 exp.DataType.Type.TIMETZ, 989 exp.DataType.Type.TIMESTAMPTZ, 990 ): 991 type_sql = f"{type_sql} WITH TIME ZONE" 992 993 return type_sql 994 995 def directory_sql(self, expression: exp.Directory) -> str: 996 local = "LOCAL " if expression.args.get("local") else "" 997 row_format = self.sql(expression, "row_format") 998 row_format = f" {row_format}" if row_format else "" 999 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}" 1000 1001 def delete_sql(self, expression: exp.Delete) -> str: 1002 this = self.sql(expression, "this") 1003 this = f" FROM {this}" if this else "" 1004 using = self.sql(expression, "using") 1005 using = f" USING {using}" if using else "" 1006 where = self.sql(expression, "where") 1007 returning = self.sql(expression, "returning") 1008 limit = self.sql(expression, "limit") 1009 tables = self.expressions(expression, key="tables") 1010 tables = f" {tables}" if tables else "" 1011 if self.RETURNING_END: 1012 expression_sql = f"{this}{using}{where}{returning}{limit}" 1013 else: 1014 expression_sql = f"{returning}{this}{using}{where}{limit}" 1015 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}") 1016 1017 def drop_sql(self, expression: exp.Drop) -> str: 1018 this = self.sql(expression, "this") 1019 kind = expression.args["kind"] 1020 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1021 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1022 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1023 cascade = " CASCADE" if expression.args.get("cascade") else "" 1024 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1025 purge = " PURGE" if expression.args.get("purge") else "" 1026 return ( 1027 f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}" 1028 ) 1029 1030 def except_sql(self, expression: exp.Except) -> str: 1031 return self.prepend_ctes( 1032 expression, 1033 self.set_operation(expression, self.except_op(expression)), 1034 ) 1035 1036 def except_op(self, expression: exp.Except) -> str: 1037 return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}" 1038 1039 def fetch_sql(self, expression: exp.Fetch) -> str: 1040 direction = expression.args.get("direction") 1041 direction = f" {direction}" if direction else "" 1042 count = expression.args.get("count") 1043 count = f" {count}" if count else "" 1044 if expression.args.get("percent"): 1045 count = f"{count} PERCENT" 1046 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1047 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}" 1048 1049 def filter_sql(self, expression: exp.Filter) -> str: 1050 if self.AGGREGATE_FILTER_SUPPORTED: 1051 this = self.sql(expression, "this") 1052 where = self.sql(expression, "expression").strip() 1053 return f"{this} FILTER({where})" 1054 1055 agg = expression.this 1056 agg_arg = agg.this 1057 cond = expression.expression.this 1058 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1059 return self.sql(agg) 1060 1061 def hint_sql(self, expression: exp.Hint) -> str: 1062 if not self.QUERY_HINTS: 1063 self.unsupported("Hints are not supported") 1064 return "" 1065 1066 return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */" 1067 1068 def index_sql(self, expression: exp.Index) -> str: 1069 unique = "UNIQUE " if expression.args.get("unique") else "" 1070 primary = "PRIMARY " if expression.args.get("primary") else "" 1071 amp = "AMP " if expression.args.get("amp") else "" 1072 name = self.sql(expression, "this") 1073 name = f"{name} " if name else "" 1074 table = self.sql(expression, "table") 1075 table = f"{self.INDEX_ON} {table}" if table else "" 1076 using = self.sql(expression, "using") 1077 using = f" USING {using}" if using else "" 1078 index = "INDEX " if not table else "" 1079 columns = self.expressions(expression, key="columns", flat=True) 1080 columns = f"({columns})" if columns else "" 1081 partition_by = self.expressions(expression, key="partition_by", flat=True) 1082 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1083 where = self.sql(expression, "where") 1084 return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{partition_by}{where}" 1085 1086 def identifier_sql(self, expression: exp.Identifier) -> str: 1087 text = expression.name 1088 lower = text.lower() 1089 text = lower if self.normalize and not expression.quoted else text 1090 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1091 if ( 1092 expression.quoted 1093 or self.dialect.can_identify(text, self.identify) 1094 or lower in self.RESERVED_KEYWORDS 1095 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1096 ): 1097 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1098 return text 1099 1100 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1101 input_format = self.sql(expression, "input_format") 1102 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1103 output_format = self.sql(expression, "output_format") 1104 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1105 return self.sep().join((input_format, output_format)) 1106 1107 def national_sql(self, expression: exp.National, prefix: str = "N") -> str: 1108 string = self.sql(exp.Literal.string(expression.name)) 1109 return f"{prefix}{string}" 1110 1111 def partition_sql(self, expression: exp.Partition) -> str: 1112 return f"PARTITION({self.expressions(expression, flat=True)})" 1113 1114 def properties_sql(self, expression: exp.Properties) -> str: 1115 root_properties = [] 1116 with_properties = [] 1117 1118 for p in expression.expressions: 1119 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1120 if p_loc == exp.Properties.Location.POST_WITH: 1121 with_properties.append(p) 1122 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1123 root_properties.append(p) 1124 1125 return self.root_properties( 1126 exp.Properties(expressions=root_properties) 1127 ) + self.with_properties(exp.Properties(expressions=with_properties)) 1128 1129 def root_properties(self, properties: exp.Properties) -> str: 1130 if properties.expressions: 1131 return self.sep() + self.expressions(properties, indent=False, sep=" ") 1132 return "" 1133 1134 def properties( 1135 self, 1136 properties: exp.Properties, 1137 prefix: str = "", 1138 sep: str = ", ", 1139 suffix: str = "", 1140 wrapped: bool = True, 1141 ) -> str: 1142 if properties.expressions: 1143 expressions = self.expressions(properties, sep=sep, indent=False) 1144 if expressions: 1145 expressions = self.wrap(expressions) if wrapped else expressions 1146 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1147 return "" 1148 1149 def with_properties(self, properties: exp.Properties) -> str: 1150 return self.properties(properties, prefix=self.seg("WITH")) 1151 1152 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1153 properties_locs = defaultdict(list) 1154 for p in properties.expressions: 1155 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1156 if p_loc != exp.Properties.Location.UNSUPPORTED: 1157 properties_locs[p_loc].append(p) 1158 else: 1159 self.unsupported(f"Unsupported property {p.key}") 1160 1161 return properties_locs 1162 1163 def property_name(self, expression: exp.Property, string_key: bool = False) -> str: 1164 if isinstance(expression.this, exp.Dot): 1165 return self.sql(expression, "this") 1166 return f"'{expression.name}'" if string_key else expression.name 1167 1168 def property_sql(self, expression: exp.Property) -> str: 1169 property_cls = expression.__class__ 1170 if property_cls == exp.Property: 1171 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1172 1173 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1174 if not property_name: 1175 self.unsupported(f"Unsupported property {expression.key}") 1176 1177 return f"{property_name}={self.sql(expression, 'this')}" 1178 1179 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1180 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1181 options = f" {options}" if options else "" 1182 return f"LIKE {self.sql(expression, 'this')}{options}" 1183 1184 def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str: 1185 no = "NO " if expression.args.get("no") else "" 1186 protection = " PROTECTION" if expression.args.get("protection") else "" 1187 return f"{no}FALLBACK{protection}" 1188 1189 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1190 no = "NO " if expression.args.get("no") else "" 1191 local = expression.args.get("local") 1192 local = f"{local} " if local else "" 1193 dual = "DUAL " if expression.args.get("dual") else "" 1194 before = "BEFORE " if expression.args.get("before") else "" 1195 after = "AFTER " if expression.args.get("after") else "" 1196 return f"{no}{local}{dual}{before}{after}JOURNAL" 1197 1198 def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str: 1199 freespace = self.sql(expression, "this") 1200 percent = " PERCENT" if expression.args.get("percent") else "" 1201 return f"FREESPACE={freespace}{percent}" 1202 1203 def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str: 1204 if expression.args.get("default"): 1205 property = "DEFAULT" 1206 elif expression.args.get("on"): 1207 property = "ON" 1208 else: 1209 property = "OFF" 1210 return f"CHECKSUM={property}" 1211 1212 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1213 if expression.args.get("no"): 1214 return "NO MERGEBLOCKRATIO" 1215 if expression.args.get("default"): 1216 return "DEFAULT MERGEBLOCKRATIO" 1217 1218 percent = " PERCENT" if expression.args.get("percent") else "" 1219 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}" 1220 1221 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1222 default = expression.args.get("default") 1223 minimum = expression.args.get("minimum") 1224 maximum = expression.args.get("maximum") 1225 if default or minimum or maximum: 1226 if default: 1227 prop = "DEFAULT" 1228 elif minimum: 1229 prop = "MINIMUM" 1230 else: 1231 prop = "MAXIMUM" 1232 return f"{prop} DATABLOCKSIZE" 1233 units = expression.args.get("units") 1234 units = f" {units}" if units else "" 1235 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}" 1236 1237 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1238 autotemp = expression.args.get("autotemp") 1239 always = expression.args.get("always") 1240 default = expression.args.get("default") 1241 manual = expression.args.get("manual") 1242 never = expression.args.get("never") 1243 1244 if autotemp is not None: 1245 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1246 elif always: 1247 prop = "ALWAYS" 1248 elif default: 1249 prop = "DEFAULT" 1250 elif manual: 1251 prop = "MANUAL" 1252 elif never: 1253 prop = "NEVER" 1254 return f"BLOCKCOMPRESSION={prop}" 1255 1256 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1257 no = expression.args.get("no") 1258 no = " NO" if no else "" 1259 concurrent = expression.args.get("concurrent") 1260 concurrent = " CONCURRENT" if concurrent else "" 1261 1262 for_ = "" 1263 if expression.args.get("for_all"): 1264 for_ = " FOR ALL" 1265 elif expression.args.get("for_insert"): 1266 for_ = " FOR INSERT" 1267 elif expression.args.get("for_none"): 1268 for_ = " FOR NONE" 1269 return f"WITH{no}{concurrent} ISOLATED LOADING{for_}" 1270 1271 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1272 if isinstance(expression.this, list): 1273 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1274 if expression.this: 1275 modulus = self.sql(expression, "this") 1276 remainder = self.sql(expression, "expression") 1277 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1278 1279 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1280 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1281 return f"FROM ({from_expressions}) TO ({to_expressions})" 1282 1283 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1284 this = self.sql(expression, "this") 1285 1286 for_values_or_default = expression.expression 1287 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1288 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1289 else: 1290 for_values_or_default = " DEFAULT" 1291 1292 return f"PARTITION OF {this}{for_values_or_default}" 1293 1294 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1295 kind = expression.args.get("kind") 1296 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1297 for_or_in = expression.args.get("for_or_in") 1298 for_or_in = f" {for_or_in}" if for_or_in else "" 1299 lock_type = expression.args.get("lock_type") 1300 override = " OVERRIDE" if expression.args.get("override") else "" 1301 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}" 1302 1303 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1304 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1305 statistics = expression.args.get("statistics") 1306 statistics_sql = "" 1307 if statistics is not None: 1308 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1309 return f"{data_sql}{statistics_sql}" 1310 1311 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1312 sql = "WITH(SYSTEM_VERSIONING=ON" 1313 1314 if expression.this: 1315 history_table = self.sql(expression, "this") 1316 sql = f"{sql}(HISTORY_TABLE={history_table}" 1317 1318 if expression.expression: 1319 data_consistency_check = self.sql(expression, "expression") 1320 sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}" 1321 1322 sql = f"{sql})" 1323 1324 return f"{sql})" 1325 1326 def insert_sql(self, expression: exp.Insert) -> str: 1327 overwrite = expression.args.get("overwrite") 1328 1329 if isinstance(expression.this, exp.Directory): 1330 this = " OVERWRITE" if overwrite else " INTO" 1331 else: 1332 this = " OVERWRITE TABLE" if overwrite else " INTO" 1333 1334 alternative = expression.args.get("alternative") 1335 alternative = f" OR {alternative}" if alternative else "" 1336 ignore = " IGNORE" if expression.args.get("ignore") else "" 1337 1338 this = f"{this} {self.sql(expression, 'this')}" 1339 1340 exists = " IF EXISTS" if expression.args.get("exists") else "" 1341 partition_sql = ( 1342 f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else "" 1343 ) 1344 where = self.sql(expression, "where") 1345 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1346 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1347 conflict = self.sql(expression, "conflict") 1348 by_name = " BY NAME" if expression.args.get("by_name") else "" 1349 returning = self.sql(expression, "returning") 1350 1351 if self.RETURNING_END: 1352 expression_sql = f"{expression_sql}{conflict}{returning}" 1353 else: 1354 expression_sql = f"{returning}{expression_sql}{conflict}" 1355 1356 sql = f"INSERT{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}" 1357 return self.prepend_ctes(expression, sql) 1358 1359 def intersect_sql(self, expression: exp.Intersect) -> str: 1360 return self.prepend_ctes( 1361 expression, 1362 self.set_operation(expression, self.intersect_op(expression)), 1363 ) 1364 1365 def intersect_op(self, expression: exp.Intersect) -> str: 1366 return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}" 1367 1368 def introducer_sql(self, expression: exp.Introducer) -> str: 1369 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 1370 1371 def kill_sql(self, expression: exp.Kill) -> str: 1372 kind = self.sql(expression, "kind") 1373 kind = f" {kind}" if kind else "" 1374 this = self.sql(expression, "this") 1375 this = f" {this}" if this else "" 1376 return f"KILL{kind}{this}" 1377 1378 def pseudotype_sql(self, expression: exp.PseudoType) -> str: 1379 return expression.name 1380 1381 def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str: 1382 return expression.name 1383 1384 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1385 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1386 constraint = self.sql(expression, "constraint") 1387 if constraint: 1388 constraint = f"ON CONSTRAINT {constraint}" 1389 key = self.expressions(expression, key="key", flat=True) 1390 do = "" if expression.args.get("duplicate") else " DO " 1391 nothing = "NOTHING" if expression.args.get("nothing") else "" 1392 expressions = self.expressions(expression, flat=True) 1393 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1394 if expressions: 1395 expressions = f"UPDATE {set_keyword}{expressions}" 1396 return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}" 1397 1398 def returning_sql(self, expression: exp.Returning) -> str: 1399 return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}" 1400 1401 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1402 fields = expression.args.get("fields") 1403 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1404 escaped = expression.args.get("escaped") 1405 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1406 items = expression.args.get("collection_items") 1407 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1408 keys = expression.args.get("map_keys") 1409 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1410 lines = expression.args.get("lines") 1411 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1412 null = expression.args.get("null") 1413 null = f" NULL DEFINED AS {null}" if null else "" 1414 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}" 1415 1416 def withtablehint_sql(self, expression: exp.WithTableHint) -> str: 1417 return f"WITH ({self.expressions(expression, flat=True)})" 1418 1419 def indextablehint_sql(self, expression: exp.IndexTableHint) -> str: 1420 this = f"{self.sql(expression, 'this')} INDEX" 1421 target = self.sql(expression, "target") 1422 target = f" FOR {target}" if target else "" 1423 return f"{this}{target} ({self.expressions(expression, flat=True)})" 1424 1425 def historicaldata_sql(self, expression: exp.HistoricalData) -> str: 1426 this = self.sql(expression, "this") 1427 kind = self.sql(expression, "kind") 1428 expr = self.sql(expression, "expression") 1429 return f"{this} ({kind} => {expr})" 1430 1431 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1432 table = ".".join( 1433 self.sql(part) 1434 for part in ( 1435 expression.args.get("catalog"), 1436 expression.args.get("db"), 1437 expression.args.get("this"), 1438 ) 1439 if part is not None 1440 ) 1441 1442 version = self.sql(expression, "version") 1443 version = f" {version}" if version else "" 1444 alias = self.sql(expression, "alias") 1445 alias = f"{sep}{alias}" if alias else "" 1446 hints = self.expressions(expression, key="hints", sep=" ") 1447 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1448 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 1449 pivots = f" {pivots}" if pivots else "" 1450 joins = self.expressions(expression, key="joins", sep="", skip_first=True) 1451 laterals = self.expressions(expression, key="laterals", sep="") 1452 1453 file_format = self.sql(expression, "format") 1454 if file_format: 1455 pattern = self.sql(expression, "pattern") 1456 pattern = f", PATTERN => {pattern}" if pattern else "" 1457 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1458 1459 ordinality = expression.args.get("ordinality") or "" 1460 if ordinality: 1461 ordinality = f" WITH ORDINALITY{alias}" 1462 alias = "" 1463 1464 when = self.sql(expression, "when") 1465 if when: 1466 table = f"{table} {when}" 1467 1468 return f"{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}" 1469 1470 def tablesample_sql( 1471 self, 1472 expression: exp.TableSample, 1473 sep: str = " AS ", 1474 tablesample_keyword: t.Optional[str] = None, 1475 ) -> str: 1476 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1477 table = expression.this.copy() 1478 table.set("alias", None) 1479 this = self.sql(table) 1480 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1481 else: 1482 this = self.sql(expression, "this") 1483 alias = "" 1484 1485 method = self.sql(expression, "method") 1486 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1487 numerator = self.sql(expression, "bucket_numerator") 1488 denominator = self.sql(expression, "bucket_denominator") 1489 field = self.sql(expression, "bucket_field") 1490 field = f" ON {field}" if field else "" 1491 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1492 seed = self.sql(expression, "seed") 1493 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1494 1495 size = self.sql(expression, "size") 1496 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1497 size = f"{size} ROWS" 1498 1499 percent = self.sql(expression, "percent") 1500 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1501 percent = f"{percent} PERCENT" 1502 1503 expr = f"{bucket}{percent}{size}" 1504 if self.TABLESAMPLE_REQUIRES_PARENS: 1505 expr = f"({expr})" 1506 1507 return ( 1508 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1509 ) 1510 1511 def pivot_sql(self, expression: exp.Pivot) -> str: 1512 expressions = self.expressions(expression, flat=True) 1513 1514 if expression.this: 1515 this = self.sql(expression, "this") 1516 if not expressions: 1517 return f"UNPIVOT {this}" 1518 1519 on = f"{self.seg('ON')} {expressions}" 1520 using = self.expressions(expression, key="using", flat=True) 1521 using = f"{self.seg('USING')} {using}" if using else "" 1522 group = self.sql(expression, "group") 1523 return f"PIVOT {this}{on}{using}{group}" 1524 1525 alias = self.sql(expression, "alias") 1526 alias = f" AS {alias}" if alias else "" 1527 unpivot = expression.args.get("unpivot") 1528 direction = "UNPIVOT" if unpivot else "PIVOT" 1529 field = self.sql(expression, "field") 1530 include_nulls = expression.args.get("include_nulls") 1531 if include_nulls is not None: 1532 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1533 else: 1534 nulls = "" 1535 return f"{direction}{nulls}({expressions} FOR {field}){alias}" 1536 1537 def version_sql(self, expression: exp.Version) -> str: 1538 this = f"FOR {expression.name}" 1539 kind = expression.text("kind") 1540 expr = self.sql(expression, "expression") 1541 return f"{this} {kind} {expr}" 1542 1543 def tuple_sql(self, expression: exp.Tuple) -> str: 1544 return f"({self.expressions(expression, flat=True)})" 1545 1546 def update_sql(self, expression: exp.Update) -> str: 1547 this = self.sql(expression, "this") 1548 set_sql = self.expressions(expression, flat=True) 1549 from_sql = self.sql(expression, "from") 1550 where_sql = self.sql(expression, "where") 1551 returning = self.sql(expression, "returning") 1552 order = self.sql(expression, "order") 1553 limit = self.sql(expression, "limit") 1554 if self.RETURNING_END: 1555 expression_sql = f"{from_sql}{where_sql}{returning}" 1556 else: 1557 expression_sql = f"{returning}{from_sql}{where_sql}" 1558 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1559 return self.prepend_ctes(expression, sql) 1560 1561 def values_sql(self, expression: exp.Values) -> str: 1562 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1563 if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join): 1564 args = self.expressions(expression) 1565 alias = self.sql(expression, "alias") 1566 values = f"VALUES{self.seg('')}{args}" 1567 values = ( 1568 f"({values})" 1569 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1570 else values 1571 ) 1572 return f"{values} AS {alias}" if alias else values 1573 1574 # Converts `VALUES...` expression into a series of select unions. 1575 alias_node = expression.args.get("alias") 1576 column_names = alias_node and alias_node.columns 1577 1578 selects: t.List[exp.Subqueryable] = [] 1579 1580 for i, tup in enumerate(expression.expressions): 1581 row = tup.expressions 1582 1583 if i == 0 and column_names: 1584 row = [ 1585 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1586 ] 1587 1588 selects.append(exp.Select(expressions=row)) 1589 1590 if self.pretty: 1591 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1592 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1593 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1594 subqueryable = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1595 return self.subquery_sql( 1596 subqueryable.subquery(alias_node and alias_node.this, copy=False) 1597 ) 1598 1599 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1600 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1601 return f"({unions}){alias}" 1602 1603 def var_sql(self, expression: exp.Var) -> str: 1604 return self.sql(expression, "this") 1605 1606 def into_sql(self, expression: exp.Into) -> str: 1607 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1608 unlogged = " UNLOGGED" if expression.args.get("unlogged") else "" 1609 return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}" 1610 1611 def from_sql(self, expression: exp.From) -> str: 1612 return f"{self.seg('FROM')} {self.sql(expression, 'this')}" 1613 1614 def group_sql(self, expression: exp.Group) -> str: 1615 group_by = self.op_expressions("GROUP BY", expression) 1616 1617 if expression.args.get("all"): 1618 return f"{group_by} ALL" 1619 1620 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1621 grouping_sets = ( 1622 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1623 ) 1624 1625 cube = expression.args.get("cube", []) 1626 if seq_get(cube, 0) is True: 1627 return f"{group_by}{self.seg('WITH CUBE')}" 1628 else: 1629 cube_sql = self.expressions(expression, key="cube", indent=False) 1630 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1631 1632 rollup = expression.args.get("rollup", []) 1633 if seq_get(rollup, 0) is True: 1634 return f"{group_by}{self.seg('WITH ROLLUP')}" 1635 else: 1636 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1637 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1638 1639 groupings = csv( 1640 grouping_sets, 1641 cube_sql, 1642 rollup_sql, 1643 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1644 sep=self.GROUPINGS_SEP, 1645 ) 1646 1647 if expression.args.get("expressions") and groupings: 1648 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1649 1650 return f"{group_by}{groupings}" 1651 1652 def having_sql(self, expression: exp.Having) -> str: 1653 this = self.indent(self.sql(expression, "this")) 1654 return f"{self.seg('HAVING')}{self.sep()}{this}" 1655 1656 def connect_sql(self, expression: exp.Connect) -> str: 1657 start = self.sql(expression, "start") 1658 start = self.seg(f"START WITH {start}") if start else "" 1659 connect = self.sql(expression, "connect") 1660 connect = self.seg(f"CONNECT BY {connect}") 1661 return start + connect 1662 1663 def prior_sql(self, expression: exp.Prior) -> str: 1664 return f"PRIOR {self.sql(expression, 'this')}" 1665 1666 def join_sql(self, expression: exp.Join) -> str: 1667 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1668 side = None 1669 else: 1670 side = expression.side 1671 1672 op_sql = " ".join( 1673 op 1674 for op in ( 1675 expression.method, 1676 "GLOBAL" if expression.args.get("global") else None, 1677 side, 1678 expression.kind, 1679 expression.hint if self.JOIN_HINTS else None, 1680 ) 1681 if op 1682 ) 1683 on_sql = self.sql(expression, "on") 1684 using = expression.args.get("using") 1685 1686 if not on_sql and using: 1687 on_sql = csv(*(self.sql(column) for column in using)) 1688 1689 this_sql = self.sql(expression, "this") 1690 1691 if on_sql: 1692 on_sql = self.indent(on_sql, skip_first=True) 1693 space = self.seg(" " * self.pad) if self.pretty else " " 1694 if using: 1695 on_sql = f"{space}USING ({on_sql})" 1696 else: 1697 on_sql = f"{space}ON {on_sql}" 1698 elif not op_sql: 1699 return f", {this_sql}" 1700 1701 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1702 return f"{self.seg(op_sql)} {this_sql}{on_sql}" 1703 1704 def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str: 1705 args = self.expressions(expression, flat=True) 1706 args = f"({args})" if len(args.split(",")) > 1 else args 1707 return f"{args} {arrow_sep} {self.sql(expression, 'this')}" 1708 1709 def lateral_sql(self, expression: exp.Lateral) -> str: 1710 this = self.sql(expression, "this") 1711 1712 if expression.args.get("view"): 1713 alias = expression.args["alias"] 1714 columns = self.expressions(alias, key="columns", flat=True) 1715 table = f" {alias.name}" if alias.name else "" 1716 columns = f" AS {columns}" if columns else "" 1717 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1718 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1719 1720 alias = self.sql(expression, "alias") 1721 alias = f" AS {alias}" if alias else "" 1722 return f"LATERAL {this}{alias}" 1723 1724 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1725 this = self.sql(expression, "this") 1726 1727 args = [ 1728 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1729 for e in (expression.args.get(k) for k in ("offset", "expression")) 1730 if e 1731 ] 1732 1733 args_sql = ", ".join(self.sql(e) for e in args) 1734 args_sql = f"({args_sql})" if any(top and not e.is_number for e in args) else args_sql 1735 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}" 1736 1737 def offset_sql(self, expression: exp.Offset) -> str: 1738 this = self.sql(expression, "this") 1739 expression = expression.expression 1740 expression = ( 1741 self._simplify_unless_literal(expression) if self.LIMIT_ONLY_LITERALS else expression 1742 ) 1743 return f"{this}{self.seg('OFFSET')} {self.sql(expression)}" 1744 1745 def setitem_sql(self, expression: exp.SetItem) -> str: 1746 kind = self.sql(expression, "kind") 1747 kind = f"{kind} " if kind else "" 1748 this = self.sql(expression, "this") 1749 expressions = self.expressions(expression) 1750 collate = self.sql(expression, "collate") 1751 collate = f" COLLATE {collate}" if collate else "" 1752 global_ = "GLOBAL " if expression.args.get("global") else "" 1753 return f"{global_}{kind}{this}{expressions}{collate}" 1754 1755 def set_sql(self, expression: exp.Set) -> str: 1756 expressions = ( 1757 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 1758 ) 1759 tag = " TAG" if expression.args.get("tag") else "" 1760 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}" 1761 1762 def pragma_sql(self, expression: exp.Pragma) -> str: 1763 return f"PRAGMA {self.sql(expression, 'this')}" 1764 1765 def lock_sql(self, expression: exp.Lock) -> str: 1766 if not self.LOCKING_READS_SUPPORTED: 1767 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 1768 return "" 1769 1770 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 1771 expressions = self.expressions(expression, flat=True) 1772 expressions = f" OF {expressions}" if expressions else "" 1773 wait = expression.args.get("wait") 1774 1775 if wait is not None: 1776 if isinstance(wait, exp.Literal): 1777 wait = f" WAIT {self.sql(wait)}" 1778 else: 1779 wait = " NOWAIT" if wait else " SKIP LOCKED" 1780 1781 return f"{lock_type}{expressions}{wait or ''}" 1782 1783 def literal_sql(self, expression: exp.Literal) -> str: 1784 text = expression.this or "" 1785 if expression.is_string: 1786 text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}" 1787 return text 1788 1789 def escape_str(self, text: str) -> str: 1790 text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end) 1791 if self.dialect.INVERSE_ESCAPE_SEQUENCES: 1792 text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text) 1793 elif self.pretty: 1794 text = text.replace("\n", self.SENTINEL_LINE_BREAK) 1795 return text 1796 1797 def loaddata_sql(self, expression: exp.LoadData) -> str: 1798 local = " LOCAL" if expression.args.get("local") else "" 1799 inpath = f" INPATH {self.sql(expression, 'inpath')}" 1800 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 1801 this = f" INTO TABLE {self.sql(expression, 'this')}" 1802 partition = self.sql(expression, "partition") 1803 partition = f" {partition}" if partition else "" 1804 input_format = self.sql(expression, "input_format") 1805 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 1806 serde = self.sql(expression, "serde") 1807 serde = f" SERDE {serde}" if serde else "" 1808 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}" 1809 1810 def null_sql(self, *_) -> str: 1811 return "NULL" 1812 1813 def boolean_sql(self, expression: exp.Boolean) -> str: 1814 return "TRUE" if expression.this else "FALSE" 1815 1816 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 1817 this = self.sql(expression, "this") 1818 this = f"{this} " if this else this 1819 order = self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat) # type: ignore 1820 interpolated_values = [ 1821 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 1822 for named_expression in expression.args.get("interpolate") or [] 1823 ] 1824 interpolate = ( 1825 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 1826 ) 1827 return f"{order}{interpolate}" 1828 1829 def withfill_sql(self, expression: exp.WithFill) -> str: 1830 from_sql = self.sql(expression, "from") 1831 from_sql = f" FROM {from_sql}" if from_sql else "" 1832 to_sql = self.sql(expression, "to") 1833 to_sql = f" TO {to_sql}" if to_sql else "" 1834 step_sql = self.sql(expression, "step") 1835 step_sql = f" STEP {step_sql}" if step_sql else "" 1836 return f"WITH FILL{from_sql}{to_sql}{step_sql}" 1837 1838 def cluster_sql(self, expression: exp.Cluster) -> str: 1839 return self.op_expressions("CLUSTER BY", expression) 1840 1841 def distribute_sql(self, expression: exp.Distribute) -> str: 1842 return self.op_expressions("DISTRIBUTE BY", expression) 1843 1844 def sort_sql(self, expression: exp.Sort) -> str: 1845 return self.op_expressions("SORT BY", expression) 1846 1847 def ordered_sql(self, expression: exp.Ordered) -> str: 1848 desc = expression.args.get("desc") 1849 asc = not desc 1850 1851 nulls_first = expression.args.get("nulls_first") 1852 nulls_last = not nulls_first 1853 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 1854 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 1855 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 1856 1857 this = self.sql(expression, "this") 1858 1859 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 1860 nulls_sort_change = "" 1861 if nulls_first and ( 1862 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 1863 ): 1864 nulls_sort_change = " NULLS FIRST" 1865 elif ( 1866 nulls_last 1867 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 1868 and not nulls_are_last 1869 ): 1870 nulls_sort_change = " NULLS LAST" 1871 1872 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 1873 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 1874 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 1875 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 1876 nulls_sort_change = "" 1877 1878 with_fill = self.sql(expression, "with_fill") 1879 with_fill = f" {with_fill}" if with_fill else "" 1880 1881 return f"{this}{sort_order}{nulls_sort_change}{with_fill}" 1882 1883 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 1884 partition = self.partition_by_sql(expression) 1885 order = self.sql(expression, "order") 1886 measures = self.expressions(expression, key="measures") 1887 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 1888 rows = self.sql(expression, "rows") 1889 rows = self.seg(rows) if rows else "" 1890 after = self.sql(expression, "after") 1891 after = self.seg(after) if after else "" 1892 pattern = self.sql(expression, "pattern") 1893 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 1894 definition_sqls = [ 1895 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 1896 for definition in expression.args.get("define", []) 1897 ] 1898 definitions = self.expressions(sqls=definition_sqls) 1899 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 1900 body = "".join( 1901 ( 1902 partition, 1903 order, 1904 measures, 1905 rows, 1906 after, 1907 pattern, 1908 define, 1909 ) 1910 ) 1911 alias = self.sql(expression, "alias") 1912 alias = f" {alias}" if alias else "" 1913 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}" 1914 1915 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 1916 limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit") 1917 1918 # If the limit is generated as TOP, we need to ensure it's not generated twice 1919 with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP 1920 1921 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 1922 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 1923 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 1924 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 1925 1926 fetch = isinstance(limit, exp.Fetch) 1927 1928 offset_limit_modifiers = ( 1929 self.offset_limit_modifiers(expression, fetch, limit) 1930 if with_offset_limit_modifiers 1931 else [] 1932 ) 1933 1934 return csv( 1935 *sqls, 1936 *[self.sql(join) for join in expression.args.get("joins") or []], 1937 self.sql(expression, "connect"), 1938 self.sql(expression, "match"), 1939 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 1940 self.sql(expression, "where"), 1941 self.sql(expression, "group"), 1942 self.sql(expression, "having"), 1943 *self.after_having_modifiers(expression), 1944 self.sql(expression, "order"), 1945 *offset_limit_modifiers, 1946 *self.after_limit_modifiers(expression), 1947 sep="", 1948 ) 1949 1950 def offset_limit_modifiers( 1951 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 1952 ) -> t.List[str]: 1953 return [ 1954 self.sql(expression, "offset") if fetch else self.sql(limit), 1955 self.sql(limit) if fetch else self.sql(expression, "offset"), 1956 ] 1957 1958 def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]: 1959 return [ 1960 self.sql(expression, "qualify"), 1961 self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True) 1962 if expression.args.get("windows") 1963 else "", 1964 self.sql(expression, "distribute"), 1965 self.sql(expression, "sort"), 1966 self.sql(expression, "cluster"), 1967 ] 1968 1969 def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]: 1970 locks = self.expressions(expression, key="locks", sep=" ") 1971 locks = f" {locks}" if locks else "" 1972 return [locks, self.sql(expression, "sample")] 1973 1974 def select_sql(self, expression: exp.Select) -> str: 1975 hint = self.sql(expression, "hint") 1976 distinct = self.sql(expression, "distinct") 1977 distinct = f" {distinct}" if distinct else "" 1978 kind = self.sql(expression, "kind") 1979 limit = expression.args.get("limit") 1980 top = ( 1981 self.limit_sql(limit, top=True) 1982 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP 1983 else "" 1984 ) 1985 1986 expressions = self.expressions(expression) 1987 1988 if kind: 1989 if kind in self.SELECT_KINDS: 1990 kind = f" AS {kind}" 1991 else: 1992 if kind == "STRUCT": 1993 expressions = self.expressions( 1994 sqls=[ 1995 self.sql( 1996 exp.Struct( 1997 expressions=[ 1998 exp.column(e.output_name).eq( 1999 e.this if isinstance(e, exp.Alias) else e 2000 ) 2001 for e in expression.expressions 2002 ] 2003 ) 2004 ) 2005 ] 2006 ) 2007 kind = "" 2008 2009 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2010 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2011 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2012 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2013 sql = self.query_modifiers( 2014 expression, 2015 f"SELECT{top_distinct}{kind}{expressions}", 2016 self.sql(expression, "into", comment=False), 2017 self.sql(expression, "from", comment=False), 2018 ) 2019 return self.prepend_ctes(expression, sql) 2020 2021 def schema_sql(self, expression: exp.Schema) -> str: 2022 this = self.sql(expression, "this") 2023 sql = self.schema_columns_sql(expression) 2024 return f"{this} {sql}" if this and sql else this or sql 2025 2026 def schema_columns_sql(self, expression: exp.Schema) -> str: 2027 if expression.expressions: 2028 return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}" 2029 return "" 2030 2031 def star_sql(self, expression: exp.Star) -> str: 2032 except_ = self.expressions(expression, key="except", flat=True) 2033 except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else "" 2034 replace = self.expressions(expression, key="replace", flat=True) 2035 replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else "" 2036 return f"*{except_}{replace}" 2037 2038 def parameter_sql(self, expression: exp.Parameter) -> str: 2039 this = self.sql(expression, "this") 2040 return f"{self.PARAMETER_TOKEN}{this}" 2041 2042 def sessionparameter_sql(self, expression: exp.SessionParameter) -> str: 2043 this = self.sql(expression, "this") 2044 kind = expression.text("kind") 2045 if kind: 2046 kind = f"{kind}." 2047 return f"@@{kind}{this}" 2048 2049 def placeholder_sql(self, expression: exp.Placeholder) -> str: 2050 return f":{expression.name}" if expression.name else "?" 2051 2052 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2053 alias = self.sql(expression, "alias") 2054 alias = f"{sep}{alias}" if alias else "" 2055 2056 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2057 pivots = f" {pivots}" if pivots else "" 2058 2059 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2060 return self.prepend_ctes(expression, sql) 2061 2062 def qualify_sql(self, expression: exp.Qualify) -> str: 2063 this = self.indent(self.sql(expression, "this")) 2064 return f"{self.seg('QUALIFY')}{self.sep()}{this}" 2065 2066 def union_sql(self, expression: exp.Union) -> str: 2067 return self.prepend_ctes( 2068 expression, 2069 self.set_operation(expression, self.union_op(expression)), 2070 ) 2071 2072 def union_op(self, expression: exp.Union) -> str: 2073 kind = " DISTINCT" if self.EXPLICIT_UNION else "" 2074 kind = kind if expression.args.get("distinct") else " ALL" 2075 by_name = " BY NAME" if expression.args.get("by_name") else "" 2076 return f"UNION{kind}{by_name}" 2077 2078 def unnest_sql(self, expression: exp.Unnest) -> str: 2079 args = self.expressions(expression, flat=True) 2080 2081 alias = expression.args.get("alias") 2082 offset = expression.args.get("offset") 2083 2084 if self.UNNEST_WITH_ORDINALITY: 2085 if alias and isinstance(offset, exp.Expression): 2086 alias.append("columns", offset) 2087 2088 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2089 columns = alias.columns 2090 alias = self.sql(columns[0]) if columns else "" 2091 else: 2092 alias = self.sql(alias) 2093 2094 alias = f" AS {alias}" if alias else alias 2095 if self.UNNEST_WITH_ORDINALITY: 2096 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2097 else: 2098 if isinstance(offset, exp.Expression): 2099 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2100 elif offset: 2101 suffix = f"{alias} WITH OFFSET" 2102 else: 2103 suffix = alias 2104 2105 return f"UNNEST({args}){suffix}" 2106 2107 def where_sql(self, expression: exp.Where) -> str: 2108 this = self.indent(self.sql(expression, "this")) 2109 return f"{self.seg('WHERE')}{self.sep()}{this}" 2110 2111 def window_sql(self, expression: exp.Window) -> str: 2112 this = self.sql(expression, "this") 2113 partition = self.partition_by_sql(expression) 2114 order = expression.args.get("order") 2115 order = self.order_sql(order, flat=True) if order else "" 2116 spec = self.sql(expression, "spec") 2117 alias = self.sql(expression, "alias") 2118 over = self.sql(expression, "over") or "OVER" 2119 2120 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2121 2122 first = expression.args.get("first") 2123 if first is None: 2124 first = "" 2125 else: 2126 first = "FIRST" if first else "LAST" 2127 2128 if not partition and not order and not spec and alias: 2129 return f"{this} {alias}" 2130 2131 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2132 return f"{this} ({args})" 2133 2134 def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str: 2135 partition = self.expressions(expression, key="partition_by", flat=True) 2136 return f"PARTITION BY {partition}" if partition else "" 2137 2138 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2139 kind = self.sql(expression, "kind") 2140 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2141 end = ( 2142 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2143 or "CURRENT ROW" 2144 ) 2145 return f"{kind} BETWEEN {start} AND {end}" 2146 2147 def withingroup_sql(self, expression: exp.WithinGroup) -> str: 2148 this = self.sql(expression, "this") 2149 expression_sql = self.sql(expression, "expression")[1:] # order has a leading space 2150 return f"{this} WITHIN GROUP ({expression_sql})" 2151 2152 def between_sql(self, expression: exp.Between) -> str: 2153 this = self.sql(expression, "this") 2154 low = self.sql(expression, "low") 2155 high = self.sql(expression, "high") 2156 return f"{this} BETWEEN {low} AND {high}" 2157 2158 def bracket_sql(self, expression: exp.Bracket) -> str: 2159 expressions = apply_index_offset( 2160 expression.this, 2161 expression.expressions, 2162 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2163 ) 2164 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2165 return f"{self.sql(expression, 'this')}[{expressions_sql}]" 2166 2167 def all_sql(self, expression: exp.All) -> str: 2168 return f"ALL {self.wrap(expression)}" 2169 2170 def any_sql(self, expression: exp.Any) -> str: 2171 this = self.sql(expression, "this") 2172 if isinstance(expression.this, exp.Subqueryable): 2173 this = self.wrap(this) 2174 return f"ANY {this}" 2175 2176 def exists_sql(self, expression: exp.Exists) -> str: 2177 return f"EXISTS{self.wrap(expression)}" 2178 2179 def case_sql(self, expression: exp.Case) -> str: 2180 this = self.sql(expression, "this") 2181 statements = [f"CASE {this}" if this else "CASE"] 2182 2183 for e in expression.args["ifs"]: 2184 statements.append(f"WHEN {self.sql(e, 'this')}") 2185 statements.append(f"THEN {self.sql(e, 'true')}") 2186 2187 default = self.sql(expression, "default") 2188 2189 if default: 2190 statements.append(f"ELSE {default}") 2191 2192 statements.append("END") 2193 2194 if self.pretty and self.text_width(statements) > self.max_text_width: 2195 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2196 2197 return " ".join(statements) 2198 2199 def constraint_sql(self, expression: exp.Constraint) -> str: 2200 this = self.sql(expression, "this") 2201 expressions = self.expressions(expression, flat=True) 2202 return f"CONSTRAINT {this} {expressions}" 2203 2204 def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str: 2205 order = expression.args.get("order") 2206 order = f" OVER ({self.order_sql(order, flat=True)})" if order else "" 2207 return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}" 2208 2209 def extract_sql(self, expression: exp.Extract) -> str: 2210 this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name 2211 expression_sql = self.sql(expression, "expression") 2212 return f"EXTRACT({this} FROM {expression_sql})" 2213 2214 def trim_sql(self, expression: exp.Trim) -> str: 2215 trim_type = self.sql(expression, "position") 2216 2217 if trim_type == "LEADING": 2218 return self.func("LTRIM", expression.this) 2219 elif trim_type == "TRAILING": 2220 return self.func("RTRIM", expression.this) 2221 else: 2222 return self.func("TRIM", expression.this, expression.expression) 2223 2224 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2225 args = expression.expressions 2226 if isinstance(expression, exp.ConcatWs): 2227 args = args[1:] # Skip the delimiter 2228 2229 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2230 args = [exp.cast(e, "text") for e in args] 2231 2232 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2233 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2234 2235 return args 2236 2237 def concat_sql(self, expression: exp.Concat) -> str: 2238 expressions = self.convert_concat_args(expression) 2239 2240 # Some dialects don't allow a single-argument CONCAT call 2241 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2242 return self.sql(expressions[0]) 2243 2244 return self.func("CONCAT", *expressions) 2245 2246 def concatws_sql(self, expression: exp.ConcatWs) -> str: 2247 return self.func( 2248 "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression) 2249 ) 2250 2251 def check_sql(self, expression: exp.Check) -> str: 2252 this = self.sql(expression, key="this") 2253 return f"CHECK ({this})" 2254 2255 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2256 expressions = self.expressions(expression, flat=True) 2257 reference = self.sql(expression, "reference") 2258 reference = f" {reference}" if reference else "" 2259 delete = self.sql(expression, "delete") 2260 delete = f" ON DELETE {delete}" if delete else "" 2261 update = self.sql(expression, "update") 2262 update = f" ON UPDATE {update}" if update else "" 2263 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}" 2264 2265 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2266 expressions = self.expressions(expression, flat=True) 2267 options = self.expressions(expression, key="options", flat=True, sep=" ") 2268 options = f" {options}" if options else "" 2269 return f"PRIMARY KEY ({expressions}){options}" 2270 2271 def if_sql(self, expression: exp.If) -> str: 2272 return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false"))) 2273 2274 def matchagainst_sql(self, expression: exp.MatchAgainst) -> str: 2275 modifier = expression.args.get("modifier") 2276 modifier = f" {modifier}" if modifier else "" 2277 return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})" 2278 2279 def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str: 2280 return f"{self.sql(expression, 'this')}: {self.sql(expression, 'expression')}" 2281 2282 def formatjson_sql(self, expression: exp.FormatJson) -> str: 2283 return f"{self.sql(expression, 'this')} FORMAT JSON" 2284 2285 def jsonobject_sql(self, expression: exp.JSONObject) -> str: 2286 null_handling = expression.args.get("null_handling") 2287 null_handling = f" {null_handling}" if null_handling else "" 2288 unique_keys = expression.args.get("unique_keys") 2289 if unique_keys is not None: 2290 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2291 else: 2292 unique_keys = "" 2293 return_type = self.sql(expression, "return_type") 2294 return_type = f" RETURNING {return_type}" if return_type else "" 2295 encoding = self.sql(expression, "encoding") 2296 encoding = f" ENCODING {encoding}" if encoding else "" 2297 return self.func( 2298 "JSON_OBJECT", 2299 *expression.expressions, 2300 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2301 ) 2302 2303 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2304 null_handling = expression.args.get("null_handling") 2305 null_handling = f" {null_handling}" if null_handling else "" 2306 return_type = self.sql(expression, "return_type") 2307 return_type = f" RETURNING {return_type}" if return_type else "" 2308 strict = " STRICT" if expression.args.get("strict") else "" 2309 return self.func( 2310 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2311 ) 2312 2313 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2314 this = self.sql(expression, "this") 2315 order = self.sql(expression, "order") 2316 null_handling = expression.args.get("null_handling") 2317 null_handling = f" {null_handling}" if null_handling else "" 2318 return_type = self.sql(expression, "return_type") 2319 return_type = f" RETURNING {return_type}" if return_type else "" 2320 strict = " STRICT" if expression.args.get("strict") else "" 2321 return self.func( 2322 "JSON_ARRAYAGG", 2323 this, 2324 suffix=f"{order}{null_handling}{return_type}{strict})", 2325 ) 2326 2327 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2328 path = self.sql(expression, "path") 2329 path = f" PATH {path}" if path else "" 2330 nested_schema = self.sql(expression, "nested_schema") 2331 2332 if nested_schema: 2333 return f"NESTED{path} {nested_schema}" 2334 2335 this = self.sql(expression, "this") 2336 kind = self.sql(expression, "kind") 2337 kind = f" {kind}" if kind else "" 2338 return f"{this}{kind}{path}" 2339 2340 def jsonschema_sql(self, expression: exp.JSONSchema) -> str: 2341 return self.func("COLUMNS", *expression.expressions) 2342 2343 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2344 this = self.sql(expression, "this") 2345 path = self.sql(expression, "path") 2346 path = f", {path}" if path else "" 2347 error_handling = expression.args.get("error_handling") 2348 error_handling = f" {error_handling}" if error_handling else "" 2349 empty_handling = expression.args.get("empty_handling") 2350 empty_handling = f" {empty_handling}" if empty_handling else "" 2351 schema = self.sql(expression, "schema") 2352 return self.func( 2353 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2354 ) 2355 2356 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2357 this = self.sql(expression, "this") 2358 kind = self.sql(expression, "kind") 2359 path = self.sql(expression, "path") 2360 path = f" {path}" if path else "" 2361 as_json = " AS JSON" if expression.args.get("as_json") else "" 2362 return f"{this} {kind}{path}{as_json}" 2363 2364 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2365 this = self.sql(expression, "this") 2366 path = self.sql(expression, "path") 2367 path = f", {path}" if path else "" 2368 expressions = self.expressions(expression) 2369 with_ = ( 2370 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2371 if expressions 2372 else "" 2373 ) 2374 return f"OPENJSON({this}{path}){with_}" 2375 2376 def in_sql(self, expression: exp.In) -> str: 2377 query = expression.args.get("query") 2378 unnest = expression.args.get("unnest") 2379 field = expression.args.get("field") 2380 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2381 2382 if query: 2383 in_sql = self.wrap(query) 2384 elif unnest: 2385 in_sql = self.in_unnest_op(unnest) 2386 elif field: 2387 in_sql = self.sql(field) 2388 else: 2389 in_sql = f"({self.expressions(expression, flat=True)})" 2390 2391 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}" 2392 2393 def in_unnest_op(self, unnest: exp.Unnest) -> str: 2394 return f"(SELECT {self.sql(unnest)})" 2395 2396 def interval_sql(self, expression: exp.Interval) -> str: 2397 unit = self.sql(expression, "unit") 2398 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2399 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2400 unit = f" {unit}" if unit else "" 2401 2402 if self.SINGLE_STRING_INTERVAL: 2403 this = expression.this.name if expression.this else "" 2404 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2405 2406 this = self.sql(expression, "this") 2407 if this: 2408 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2409 this = f" {this}" if unwrapped else f" ({this})" 2410 2411 return f"INTERVAL{this}{unit}" 2412 2413 def return_sql(self, expression: exp.Return) -> str: 2414 return f"RETURN {self.sql(expression, 'this')}" 2415 2416 def reference_sql(self, expression: exp.Reference) -> str: 2417 this = self.sql(expression, "this") 2418 expressions = self.expressions(expression, flat=True) 2419 expressions = f"({expressions})" if expressions else "" 2420 options = self.expressions(expression, key="options", flat=True, sep=" ") 2421 options = f" {options}" if options else "" 2422 return f"REFERENCES {this}{expressions}{options}" 2423 2424 def anonymous_sql(self, expression: exp.Anonymous) -> str: 2425 return self.func(expression.name, *expression.expressions) 2426 2427 def paren_sql(self, expression: exp.Paren) -> str: 2428 if isinstance(expression.unnest(), exp.Select): 2429 sql = self.wrap(expression) 2430 else: 2431 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2432 sql = f"({sql}{self.seg(')', sep='')}" 2433 2434 return self.prepend_ctes(expression, sql) 2435 2436 def neg_sql(self, expression: exp.Neg) -> str: 2437 # This makes sure we don't convert "- - 5" to "--5", which is a comment 2438 this_sql = self.sql(expression, "this") 2439 sep = " " if this_sql[0] == "-" else "" 2440 return f"-{sep}{this_sql}" 2441 2442 def not_sql(self, expression: exp.Not) -> str: 2443 return f"NOT {self.sql(expression, 'this')}" 2444 2445 def alias_sql(self, expression: exp.Alias) -> str: 2446 alias = self.sql(expression, "alias") 2447 alias = f" AS {alias}" if alias else "" 2448 return f"{self.sql(expression, 'this')}{alias}" 2449 2450 def aliases_sql(self, expression: exp.Aliases) -> str: 2451 return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})" 2452 2453 def atindex_sql(self, expression: exp.AtTimeZone) -> str: 2454 this = self.sql(expression, "this") 2455 index = self.sql(expression, "expression") 2456 return f"{this} AT {index}" 2457 2458 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 2459 this = self.sql(expression, "this") 2460 zone = self.sql(expression, "zone") 2461 return f"{this} AT TIME ZONE {zone}" 2462 2463 def add_sql(self, expression: exp.Add) -> str: 2464 return self.binary(expression, "+") 2465 2466 def and_sql(self, expression: exp.And) -> str: 2467 return self.connector_sql(expression, "AND") 2468 2469 def xor_sql(self, expression: exp.Xor) -> str: 2470 return self.connector_sql(expression, "XOR") 2471 2472 def connector_sql(self, expression: exp.Connector, op: str) -> str: 2473 if not self.pretty: 2474 return self.binary(expression, op) 2475 2476 sqls = tuple( 2477 self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e) 2478 for i, e in enumerate(expression.flatten(unnest=False)) 2479 ) 2480 2481 sep = "\n" if self.text_width(sqls) > self.max_text_width else " " 2482 return f"{sep}{op} ".join(sqls) 2483 2484 def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str: 2485 return self.binary(expression, "&") 2486 2487 def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str: 2488 return self.binary(expression, "<<") 2489 2490 def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str: 2491 return f"~{self.sql(expression, 'this')}" 2492 2493 def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str: 2494 return self.binary(expression, "|") 2495 2496 def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str: 2497 return self.binary(expression, ">>") 2498 2499 def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str: 2500 return self.binary(expression, "^") 2501 2502 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2503 format_sql = self.sql(expression, "format") 2504 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2505 to_sql = self.sql(expression, "to") 2506 to_sql = f" {to_sql}" if to_sql else "" 2507 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})" 2508 2509 def currentdate_sql(self, expression: exp.CurrentDate) -> str: 2510 zone = self.sql(expression, "this") 2511 return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE" 2512 2513 def collate_sql(self, expression: exp.Collate) -> str: 2514 if self.COLLATE_IS_FUNC: 2515 return self.function_fallback_sql(expression) 2516 return self.binary(expression, "COLLATE") 2517 2518 def command_sql(self, expression: exp.Command) -> str: 2519 return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}" 2520 2521 def comment_sql(self, expression: exp.Comment) -> str: 2522 this = self.sql(expression, "this") 2523 kind = expression.args["kind"] 2524 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2525 expression_sql = self.sql(expression, "expression") 2526 return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}" 2527 2528 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2529 this = self.sql(expression, "this") 2530 delete = " DELETE" if expression.args.get("delete") else "" 2531 recompress = self.sql(expression, "recompress") 2532 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2533 to_disk = self.sql(expression, "to_disk") 2534 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2535 to_volume = self.sql(expression, "to_volume") 2536 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2537 return f"{this}{delete}{recompress}{to_disk}{to_volume}" 2538 2539 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2540 where = self.sql(expression, "where") 2541 group = self.sql(expression, "group") 2542 aggregates = self.expressions(expression, key="aggregates") 2543 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2544 2545 if not (where or group or aggregates) and len(expression.expressions) == 1: 2546 return f"TTL {self.expressions(expression, flat=True)}" 2547 2548 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}" 2549 2550 def transaction_sql(self, expression: exp.Transaction) -> str: 2551 return "BEGIN" 2552 2553 def commit_sql(self, expression: exp.Commit) -> str: 2554 chain = expression.args.get("chain") 2555 if chain is not None: 2556 chain = " AND CHAIN" if chain else " AND NO CHAIN" 2557 2558 return f"COMMIT{chain or ''}" 2559 2560 def rollback_sql(self, expression: exp.Rollback) -> str: 2561 savepoint = expression.args.get("savepoint") 2562 savepoint = f" TO {savepoint}" if savepoint else "" 2563 return f"ROLLBACK{savepoint}" 2564 2565 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2566 this = self.sql(expression, "this") 2567 2568 dtype = self.sql(expression, "dtype") 2569 if dtype: 2570 collate = self.sql(expression, "collate") 2571 collate = f" COLLATE {collate}" if collate else "" 2572 using = self.sql(expression, "using") 2573 using = f" USING {using}" if using else "" 2574 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2575 2576 default = self.sql(expression, "default") 2577 if default: 2578 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2579 2580 if not expression.args.get("drop"): 2581 self.unsupported("Unsupported ALTER COLUMN syntax") 2582 2583 return f"ALTER COLUMN {this} DROP DEFAULT" 2584 2585 def renametable_sql(self, expression: exp.RenameTable) -> str: 2586 if not self.RENAME_TABLE_WITH_DB: 2587 # Remove db from tables 2588 expression = expression.transform( 2589 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 2590 ) 2591 this = self.sql(expression, "this") 2592 return f"RENAME TO {this}" 2593 2594 def altertable_sql(self, expression: exp.AlterTable) -> str: 2595 actions = expression.args["actions"] 2596 2597 if isinstance(actions[0], exp.ColumnDef): 2598 actions = self.add_column_sql(expression) 2599 elif isinstance(actions[0], exp.Schema): 2600 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 2601 elif isinstance(actions[0], exp.Delete): 2602 actions = self.expressions(expression, key="actions", flat=True) 2603 else: 2604 actions = self.expressions(expression, key="actions", flat=True) 2605 2606 exists = " IF EXISTS" if expression.args.get("exists") else "" 2607 only = " ONLY" if expression.args.get("only") else "" 2608 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}" 2609 2610 def add_column_sql(self, expression: exp.AlterTable) -> str: 2611 if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD: 2612 return self.expressions( 2613 expression, 2614 key="actions", 2615 prefix="ADD COLUMN ", 2616 ) 2617 return f"ADD {self.expressions(expression, key='actions', flat=True)}" 2618 2619 def droppartition_sql(self, expression: exp.DropPartition) -> str: 2620 expressions = self.expressions(expression) 2621 exists = " IF EXISTS " if expression.args.get("exists") else " " 2622 return f"DROP{exists}{expressions}" 2623 2624 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 2625 this = self.sql(expression, "this") 2626 expression_ = self.sql(expression, "expression") 2627 add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD" 2628 2629 enforced = expression.args.get("enforced") 2630 if enforced is not None: 2631 return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}" 2632 2633 return f"{add_constraint} {expression_}" 2634 2635 def distinct_sql(self, expression: exp.Distinct) -> str: 2636 this = self.expressions(expression, flat=True) 2637 this = f" {this}" if this else "" 2638 2639 on = self.sql(expression, "on") 2640 on = f" ON {on}" if on else "" 2641 return f"DISTINCT{this}{on}" 2642 2643 def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: 2644 return f"{self.sql(expression, 'this')} IGNORE NULLS" 2645 2646 def respectnulls_sql(self, expression: exp.RespectNulls) -> str: 2647 return f"{self.sql(expression, 'this')} RESPECT NULLS" 2648 2649 def intdiv_sql(self, expression: exp.IntDiv) -> str: 2650 return self.sql( 2651 exp.Cast( 2652 this=exp.Div(this=expression.this, expression=expression.expression), 2653 to=exp.DataType(this=exp.DataType.Type.INT), 2654 ) 2655 ) 2656 2657 def dpipe_sql(self, expression: exp.DPipe) -> str: 2658 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2659 return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten())) 2660 return self.binary(expression, "||") 2661 2662 def div_sql(self, expression: exp.Div) -> str: 2663 l, r = expression.left, expression.right 2664 2665 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 2666 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 2667 2668 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 2669 if not l.is_type(*exp.DataType.FLOAT_TYPES) and not r.is_type( 2670 *exp.DataType.FLOAT_TYPES 2671 ): 2672 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 2673 2674 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 2675 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 2676 return self.sql( 2677 exp.cast( 2678 l / r, 2679 to=exp.DataType.Type.BIGINT, 2680 ) 2681 ) 2682 2683 return self.binary(expression, "/") 2684 2685 def overlaps_sql(self, expression: exp.Overlaps) -> str: 2686 return self.binary(expression, "OVERLAPS") 2687 2688 def distance_sql(self, expression: exp.Distance) -> str: 2689 return self.binary(expression, "<->") 2690 2691 def dot_sql(self, expression: exp.Dot) -> str: 2692 return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}" 2693 2694 def eq_sql(self, expression: exp.EQ) -> str: 2695 return self.binary(expression, "=") 2696 2697 def propertyeq_sql(self, expression: exp.PropertyEQ) -> str: 2698 return self.binary(expression, ":=") 2699 2700 def escape_sql(self, expression: exp.Escape) -> str: 2701 return self.binary(expression, "ESCAPE") 2702 2703 def glob_sql(self, expression: exp.Glob) -> str: 2704 return self.binary(expression, "GLOB") 2705 2706 def gt_sql(self, expression: exp.GT) -> str: 2707 return self.binary(expression, ">") 2708 2709 def gte_sql(self, expression: exp.GTE) -> str: 2710 return self.binary(expression, ">=") 2711 2712 def ilike_sql(self, expression: exp.ILike) -> str: 2713 return self.binary(expression, "ILIKE") 2714 2715 def ilikeany_sql(self, expression: exp.ILikeAny) -> str: 2716 return self.binary(expression, "ILIKE ANY") 2717 2718 def is_sql(self, expression: exp.Is) -> str: 2719 if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean): 2720 return self.sql( 2721 expression.this if expression.expression.this else exp.not_(expression.this) 2722 ) 2723 return self.binary(expression, "IS") 2724 2725 def like_sql(self, expression: exp.Like) -> str: 2726 return self.binary(expression, "LIKE") 2727 2728 def likeany_sql(self, expression: exp.LikeAny) -> str: 2729 return self.binary(expression, "LIKE ANY") 2730 2731 def similarto_sql(self, expression: exp.SimilarTo) -> str: 2732 return self.binary(expression, "SIMILAR TO") 2733 2734 def lt_sql(self, expression: exp.LT) -> str: 2735 return self.binary(expression, "<") 2736 2737 def lte_sql(self, expression: exp.LTE) -> str: 2738 return self.binary(expression, "<=") 2739 2740 def mod_sql(self, expression: exp.Mod) -> str: 2741 return self.binary(expression, "%") 2742 2743 def mul_sql(self, expression: exp.Mul) -> str: 2744 return self.binary(expression, "*") 2745 2746 def neq_sql(self, expression: exp.NEQ) -> str: 2747 return self.binary(expression, "<>") 2748 2749 def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str: 2750 return self.binary(expression, "IS NOT DISTINCT FROM") 2751 2752 def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str: 2753 return self.binary(expression, "IS DISTINCT FROM") 2754 2755 def or_sql(self, expression: exp.Or) -> str: 2756 return self.connector_sql(expression, "OR") 2757 2758 def slice_sql(self, expression: exp.Slice) -> str: 2759 return self.binary(expression, ":") 2760 2761 def sub_sql(self, expression: exp.Sub) -> str: 2762 return self.binary(expression, "-") 2763 2764 def trycast_sql(self, expression: exp.TryCast) -> str: 2765 return self.cast_sql(expression, safe_prefix="TRY_") 2766 2767 def log_sql(self, expression: exp.Log) -> str: 2768 this = expression.this 2769 expr = expression.expression 2770 2771 if not self.dialect.LOG_BASE_FIRST: 2772 this, expr = expr, this 2773 2774 return self.func("LOG", this, expr) 2775 2776 def use_sql(self, expression: exp.Use) -> str: 2777 kind = self.sql(expression, "kind") 2778 kind = f" {kind}" if kind else "" 2779 this = self.sql(expression, "this") 2780 this = f" {this}" if this else "" 2781 return f"USE{kind}{this}" 2782 2783 def binary(self, expression: exp.Binary, op: str) -> str: 2784 op = self.maybe_comment(op, comments=expression.comments) 2785 return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}" 2786 2787 def function_fallback_sql(self, expression: exp.Func) -> str: 2788 args = [] 2789 2790 for key in expression.arg_types: 2791 arg_value = expression.args.get(key) 2792 2793 if isinstance(arg_value, list): 2794 for value in arg_value: 2795 args.append(value) 2796 elif arg_value is not None: 2797 args.append(arg_value) 2798 2799 if self.normalize_functions: 2800 name = expression.sql_name() 2801 else: 2802 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 2803 2804 return self.func(name, *args) 2805 2806 def func( 2807 self, 2808 name: str, 2809 *args: t.Optional[exp.Expression | str], 2810 prefix: str = "(", 2811 suffix: str = ")", 2812 ) -> str: 2813 return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}" 2814 2815 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 2816 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 2817 if self.pretty and self.text_width(arg_sqls) > self.max_text_width: 2818 return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 2819 return ", ".join(arg_sqls) 2820 2821 def text_width(self, args: t.Iterable) -> int: 2822 return sum(len(arg) for arg in args) 2823 2824 def format_time(self, expression: exp.Expression) -> t.Optional[str]: 2825 return format_time( 2826 self.sql(expression, "format"), 2827 self.dialect.INVERSE_TIME_MAPPING, 2828 self.dialect.INVERSE_TIME_TRIE, 2829 ) 2830 2831 def expressions( 2832 self, 2833 expression: t.Optional[exp.Expression] = None, 2834 key: t.Optional[str] = None, 2835 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 2836 flat: bool = False, 2837 indent: bool = True, 2838 skip_first: bool = False, 2839 sep: str = ", ", 2840 prefix: str = "", 2841 ) -> str: 2842 expressions = expression.args.get(key or "expressions") if expression else sqls 2843 2844 if not expressions: 2845 return "" 2846 2847 if flat: 2848 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 2849 2850 num_sqls = len(expressions) 2851 2852 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 2853 pad = " " * self.pad 2854 stripped_sep = sep.strip() 2855 2856 result_sqls = [] 2857 for i, e in enumerate(expressions): 2858 sql = self.sql(e, comment=False) 2859 if not sql: 2860 continue 2861 2862 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 2863 2864 if self.pretty: 2865 if self.leading_comma: 2866 result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}") 2867 else: 2868 result_sqls.append( 2869 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 2870 ) 2871 else: 2872 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 2873 2874 result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls) 2875 return self.indent(result_sql, skip_first=skip_first) if indent else result_sql 2876 2877 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 2878 flat = flat or isinstance(expression.parent, exp.Properties) 2879 expressions_sql = self.expressions(expression, flat=flat) 2880 if flat: 2881 return f"{op} {expressions_sql}" 2882 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}" 2883 2884 def naked_property(self, expression: exp.Property) -> str: 2885 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 2886 if not property_name: 2887 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 2888 return f"{property_name} {self.sql(expression, 'this')}" 2889 2890 def set_operation(self, expression: exp.Union, op: str) -> str: 2891 this = self.maybe_comment(self.sql(expression, "this"), comments=expression.comments) 2892 op = self.seg(op) 2893 return self.query_modifiers( 2894 expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}" 2895 ) 2896 2897 def tag_sql(self, expression: exp.Tag) -> str: 2898 return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}" 2899 2900 def token_sql(self, token_type: TokenType) -> str: 2901 return self.TOKEN_MAPPING.get(token_type, token_type.name) 2902 2903 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 2904 this = self.sql(expression, "this") 2905 expressions = self.no_identify(self.expressions, expression) 2906 expressions = ( 2907 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 2908 ) 2909 return f"{this}{expressions}" 2910 2911 def joinhint_sql(self, expression: exp.JoinHint) -> str: 2912 this = self.sql(expression, "this") 2913 expressions = self.expressions(expression, flat=True) 2914 return f"{this}({expressions})" 2915 2916 def kwarg_sql(self, expression: exp.Kwarg) -> str: 2917 return self.binary(expression, "=>") 2918 2919 def when_sql(self, expression: exp.When) -> str: 2920 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 2921 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 2922 condition = self.sql(expression, "condition") 2923 condition = f" AND {condition}" if condition else "" 2924 2925 then_expression = expression.args.get("then") 2926 if isinstance(then_expression, exp.Insert): 2927 then = f"INSERT {self.sql(then_expression, 'this')}" 2928 if "expression" in then_expression.args: 2929 then += f" VALUES {self.sql(then_expression, 'expression')}" 2930 elif isinstance(then_expression, exp.Update): 2931 if isinstance(then_expression.args.get("expressions"), exp.Star): 2932 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 2933 else: 2934 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 2935 else: 2936 then = self.sql(then_expression) 2937 return f"WHEN {matched}{source}{condition} THEN {then}" 2938 2939 def merge_sql(self, expression: exp.Merge) -> str: 2940 table = expression.this 2941 table_alias = "" 2942 2943 hints = table.args.get("hints") 2944 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 2945 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 2946 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 2947 2948 this = self.sql(table) 2949 using = f"USING {self.sql(expression, 'using')}" 2950 on = f"ON {self.sql(expression, 'on')}" 2951 expressions = self.expressions(expression, sep=" ") 2952 2953 return self.prepend_ctes( 2954 expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}" 2955 ) 2956 2957 def tochar_sql(self, expression: exp.ToChar) -> str: 2958 if expression.args.get("format"): 2959 self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function") 2960 2961 return self.sql(exp.cast(expression.this, "text")) 2962 2963 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 2964 this = self.sql(expression, "this") 2965 kind = self.sql(expression, "kind") 2966 settings_sql = self.expressions(expression, key="settings", sep=" ") 2967 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 2968 return f"{this}({kind}{args})" 2969 2970 def dictrange_sql(self, expression: exp.DictRange) -> str: 2971 this = self.sql(expression, "this") 2972 max = self.sql(expression, "max") 2973 min = self.sql(expression, "min") 2974 return f"{this}(MIN {min} MAX {max})" 2975 2976 def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str: 2977 return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}" 2978 2979 def oncluster_sql(self, expression: exp.OnCluster) -> str: 2980 return "" 2981 2982 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 2983 expressions = self.expressions(expression, key="expressions", flat=True) 2984 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 2985 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 2986 buckets = self.sql(expression, "buckets") 2987 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS" 2988 2989 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 2990 this = self.sql(expression, "this") 2991 having = self.sql(expression, "having") 2992 2993 if having: 2994 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 2995 2996 return self.func("ANY_VALUE", this) 2997 2998 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 2999 transform = self.func("TRANSFORM", *expression.expressions) 3000 row_format_before = self.sql(expression, "row_format_before") 3001 row_format_before = f" {row_format_before}" if row_format_before else "" 3002 record_writer = self.sql(expression, "record_writer") 3003 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3004 using = f" USING {self.sql(expression, 'command_script')}" 3005 schema = self.sql(expression, "schema") 3006 schema = f" AS {schema}" if schema else "" 3007 row_format_after = self.sql(expression, "row_format_after") 3008 row_format_after = f" {row_format_after}" if row_format_after else "" 3009 record_reader = self.sql(expression, "record_reader") 3010 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3011 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}" 3012 3013 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3014 key_block_size = self.sql(expression, "key_block_size") 3015 if key_block_size: 3016 return f"KEY_BLOCK_SIZE = {key_block_size}" 3017 3018 using = self.sql(expression, "using") 3019 if using: 3020 return f"USING {using}" 3021 3022 parser = self.sql(expression, "parser") 3023 if parser: 3024 return f"WITH PARSER {parser}" 3025 3026 comment = self.sql(expression, "comment") 3027 if comment: 3028 return f"COMMENT {comment}" 3029 3030 visible = expression.args.get("visible") 3031 if visible is not None: 3032 return "VISIBLE" if visible else "INVISIBLE" 3033 3034 engine_attr = self.sql(expression, "engine_attr") 3035 if engine_attr: 3036 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3037 3038 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3039 if secondary_engine_attr: 3040 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3041 3042 self.unsupported("Unsupported index constraint option.") 3043 return "" 3044 3045 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3046 kind = self.sql(expression, "kind") 3047 kind = f"{kind} INDEX" if kind else "INDEX" 3048 this = self.sql(expression, "this") 3049 this = f" {this}" if this else "" 3050 index_type = self.sql(expression, "index_type") 3051 index_type = f" USING {index_type}" if index_type else "" 3052 schema = self.sql(expression, "schema") 3053 schema = f" {schema}" if schema else "" 3054 options = self.expressions(expression, key="options", sep=" ") 3055 options = f" {options}" if options else "" 3056 return f"{kind}{this}{index_type}{schema}{options}" 3057 3058 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3059 if self.NVL2_SUPPORTED: 3060 return self.function_fallback_sql(expression) 3061 3062 case = exp.Case().when( 3063 expression.this.is_(exp.null()).not_(copy=False), 3064 expression.args["true"], 3065 copy=False, 3066 ) 3067 else_cond = expression.args.get("false") 3068 if else_cond: 3069 case.else_(else_cond, copy=False) 3070 3071 return self.sql(case) 3072 3073 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3074 this = self.sql(expression, "this") 3075 expr = self.sql(expression, "expression") 3076 iterator = self.sql(expression, "iterator") 3077 condition = self.sql(expression, "condition") 3078 condition = f" IF {condition}" if condition else "" 3079 return f"{this} FOR {expr} IN {iterator}{condition}" 3080 3081 def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str: 3082 return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})" 3083 3084 def opclass_sql(self, expression: exp.Opclass) -> str: 3085 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 3086 3087 def predict_sql(self, expression: exp.Predict) -> str: 3088 model = self.sql(expression, "this") 3089 model = f"MODEL {model}" 3090 table = self.sql(expression, "expression") 3091 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3092 parameters = self.sql(expression, "params_struct") 3093 return self.func("PREDICT", model, table, parameters or None) 3094 3095 def forin_sql(self, expression: exp.ForIn) -> str: 3096 this = self.sql(expression, "this") 3097 expression_sql = self.sql(expression, "expression") 3098 return f"FOR {this} DO {expression_sql}" 3099 3100 def refresh_sql(self, expression: exp.Refresh) -> str: 3101 this = self.sql(expression, "this") 3102 table = "" if isinstance(expression.this, exp.Literal) else "TABLE " 3103 return f"REFRESH {table}{this}" 3104 3105 def operator_sql(self, expression: exp.Operator) -> str: 3106 return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})") 3107 3108 def toarray_sql(self, expression: exp.ToArray) -> str: 3109 arg = expression.this 3110 if not arg.type: 3111 from sqlglot.optimizer.annotate_types import annotate_types 3112 3113 arg = annotate_types(arg) 3114 3115 if arg.is_type(exp.DataType.Type.ARRAY): 3116 return self.sql(arg) 3117 3118 cond_for_null = arg.is_(exp.null()) 3119 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.Array(expressions=[arg]))) 3120 3121 def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str: 3122 this = expression.this 3123 if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME): 3124 return self.sql(this) 3125 3126 return self.sql(exp.cast(this, "time")) 3127 3128 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3129 this = expression.this 3130 time_format = self.format_time(expression) 3131 3132 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3133 return self.sql( 3134 exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date") 3135 ) 3136 3137 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3138 return self.sql(this) 3139 3140 return self.sql(exp.cast(this, "date")) 3141 3142 def unixdate_sql(self, expression: exp.UnixDate) -> str: 3143 return self.sql( 3144 exp.func( 3145 "DATEDIFF", 3146 expression.this, 3147 exp.cast(exp.Literal.string("1970-01-01"), "date"), 3148 "day", 3149 ) 3150 ) 3151 3152 def lastday_sql(self, expression: exp.LastDay) -> str: 3153 if self.LAST_DAY_SUPPORTS_DATE_PART: 3154 return self.function_fallback_sql(expression) 3155 3156 unit = expression.text("unit") 3157 if unit and unit != "MONTH": 3158 self.unsupported("Date parts are not supported in LAST_DAY.") 3159 3160 return self.func("LAST_DAY", expression.this) 3161 3162 def _simplify_unless_literal(self, expression: E) -> E: 3163 if not isinstance(expression, exp.Literal): 3164 from sqlglot.optimizer.simplify import simplify 3165 3166 expression = simplify(expression, dialect=self.dialect) 3167 3168 return expression 3169 3170 def _ensure_string_if_null(self, values: t.List[exp.Expression]) -> t.List[exp.Expression]: 3171 return [ 3172 exp.func("COALESCE", exp.cast(value, "text"), exp.Literal.string("")) 3173 for value in values 3174 if value 3175 ]
logger =
<Logger sqlglot (WARNING)>
ESCAPED_UNICODE_RE =
re.compile('\\\\(\\d+)')
class
Generator:
25class Generator: 26 """ 27 Generator converts a given syntax tree to the corresponding SQL string. 28 29 Args: 30 pretty: Whether or not to format the produced SQL string. 31 Default: False. 32 identify: Determines when an identifier should be quoted. Possible values are: 33 False (default): Never quote, except in cases where it's mandatory by the dialect. 34 True or 'always': Always quote. 35 'safe': Only quote identifiers that are case insensitive. 36 normalize: Whether or not to normalize identifiers to lowercase. 37 Default: False. 38 pad: Determines the pad size in a formatted string. 39 Default: 2. 40 indent: Determines the indentation size in a formatted string. 41 Default: 2. 42 normalize_functions: Whether or not to normalize all function names. Possible values are: 43 "upper" or True (default): Convert names to uppercase. 44 "lower": Convert names to lowercase. 45 False: Disables function name normalization. 46 unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. 47 Default ErrorLevel.WARN. 48 max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. 49 This is only relevant if unsupported_level is ErrorLevel.RAISE. 50 Default: 3 51 leading_comma: Determines whether or not the comma is leading or trailing in select expressions. 52 This is only relevant when generating in pretty mode. 53 Default: False 54 max_text_width: The max number of characters in a segment before creating new lines in pretty mode. 55 The default is on the smaller end because the length only represents a segment and not the true 56 line length. 57 Default: 80 58 comments: Whether or not to preserve comments in the output SQL code. 59 Default: True 60 """ 61 62 TRANSFORMS = { 63 exp.DateAdd: lambda self, e: self.func( 64 "DATE_ADD", e.this, e.expression, exp.Literal.string(e.text("unit")) 65 ), 66 exp.CaseSpecificColumnConstraint: lambda self, e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC", 67 exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}", 68 exp.CharacterSetProperty: lambda self, e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}", 69 exp.CheckColumnConstraint: lambda self, e: f"CHECK ({self.sql(e, 'this')})", 70 exp.ClusteredColumnConstraint: lambda self, e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})", 71 exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}", 72 exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}", 73 exp.CopyGrantsProperty: lambda self, e: "COPY GRANTS", 74 exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}", 75 exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}", 76 exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}", 77 exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}", 78 exp.ExecuteAsProperty: lambda self, e: self.naked_property(e), 79 exp.ExternalProperty: lambda self, e: "EXTERNAL", 80 exp.HeapProperty: lambda self, e: "HEAP", 81 exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}", 82 exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}", 83 exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}", 84 exp.LanguageProperty: lambda self, e: self.naked_property(e), 85 exp.LocationProperty: lambda self, e: self.naked_property(e), 86 exp.LogProperty: lambda self, e: f"{'NO ' if e.args.get('no') else ''}LOG", 87 exp.MaterializedProperty: lambda self, e: "MATERIALIZED", 88 exp.NoPrimaryIndexProperty: lambda self, e: "NO PRIMARY INDEX", 89 exp.NonClusteredColumnConstraint: lambda self, e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})", 90 exp.NotForReplicationColumnConstraint: lambda self, e: "NOT FOR REPLICATION", 91 exp.OnCommitProperty: lambda self, e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS", 92 exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}", 93 exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}", 94 exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}", 95 exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}", 96 exp.RemoteWithConnectionModelProperty: lambda self, e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}", 97 exp.ReturnsProperty: lambda self, e: self.naked_property(e), 98 exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}", 99 exp.SetProperty: lambda self, e: f"{'MULTI' if e.args.get('multi') else ''}SET", 100 exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}", 101 exp.SqlSecurityProperty: lambda self, e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}", 102 exp.StabilityProperty: lambda self, e: e.name, 103 exp.TemporaryProperty: lambda self, e: f"TEMPORARY", 104 exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}", 105 exp.TransientProperty: lambda self, e: "TRANSIENT", 106 exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions), 107 exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}", 108 exp.UppercaseColumnConstraint: lambda self, e: f"UPPERCASE", 109 exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]), 110 exp.VolatileProperty: lambda self, e: "VOLATILE", 111 exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}", 112 } 113 114 # Whether or not null ordering is supported in order by 115 NULL_ORDERING_SUPPORTED = True 116 117 # Whether or not locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported 118 LOCKING_READS_SUPPORTED = False 119 120 # Always do union distinct or union all 121 EXPLICIT_UNION = False 122 123 # Wrap derived values in parens, usually standard but spark doesn't support it 124 WRAP_DERIVED_VALUES = True 125 126 # Whether or not create function uses an AS before the RETURN 127 CREATE_FUNCTION_RETURN_AS = True 128 129 # Whether or not MERGE ... WHEN MATCHED BY SOURCE is allowed 130 MATCHED_BY_SOURCE = True 131 132 # Whether or not the INTERVAL expression works only with values like '1 day' 133 SINGLE_STRING_INTERVAL = False 134 135 # Whether or not the plural form of date parts like day (i.e. "days") is supported in INTERVALs 136 INTERVAL_ALLOWS_PLURAL_FORM = True 137 138 # Whether or not limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH") 139 LIMIT_FETCH = "ALL" 140 141 # Whether or not limit and fetch allows expresions or just limits 142 LIMIT_ONLY_LITERALS = False 143 144 # Whether or not a table is allowed to be renamed with a db 145 RENAME_TABLE_WITH_DB = True 146 147 # The separator for grouping sets and rollups 148 GROUPINGS_SEP = "," 149 150 # The string used for creating an index on a table 151 INDEX_ON = "ON" 152 153 # Whether or not join hints should be generated 154 JOIN_HINTS = True 155 156 # Whether or not table hints should be generated 157 TABLE_HINTS = True 158 159 # Whether or not query hints should be generated 160 QUERY_HINTS = True 161 162 # What kind of separator to use for query hints 163 QUERY_HINT_SEP = ", " 164 165 # Whether or not comparing against booleans (e.g. x IS TRUE) is supported 166 IS_BOOL_ALLOWED = True 167 168 # Whether or not to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement 169 DUPLICATE_KEY_UPDATE_WITH_SET = True 170 171 # Whether or not to generate the limit as TOP <value> instead of LIMIT <value> 172 LIMIT_IS_TOP = False 173 174 # Whether or not to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ... 175 RETURNING_END = True 176 177 # Whether or not to generate the (+) suffix for columns used in old-style join conditions 178 COLUMN_JOIN_MARKS_SUPPORTED = False 179 180 # Whether or not to generate an unquoted value for EXTRACT's date part argument 181 EXTRACT_ALLOWS_QUOTES = True 182 183 # Whether or not TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax 184 TZ_TO_WITH_TIME_ZONE = False 185 186 # Whether or not the NVL2 function is supported 187 NVL2_SUPPORTED = True 188 189 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax 190 SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE") 191 192 # Whether or not VALUES statements can be used as derived tables. 193 # MySQL 5 and Redshift do not allow this, so when False, it will convert 194 # SELECT * VALUES into SELECT UNION 195 VALUES_AS_TABLE = True 196 197 # Whether or not the word COLUMN is included when adding a column with ALTER TABLE 198 ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True 199 200 # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery) 201 UNNEST_WITH_ORDINALITY = True 202 203 # Whether or not FILTER (WHERE cond) can be used for conditional aggregation 204 AGGREGATE_FILTER_SUPPORTED = True 205 206 # Whether or not JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds 207 SEMI_ANTI_JOIN_WITH_SIDE = True 208 209 # Whether or not to include the type of a computed column in the CREATE DDL 210 COMPUTED_COLUMN_WITH_TYPE = True 211 212 # Whether or not CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY 213 SUPPORTS_TABLE_COPY = True 214 215 # Whether or not parentheses are required around the table sample's expression 216 TABLESAMPLE_REQUIRES_PARENS = True 217 218 # Whether or not a table sample clause's size needs to be followed by the ROWS keyword 219 TABLESAMPLE_SIZE_IS_ROWS = True 220 221 # The keyword(s) to use when generating a sample clause 222 TABLESAMPLE_KEYWORDS = "TABLESAMPLE" 223 224 # Whether or not the TABLESAMPLE clause supports a method name, like BERNOULLI 225 TABLESAMPLE_WITH_METHOD = True 226 227 # The keyword to use when specifying the seed of a sample clause 228 TABLESAMPLE_SEED_KEYWORD = "SEED" 229 230 # Whether or not COLLATE is a function instead of a binary operator 231 COLLATE_IS_FUNC = False 232 233 # Whether or not data types support additional specifiers like e.g. CHAR or BYTE (oracle) 234 DATA_TYPE_SPECIFIERS_ALLOWED = False 235 236 # Whether or not conditions require booleans WHERE x = 0 vs WHERE x 237 ENSURE_BOOLS = False 238 239 # Whether or not the "RECURSIVE" keyword is required when defining recursive CTEs 240 CTE_RECURSIVE_KEYWORD_REQUIRED = True 241 242 # Whether or not CONCAT requires >1 arguments 243 SUPPORTS_SINGLE_ARG_CONCAT = True 244 245 # Whether or not LAST_DAY function supports a date part argument 246 LAST_DAY_SUPPORTS_DATE_PART = True 247 248 TYPE_MAPPING = { 249 exp.DataType.Type.NCHAR: "CHAR", 250 exp.DataType.Type.NVARCHAR: "VARCHAR", 251 exp.DataType.Type.MEDIUMTEXT: "TEXT", 252 exp.DataType.Type.LONGTEXT: "TEXT", 253 exp.DataType.Type.TINYTEXT: "TEXT", 254 exp.DataType.Type.MEDIUMBLOB: "BLOB", 255 exp.DataType.Type.LONGBLOB: "BLOB", 256 exp.DataType.Type.TINYBLOB: "BLOB", 257 exp.DataType.Type.INET: "INET", 258 } 259 260 STAR_MAPPING = { 261 "except": "EXCEPT", 262 "replace": "REPLACE", 263 } 264 265 TIME_PART_SINGULARS = { 266 "MICROSECONDS": "MICROSECOND", 267 "SECONDS": "SECOND", 268 "MINUTES": "MINUTE", 269 "HOURS": "HOUR", 270 "DAYS": "DAY", 271 "WEEKS": "WEEK", 272 "MONTHS": "MONTH", 273 "QUARTERS": "QUARTER", 274 "YEARS": "YEAR", 275 } 276 277 TOKEN_MAPPING: t.Dict[TokenType, str] = {} 278 279 STRUCT_DELIMITER = ("<", ">") 280 281 PARAMETER_TOKEN = "@" 282 283 PROPERTIES_LOCATION = { 284 exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE, 285 exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA, 286 exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA, 287 exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME, 288 exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA, 289 exp.ChecksumProperty: exp.Properties.Location.POST_NAME, 290 exp.CollateProperty: exp.Properties.Location.POST_SCHEMA, 291 exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA, 292 exp.Cluster: exp.Properties.Location.POST_SCHEMA, 293 exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA, 294 exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME, 295 exp.DefinerProperty: exp.Properties.Location.POST_CREATE, 296 exp.DictRange: exp.Properties.Location.POST_SCHEMA, 297 exp.DictProperty: exp.Properties.Location.POST_SCHEMA, 298 exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA, 299 exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA, 300 exp.EngineProperty: exp.Properties.Location.POST_SCHEMA, 301 exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA, 302 exp.ExternalProperty: exp.Properties.Location.POST_CREATE, 303 exp.FallbackProperty: exp.Properties.Location.POST_NAME, 304 exp.FileFormatProperty: exp.Properties.Location.POST_WITH, 305 exp.FreespaceProperty: exp.Properties.Location.POST_NAME, 306 exp.HeapProperty: exp.Properties.Location.POST_WITH, 307 exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA, 308 exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME, 309 exp.JournalProperty: exp.Properties.Location.POST_NAME, 310 exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA, 311 exp.LikeProperty: exp.Properties.Location.POST_SCHEMA, 312 exp.LocationProperty: exp.Properties.Location.POST_SCHEMA, 313 exp.LockingProperty: exp.Properties.Location.POST_ALIAS, 314 exp.LogProperty: exp.Properties.Location.POST_NAME, 315 exp.MaterializedProperty: exp.Properties.Location.POST_CREATE, 316 exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME, 317 exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION, 318 exp.OnProperty: exp.Properties.Location.POST_SCHEMA, 319 exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION, 320 exp.Order: exp.Properties.Location.POST_SCHEMA, 321 exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA, 322 exp.PartitionedByProperty: exp.Properties.Location.POST_WITH, 323 exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA, 324 exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA, 325 exp.Property: exp.Properties.Location.POST_WITH, 326 exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA, 327 exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA, 328 exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA, 329 exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA, 330 exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA, 331 exp.SampleProperty: exp.Properties.Location.POST_SCHEMA, 332 exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA, 333 exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA, 334 exp.Set: exp.Properties.Location.POST_SCHEMA, 335 exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA, 336 exp.SetProperty: exp.Properties.Location.POST_CREATE, 337 exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA, 338 exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE, 339 exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA, 340 exp.TemporaryProperty: exp.Properties.Location.POST_CREATE, 341 exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA, 342 exp.TransientProperty: exp.Properties.Location.POST_CREATE, 343 exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA, 344 exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA, 345 exp.VolatileProperty: exp.Properties.Location.POST_CREATE, 346 exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION, 347 exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME, 348 exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA, 349 } 350 351 # Keywords that can't be used as unquoted identifier names 352 RESERVED_KEYWORDS: t.Set[str] = set() 353 354 # Expressions whose comments are separated from them for better formatting 355 WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 356 exp.Create, 357 exp.Delete, 358 exp.Drop, 359 exp.From, 360 exp.Insert, 361 exp.Join, 362 exp.Select, 363 exp.Update, 364 exp.Where, 365 exp.With, 366 ) 367 368 # Expressions that should not have their comments generated in maybe_comment 369 EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 370 exp.Binary, 371 exp.Union, 372 ) 373 374 # Expressions that can remain unwrapped when appearing in the context of an INTERVAL 375 UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = ( 376 exp.Column, 377 exp.Literal, 378 exp.Neg, 379 exp.Paren, 380 ) 381 382 # Expressions that need to have all CTEs under them bubbled up to them 383 EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set() 384 385 KEY_VALUE_DEFINITIONS = (exp.Bracket, exp.EQ, exp.PropertyEQ, exp.Slice) 386 387 SENTINEL_LINE_BREAK = "__SQLGLOT__LB__" 388 389 __slots__ = ( 390 "pretty", 391 "identify", 392 "normalize", 393 "pad", 394 "_indent", 395 "normalize_functions", 396 "unsupported_level", 397 "max_unsupported", 398 "leading_comma", 399 "max_text_width", 400 "comments", 401 "dialect", 402 "unsupported_messages", 403 "_escaped_quote_end", 404 "_escaped_identifier_end", 405 ) 406 407 def __init__( 408 self, 409 pretty: t.Optional[bool] = None, 410 identify: str | bool = False, 411 normalize: bool = False, 412 pad: int = 2, 413 indent: int = 2, 414 normalize_functions: t.Optional[str | bool] = None, 415 unsupported_level: ErrorLevel = ErrorLevel.WARN, 416 max_unsupported: int = 3, 417 leading_comma: bool = False, 418 max_text_width: int = 80, 419 comments: bool = True, 420 dialect: DialectType = None, 421 ): 422 import sqlglot 423 from sqlglot.dialects import Dialect 424 425 self.pretty = pretty if pretty is not None else sqlglot.pretty 426 self.identify = identify 427 self.normalize = normalize 428 self.pad = pad 429 self._indent = indent 430 self.unsupported_level = unsupported_level 431 self.max_unsupported = max_unsupported 432 self.leading_comma = leading_comma 433 self.max_text_width = max_text_width 434 self.comments = comments 435 self.dialect = Dialect.get_or_raise(dialect) 436 437 # This is both a Dialect property and a Generator argument, so we prioritize the latter 438 self.normalize_functions = ( 439 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 440 ) 441 442 self.unsupported_messages: t.List[str] = [] 443 self._escaped_quote_end: str = ( 444 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 445 ) 446 self._escaped_identifier_end: str = ( 447 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 448 ) 449 450 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 451 """ 452 Generates the SQL string corresponding to the given syntax tree. 453 454 Args: 455 expression: The syntax tree. 456 copy: Whether or not to copy the expression. The generator performs mutations so 457 it is safer to copy. 458 459 Returns: 460 The SQL string corresponding to `expression`. 461 """ 462 if copy: 463 expression = expression.copy() 464 465 expression = self.preprocess(expression) 466 467 self.unsupported_messages = [] 468 sql = self.sql(expression).strip() 469 470 if self.pretty: 471 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 472 473 if self.unsupported_level == ErrorLevel.IGNORE: 474 return sql 475 476 if self.unsupported_level == ErrorLevel.WARN: 477 for msg in self.unsupported_messages: 478 logger.warning(msg) 479 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 480 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 481 482 return sql 483 484 def preprocess(self, expression: exp.Expression) -> exp.Expression: 485 """Apply generic preprocessing transformations to a given expression.""" 486 if ( 487 not expression.parent 488 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 489 and any(node.parent is not expression for node in expression.find_all(exp.With)) 490 ): 491 from sqlglot.transforms import move_ctes_to_top_level 492 493 expression = move_ctes_to_top_level(expression) 494 495 if self.ENSURE_BOOLS: 496 from sqlglot.transforms import ensure_bools 497 498 expression = ensure_bools(expression) 499 500 return expression 501 502 def unsupported(self, message: str) -> None: 503 if self.unsupported_level == ErrorLevel.IMMEDIATE: 504 raise UnsupportedError(message) 505 self.unsupported_messages.append(message) 506 507 def sep(self, sep: str = " ") -> str: 508 return f"{sep.strip()}\n" if self.pretty else sep 509 510 def seg(self, sql: str, sep: str = " ") -> str: 511 return f"{self.sep(sep)}{sql}" 512 513 def pad_comment(self, comment: str) -> str: 514 comment = " " + comment if comment[0].strip() else comment 515 comment = comment + " " if comment[-1].strip() else comment 516 return comment 517 518 def maybe_comment( 519 self, 520 sql: str, 521 expression: t.Optional[exp.Expression] = None, 522 comments: t.Optional[t.List[str]] = None, 523 ) -> str: 524 comments = ( 525 ((expression and expression.comments) if comments is None else comments) # type: ignore 526 if self.comments 527 else None 528 ) 529 530 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 531 return sql 532 533 comments_sql = " ".join( 534 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 535 ) 536 537 if not comments_sql: 538 return sql 539 540 if isinstance(expression, self.WITH_SEPARATED_COMMENTS): 541 return ( 542 f"{self.sep()}{comments_sql}{sql}" 543 if sql[0].isspace() 544 else f"{comments_sql}{self.sep()}{sql}" 545 ) 546 547 return f"{sql} {comments_sql}" 548 549 def wrap(self, expression: exp.Expression | str) -> str: 550 this_sql = self.indent( 551 self.sql(expression) 552 if isinstance(expression, (exp.Select, exp.Union)) 553 else self.sql(expression, "this"), 554 level=1, 555 pad=0, 556 ) 557 return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}" 558 559 def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str: 560 original = self.identify 561 self.identify = False 562 result = func(*args, **kwargs) 563 self.identify = original 564 return result 565 566 def normalize_func(self, name: str) -> str: 567 if self.normalize_functions == "upper" or self.normalize_functions is True: 568 return name.upper() 569 if self.normalize_functions == "lower": 570 return name.lower() 571 return name 572 573 def indent( 574 self, 575 sql: str, 576 level: int = 0, 577 pad: t.Optional[int] = None, 578 skip_first: bool = False, 579 skip_last: bool = False, 580 ) -> str: 581 if not self.pretty: 582 return sql 583 584 pad = self.pad if pad is None else pad 585 lines = sql.split("\n") 586 587 return "\n".join( 588 line 589 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 590 else f"{' ' * (level * self._indent + pad)}{line}" 591 for i, line in enumerate(lines) 592 ) 593 594 def sql( 595 self, 596 expression: t.Optional[str | exp.Expression], 597 key: t.Optional[str] = None, 598 comment: bool = True, 599 ) -> str: 600 if not expression: 601 return "" 602 603 if isinstance(expression, str): 604 return expression 605 606 if key: 607 value = expression.args.get(key) 608 if value: 609 return self.sql(value) 610 return "" 611 612 transform = self.TRANSFORMS.get(expression.__class__) 613 614 if callable(transform): 615 sql = transform(self, expression) 616 elif transform: 617 sql = transform 618 elif isinstance(expression, exp.Expression): 619 exp_handler_name = f"{expression.key}_sql" 620 621 if hasattr(self, exp_handler_name): 622 sql = getattr(self, exp_handler_name)(expression) 623 elif isinstance(expression, exp.Func): 624 sql = self.function_fallback_sql(expression) 625 elif isinstance(expression, exp.Property): 626 sql = self.property_sql(expression) 627 else: 628 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 629 else: 630 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 631 632 return self.maybe_comment(sql, expression) if self.comments and comment else sql 633 634 def uncache_sql(self, expression: exp.Uncache) -> str: 635 table = self.sql(expression, "this") 636 exists_sql = " IF EXISTS" if expression.args.get("exists") else "" 637 return f"UNCACHE TABLE{exists_sql} {table}" 638 639 def cache_sql(self, expression: exp.Cache) -> str: 640 lazy = " LAZY" if expression.args.get("lazy") else "" 641 table = self.sql(expression, "this") 642 options = expression.args.get("options") 643 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 644 sql = self.sql(expression, "expression") 645 sql = f" AS{self.sep()}{sql}" if sql else "" 646 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 647 return self.prepend_ctes(expression, sql) 648 649 def characterset_sql(self, expression: exp.CharacterSet) -> str: 650 if isinstance(expression.parent, exp.Cast): 651 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 652 default = "DEFAULT " if expression.args.get("default") else "" 653 return f"{default}CHARACTER SET={self.sql(expression, 'this')}" 654 655 def column_sql(self, expression: exp.Column) -> str: 656 join_mark = " (+)" if expression.args.get("join_mark") else "" 657 658 if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED: 659 join_mark = "" 660 self.unsupported("Outer join syntax using the (+) operator is not supported.") 661 662 column = ".".join( 663 self.sql(part) 664 for part in ( 665 expression.args.get("catalog"), 666 expression.args.get("db"), 667 expression.args.get("table"), 668 expression.args.get("this"), 669 ) 670 if part 671 ) 672 673 return f"{column}{join_mark}" 674 675 def columnposition_sql(self, expression: exp.ColumnPosition) -> str: 676 this = self.sql(expression, "this") 677 this = f" {this}" if this else "" 678 position = self.sql(expression, "position") 679 return f"{position}{this}" 680 681 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 682 column = self.sql(expression, "this") 683 kind = self.sql(expression, "kind") 684 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 685 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 686 kind = f"{sep}{kind}" if kind else "" 687 constraints = f" {constraints}" if constraints else "" 688 position = self.sql(expression, "position") 689 position = f" {position}" if position else "" 690 691 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 692 kind = "" 693 694 return f"{exists}{column}{kind}{constraints}{position}" 695 696 def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str: 697 this = self.sql(expression, "this") 698 kind_sql = self.sql(expression, "kind").strip() 699 return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql 700 701 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 702 this = self.sql(expression, "this") 703 if expression.args.get("not_null"): 704 persisted = " PERSISTED NOT NULL" 705 elif expression.args.get("persisted"): 706 persisted = " PERSISTED" 707 else: 708 persisted = "" 709 return f"AS {this}{persisted}" 710 711 def autoincrementcolumnconstraint_sql(self, _) -> str: 712 return self.token_sql(TokenType.AUTO_INCREMENT) 713 714 def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str: 715 if isinstance(expression.this, list): 716 this = self.wrap(self.expressions(expression, key="this", flat=True)) 717 else: 718 this = self.sql(expression, "this") 719 720 return f"COMPRESS {this}" 721 722 def generatedasidentitycolumnconstraint_sql( 723 self, expression: exp.GeneratedAsIdentityColumnConstraint 724 ) -> str: 725 this = "" 726 if expression.this is not None: 727 on_null = " ON NULL" if expression.args.get("on_null") else "" 728 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 729 730 start = expression.args.get("start") 731 start = f"START WITH {start}" if start else "" 732 increment = expression.args.get("increment") 733 increment = f" INCREMENT BY {increment}" if increment else "" 734 minvalue = expression.args.get("minvalue") 735 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 736 maxvalue = expression.args.get("maxvalue") 737 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 738 cycle = expression.args.get("cycle") 739 cycle_sql = "" 740 741 if cycle is not None: 742 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 743 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 744 745 sequence_opts = "" 746 if start or increment or cycle_sql: 747 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 748 sequence_opts = f" ({sequence_opts.strip()})" 749 750 expr = self.sql(expression, "expression") 751 expr = f"({expr})" if expr else "IDENTITY" 752 753 return f"GENERATED{this} AS {expr}{sequence_opts}" 754 755 def generatedasrowcolumnconstraint_sql( 756 self, expression: exp.GeneratedAsRowColumnConstraint 757 ) -> str: 758 start = "START" if expression.args["start"] else "END" 759 hidden = " HIDDEN" if expression.args.get("hidden") else "" 760 return f"GENERATED ALWAYS AS ROW {start}{hidden}" 761 762 def periodforsystemtimeconstraint_sql( 763 self, expression: exp.PeriodForSystemTimeConstraint 764 ) -> str: 765 return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})" 766 767 def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str: 768 return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL" 769 770 def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str: 771 return f"AS {self.sql(expression, 'this')}" 772 773 def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str: 774 desc = expression.args.get("desc") 775 if desc is not None: 776 return f"PRIMARY KEY{' DESC' if desc else ' ASC'}" 777 return f"PRIMARY KEY" 778 779 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 780 this = self.sql(expression, "this") 781 this = f" {this}" if this else "" 782 index_type = expression.args.get("index_type") 783 index_type = f" USING {index_type}" if index_type else "" 784 return f"UNIQUE{this}{index_type}" 785 786 def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str: 787 return self.sql(expression, "this") 788 789 def create_sql(self, expression: exp.Create) -> str: 790 kind = self.sql(expression, "kind") 791 properties = expression.args.get("properties") 792 properties_locs = self.locate_properties(properties) if properties else defaultdict() 793 794 this = self.createable_sql(expression, properties_locs) 795 796 properties_sql = "" 797 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 798 exp.Properties.Location.POST_WITH 799 ): 800 properties_sql = self.sql( 801 exp.Properties( 802 expressions=[ 803 *properties_locs[exp.Properties.Location.POST_SCHEMA], 804 *properties_locs[exp.Properties.Location.POST_WITH], 805 ] 806 ) 807 ) 808 809 begin = " BEGIN" if expression.args.get("begin") else "" 810 end = " END" if expression.args.get("end") else "" 811 812 expression_sql = self.sql(expression, "expression") 813 if expression_sql: 814 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 815 816 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 817 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 818 postalias_props_sql = self.properties( 819 exp.Properties( 820 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 821 ), 822 wrapped=False, 823 ) 824 expression_sql = f" AS {postalias_props_sql}{expression_sql}" 825 else: 826 expression_sql = f" AS{expression_sql}" 827 828 postindex_props_sql = "" 829 if properties_locs.get(exp.Properties.Location.POST_INDEX): 830 postindex_props_sql = self.properties( 831 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 832 wrapped=False, 833 prefix=" ", 834 ) 835 836 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 837 indexes = f" {indexes}" if indexes else "" 838 index_sql = indexes + postindex_props_sql 839 840 replace = " OR REPLACE" if expression.args.get("replace") else "" 841 unique = " UNIQUE" if expression.args.get("unique") else "" 842 843 postcreate_props_sql = "" 844 if properties_locs.get(exp.Properties.Location.POST_CREATE): 845 postcreate_props_sql = self.properties( 846 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 847 sep=" ", 848 prefix=" ", 849 wrapped=False, 850 ) 851 852 modifiers = "".join((replace, unique, postcreate_props_sql)) 853 854 postexpression_props_sql = "" 855 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 856 postexpression_props_sql = self.properties( 857 exp.Properties( 858 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 859 ), 860 sep=" ", 861 prefix=" ", 862 wrapped=False, 863 ) 864 865 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 866 no_schema_binding = ( 867 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 868 ) 869 870 clone = self.sql(expression, "clone") 871 clone = f" {clone}" if clone else "" 872 873 expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 874 return self.prepend_ctes(expression, expression_sql) 875 876 def clone_sql(self, expression: exp.Clone) -> str: 877 this = self.sql(expression, "this") 878 shallow = "SHALLOW " if expression.args.get("shallow") else "" 879 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 880 return f"{shallow}{keyword} {this}" 881 882 def describe_sql(self, expression: exp.Describe) -> str: 883 return f"DESCRIBE {self.sql(expression, 'this')}" 884 885 def prepend_ctes(self, expression: exp.Expression, sql: str) -> str: 886 with_ = self.sql(expression, "with") 887 if with_: 888 sql = f"{with_}{self.sep()}{sql}" 889 return sql 890 891 def with_sql(self, expression: exp.With) -> str: 892 sql = self.expressions(expression, flat=True) 893 recursive = ( 894 "RECURSIVE " 895 if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive") 896 else "" 897 ) 898 899 return f"WITH {recursive}{sql}" 900 901 def cte_sql(self, expression: exp.CTE) -> str: 902 alias = self.sql(expression, "alias") 903 return f"{alias} AS {self.wrap(expression)}" 904 905 def tablealias_sql(self, expression: exp.TableAlias) -> str: 906 alias = self.sql(expression, "this") 907 columns = self.expressions(expression, key="columns", flat=True) 908 columns = f"({columns})" if columns else "" 909 910 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 911 alias = "_t" 912 913 return f"{alias}{columns}" 914 915 def bitstring_sql(self, expression: exp.BitString) -> str: 916 this = self.sql(expression, "this") 917 if self.dialect.BIT_START: 918 return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}" 919 return f"{int(this, 2)}" 920 921 def hexstring_sql(self, expression: exp.HexString) -> str: 922 this = self.sql(expression, "this") 923 if self.dialect.HEX_START: 924 return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}" 925 return f"{int(this, 16)}" 926 927 def bytestring_sql(self, expression: exp.ByteString) -> str: 928 this = self.sql(expression, "this") 929 if self.dialect.BYTE_START: 930 return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}" 931 return this 932 933 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 934 this = self.sql(expression, "this") 935 escape = expression.args.get("escape") 936 937 if self.dialect.UNICODE_START: 938 escape = f" UESCAPE {self.sql(escape)}" if escape else "" 939 return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}" 940 941 if escape: 942 pattern = re.compile(rf"{escape.name}(\d+)") 943 else: 944 pattern = ESCAPED_UNICODE_RE 945 946 this = pattern.sub(r"\\u\1", this) 947 return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}" 948 949 def rawstring_sql(self, expression: exp.RawString) -> str: 950 string = self.escape_str(expression.this.replace("\\", "\\\\")) 951 return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}" 952 953 def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str: 954 this = self.sql(expression, "this") 955 specifier = self.sql(expression, "expression") 956 specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else "" 957 return f"{this}{specifier}" 958 959 def datatype_sql(self, expression: exp.DataType) -> str: 960 type_value = expression.this 961 962 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 963 type_sql = self.sql(expression, "kind") 964 else: 965 type_sql = ( 966 self.TYPE_MAPPING.get(type_value, type_value.value) 967 if isinstance(type_value, exp.DataType.Type) 968 else type_value 969 ) 970 971 nested = "" 972 interior = self.expressions(expression, flat=True) 973 values = "" 974 975 if interior: 976 if expression.args.get("nested"): 977 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 978 if expression.args.get("values") is not None: 979 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 980 values = self.expressions(expression, key="values", flat=True) 981 values = f"{delimiters[0]}{values}{delimiters[1]}" 982 elif type_value == exp.DataType.Type.INTERVAL: 983 nested = f" {interior}" 984 else: 985 nested = f"({interior})" 986 987 type_sql = f"{type_sql}{nested}{values}" 988 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 989 exp.DataType.Type.TIMETZ, 990 exp.DataType.Type.TIMESTAMPTZ, 991 ): 992 type_sql = f"{type_sql} WITH TIME ZONE" 993 994 return type_sql 995 996 def directory_sql(self, expression: exp.Directory) -> str: 997 local = "LOCAL " if expression.args.get("local") else "" 998 row_format = self.sql(expression, "row_format") 999 row_format = f" {row_format}" if row_format else "" 1000 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}" 1001 1002 def delete_sql(self, expression: exp.Delete) -> str: 1003 this = self.sql(expression, "this") 1004 this = f" FROM {this}" if this else "" 1005 using = self.sql(expression, "using") 1006 using = f" USING {using}" if using else "" 1007 where = self.sql(expression, "where") 1008 returning = self.sql(expression, "returning") 1009 limit = self.sql(expression, "limit") 1010 tables = self.expressions(expression, key="tables") 1011 tables = f" {tables}" if tables else "" 1012 if self.RETURNING_END: 1013 expression_sql = f"{this}{using}{where}{returning}{limit}" 1014 else: 1015 expression_sql = f"{returning}{this}{using}{where}{limit}" 1016 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}") 1017 1018 def drop_sql(self, expression: exp.Drop) -> str: 1019 this = self.sql(expression, "this") 1020 kind = expression.args["kind"] 1021 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1022 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1023 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1024 cascade = " CASCADE" if expression.args.get("cascade") else "" 1025 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1026 purge = " PURGE" if expression.args.get("purge") else "" 1027 return ( 1028 f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}" 1029 ) 1030 1031 def except_sql(self, expression: exp.Except) -> str: 1032 return self.prepend_ctes( 1033 expression, 1034 self.set_operation(expression, self.except_op(expression)), 1035 ) 1036 1037 def except_op(self, expression: exp.Except) -> str: 1038 return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}" 1039 1040 def fetch_sql(self, expression: exp.Fetch) -> str: 1041 direction = expression.args.get("direction") 1042 direction = f" {direction}" if direction else "" 1043 count = expression.args.get("count") 1044 count = f" {count}" if count else "" 1045 if expression.args.get("percent"): 1046 count = f"{count} PERCENT" 1047 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1048 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}" 1049 1050 def filter_sql(self, expression: exp.Filter) -> str: 1051 if self.AGGREGATE_FILTER_SUPPORTED: 1052 this = self.sql(expression, "this") 1053 where = self.sql(expression, "expression").strip() 1054 return f"{this} FILTER({where})" 1055 1056 agg = expression.this 1057 agg_arg = agg.this 1058 cond = expression.expression.this 1059 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1060 return self.sql(agg) 1061 1062 def hint_sql(self, expression: exp.Hint) -> str: 1063 if not self.QUERY_HINTS: 1064 self.unsupported("Hints are not supported") 1065 return "" 1066 1067 return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */" 1068 1069 def index_sql(self, expression: exp.Index) -> str: 1070 unique = "UNIQUE " if expression.args.get("unique") else "" 1071 primary = "PRIMARY " if expression.args.get("primary") else "" 1072 amp = "AMP " if expression.args.get("amp") else "" 1073 name = self.sql(expression, "this") 1074 name = f"{name} " if name else "" 1075 table = self.sql(expression, "table") 1076 table = f"{self.INDEX_ON} {table}" if table else "" 1077 using = self.sql(expression, "using") 1078 using = f" USING {using}" if using else "" 1079 index = "INDEX " if not table else "" 1080 columns = self.expressions(expression, key="columns", flat=True) 1081 columns = f"({columns})" if columns else "" 1082 partition_by = self.expressions(expression, key="partition_by", flat=True) 1083 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1084 where = self.sql(expression, "where") 1085 return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{partition_by}{where}" 1086 1087 def identifier_sql(self, expression: exp.Identifier) -> str: 1088 text = expression.name 1089 lower = text.lower() 1090 text = lower if self.normalize and not expression.quoted else text 1091 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1092 if ( 1093 expression.quoted 1094 or self.dialect.can_identify(text, self.identify) 1095 or lower in self.RESERVED_KEYWORDS 1096 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1097 ): 1098 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1099 return text 1100 1101 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1102 input_format = self.sql(expression, "input_format") 1103 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1104 output_format = self.sql(expression, "output_format") 1105 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1106 return self.sep().join((input_format, output_format)) 1107 1108 def national_sql(self, expression: exp.National, prefix: str = "N") -> str: 1109 string = self.sql(exp.Literal.string(expression.name)) 1110 return f"{prefix}{string}" 1111 1112 def partition_sql(self, expression: exp.Partition) -> str: 1113 return f"PARTITION({self.expressions(expression, flat=True)})" 1114 1115 def properties_sql(self, expression: exp.Properties) -> str: 1116 root_properties = [] 1117 with_properties = [] 1118 1119 for p in expression.expressions: 1120 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1121 if p_loc == exp.Properties.Location.POST_WITH: 1122 with_properties.append(p) 1123 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1124 root_properties.append(p) 1125 1126 return self.root_properties( 1127 exp.Properties(expressions=root_properties) 1128 ) + self.with_properties(exp.Properties(expressions=with_properties)) 1129 1130 def root_properties(self, properties: exp.Properties) -> str: 1131 if properties.expressions: 1132 return self.sep() + self.expressions(properties, indent=False, sep=" ") 1133 return "" 1134 1135 def properties( 1136 self, 1137 properties: exp.Properties, 1138 prefix: str = "", 1139 sep: str = ", ", 1140 suffix: str = "", 1141 wrapped: bool = True, 1142 ) -> str: 1143 if properties.expressions: 1144 expressions = self.expressions(properties, sep=sep, indent=False) 1145 if expressions: 1146 expressions = self.wrap(expressions) if wrapped else expressions 1147 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1148 return "" 1149 1150 def with_properties(self, properties: exp.Properties) -> str: 1151 return self.properties(properties, prefix=self.seg("WITH")) 1152 1153 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1154 properties_locs = defaultdict(list) 1155 for p in properties.expressions: 1156 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1157 if p_loc != exp.Properties.Location.UNSUPPORTED: 1158 properties_locs[p_loc].append(p) 1159 else: 1160 self.unsupported(f"Unsupported property {p.key}") 1161 1162 return properties_locs 1163 1164 def property_name(self, expression: exp.Property, string_key: bool = False) -> str: 1165 if isinstance(expression.this, exp.Dot): 1166 return self.sql(expression, "this") 1167 return f"'{expression.name}'" if string_key else expression.name 1168 1169 def property_sql(self, expression: exp.Property) -> str: 1170 property_cls = expression.__class__ 1171 if property_cls == exp.Property: 1172 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1173 1174 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1175 if not property_name: 1176 self.unsupported(f"Unsupported property {expression.key}") 1177 1178 return f"{property_name}={self.sql(expression, 'this')}" 1179 1180 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1181 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1182 options = f" {options}" if options else "" 1183 return f"LIKE {self.sql(expression, 'this')}{options}" 1184 1185 def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str: 1186 no = "NO " if expression.args.get("no") else "" 1187 protection = " PROTECTION" if expression.args.get("protection") else "" 1188 return f"{no}FALLBACK{protection}" 1189 1190 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1191 no = "NO " if expression.args.get("no") else "" 1192 local = expression.args.get("local") 1193 local = f"{local} " if local else "" 1194 dual = "DUAL " if expression.args.get("dual") else "" 1195 before = "BEFORE " if expression.args.get("before") else "" 1196 after = "AFTER " if expression.args.get("after") else "" 1197 return f"{no}{local}{dual}{before}{after}JOURNAL" 1198 1199 def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str: 1200 freespace = self.sql(expression, "this") 1201 percent = " PERCENT" if expression.args.get("percent") else "" 1202 return f"FREESPACE={freespace}{percent}" 1203 1204 def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str: 1205 if expression.args.get("default"): 1206 property = "DEFAULT" 1207 elif expression.args.get("on"): 1208 property = "ON" 1209 else: 1210 property = "OFF" 1211 return f"CHECKSUM={property}" 1212 1213 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1214 if expression.args.get("no"): 1215 return "NO MERGEBLOCKRATIO" 1216 if expression.args.get("default"): 1217 return "DEFAULT MERGEBLOCKRATIO" 1218 1219 percent = " PERCENT" if expression.args.get("percent") else "" 1220 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}" 1221 1222 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1223 default = expression.args.get("default") 1224 minimum = expression.args.get("minimum") 1225 maximum = expression.args.get("maximum") 1226 if default or minimum or maximum: 1227 if default: 1228 prop = "DEFAULT" 1229 elif minimum: 1230 prop = "MINIMUM" 1231 else: 1232 prop = "MAXIMUM" 1233 return f"{prop} DATABLOCKSIZE" 1234 units = expression.args.get("units") 1235 units = f" {units}" if units else "" 1236 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}" 1237 1238 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1239 autotemp = expression.args.get("autotemp") 1240 always = expression.args.get("always") 1241 default = expression.args.get("default") 1242 manual = expression.args.get("manual") 1243 never = expression.args.get("never") 1244 1245 if autotemp is not None: 1246 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1247 elif always: 1248 prop = "ALWAYS" 1249 elif default: 1250 prop = "DEFAULT" 1251 elif manual: 1252 prop = "MANUAL" 1253 elif never: 1254 prop = "NEVER" 1255 return f"BLOCKCOMPRESSION={prop}" 1256 1257 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1258 no = expression.args.get("no") 1259 no = " NO" if no else "" 1260 concurrent = expression.args.get("concurrent") 1261 concurrent = " CONCURRENT" if concurrent else "" 1262 1263 for_ = "" 1264 if expression.args.get("for_all"): 1265 for_ = " FOR ALL" 1266 elif expression.args.get("for_insert"): 1267 for_ = " FOR INSERT" 1268 elif expression.args.get("for_none"): 1269 for_ = " FOR NONE" 1270 return f"WITH{no}{concurrent} ISOLATED LOADING{for_}" 1271 1272 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1273 if isinstance(expression.this, list): 1274 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1275 if expression.this: 1276 modulus = self.sql(expression, "this") 1277 remainder = self.sql(expression, "expression") 1278 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1279 1280 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1281 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1282 return f"FROM ({from_expressions}) TO ({to_expressions})" 1283 1284 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1285 this = self.sql(expression, "this") 1286 1287 for_values_or_default = expression.expression 1288 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1289 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1290 else: 1291 for_values_or_default = " DEFAULT" 1292 1293 return f"PARTITION OF {this}{for_values_or_default}" 1294 1295 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1296 kind = expression.args.get("kind") 1297 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1298 for_or_in = expression.args.get("for_or_in") 1299 for_or_in = f" {for_or_in}" if for_or_in else "" 1300 lock_type = expression.args.get("lock_type") 1301 override = " OVERRIDE" if expression.args.get("override") else "" 1302 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}" 1303 1304 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1305 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1306 statistics = expression.args.get("statistics") 1307 statistics_sql = "" 1308 if statistics is not None: 1309 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1310 return f"{data_sql}{statistics_sql}" 1311 1312 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1313 sql = "WITH(SYSTEM_VERSIONING=ON" 1314 1315 if expression.this: 1316 history_table = self.sql(expression, "this") 1317 sql = f"{sql}(HISTORY_TABLE={history_table}" 1318 1319 if expression.expression: 1320 data_consistency_check = self.sql(expression, "expression") 1321 sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}" 1322 1323 sql = f"{sql})" 1324 1325 return f"{sql})" 1326 1327 def insert_sql(self, expression: exp.Insert) -> str: 1328 overwrite = expression.args.get("overwrite") 1329 1330 if isinstance(expression.this, exp.Directory): 1331 this = " OVERWRITE" if overwrite else " INTO" 1332 else: 1333 this = " OVERWRITE TABLE" if overwrite else " INTO" 1334 1335 alternative = expression.args.get("alternative") 1336 alternative = f" OR {alternative}" if alternative else "" 1337 ignore = " IGNORE" if expression.args.get("ignore") else "" 1338 1339 this = f"{this} {self.sql(expression, 'this')}" 1340 1341 exists = " IF EXISTS" if expression.args.get("exists") else "" 1342 partition_sql = ( 1343 f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else "" 1344 ) 1345 where = self.sql(expression, "where") 1346 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1347 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1348 conflict = self.sql(expression, "conflict") 1349 by_name = " BY NAME" if expression.args.get("by_name") else "" 1350 returning = self.sql(expression, "returning") 1351 1352 if self.RETURNING_END: 1353 expression_sql = f"{expression_sql}{conflict}{returning}" 1354 else: 1355 expression_sql = f"{returning}{expression_sql}{conflict}" 1356 1357 sql = f"INSERT{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}" 1358 return self.prepend_ctes(expression, sql) 1359 1360 def intersect_sql(self, expression: exp.Intersect) -> str: 1361 return self.prepend_ctes( 1362 expression, 1363 self.set_operation(expression, self.intersect_op(expression)), 1364 ) 1365 1366 def intersect_op(self, expression: exp.Intersect) -> str: 1367 return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}" 1368 1369 def introducer_sql(self, expression: exp.Introducer) -> str: 1370 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 1371 1372 def kill_sql(self, expression: exp.Kill) -> str: 1373 kind = self.sql(expression, "kind") 1374 kind = f" {kind}" if kind else "" 1375 this = self.sql(expression, "this") 1376 this = f" {this}" if this else "" 1377 return f"KILL{kind}{this}" 1378 1379 def pseudotype_sql(self, expression: exp.PseudoType) -> str: 1380 return expression.name 1381 1382 def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str: 1383 return expression.name 1384 1385 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1386 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1387 constraint = self.sql(expression, "constraint") 1388 if constraint: 1389 constraint = f"ON CONSTRAINT {constraint}" 1390 key = self.expressions(expression, key="key", flat=True) 1391 do = "" if expression.args.get("duplicate") else " DO " 1392 nothing = "NOTHING" if expression.args.get("nothing") else "" 1393 expressions = self.expressions(expression, flat=True) 1394 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1395 if expressions: 1396 expressions = f"UPDATE {set_keyword}{expressions}" 1397 return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}" 1398 1399 def returning_sql(self, expression: exp.Returning) -> str: 1400 return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}" 1401 1402 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1403 fields = expression.args.get("fields") 1404 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1405 escaped = expression.args.get("escaped") 1406 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1407 items = expression.args.get("collection_items") 1408 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1409 keys = expression.args.get("map_keys") 1410 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1411 lines = expression.args.get("lines") 1412 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1413 null = expression.args.get("null") 1414 null = f" NULL DEFINED AS {null}" if null else "" 1415 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}" 1416 1417 def withtablehint_sql(self, expression: exp.WithTableHint) -> str: 1418 return f"WITH ({self.expressions(expression, flat=True)})" 1419 1420 def indextablehint_sql(self, expression: exp.IndexTableHint) -> str: 1421 this = f"{self.sql(expression, 'this')} INDEX" 1422 target = self.sql(expression, "target") 1423 target = f" FOR {target}" if target else "" 1424 return f"{this}{target} ({self.expressions(expression, flat=True)})" 1425 1426 def historicaldata_sql(self, expression: exp.HistoricalData) -> str: 1427 this = self.sql(expression, "this") 1428 kind = self.sql(expression, "kind") 1429 expr = self.sql(expression, "expression") 1430 return f"{this} ({kind} => {expr})" 1431 1432 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1433 table = ".".join( 1434 self.sql(part) 1435 for part in ( 1436 expression.args.get("catalog"), 1437 expression.args.get("db"), 1438 expression.args.get("this"), 1439 ) 1440 if part is not None 1441 ) 1442 1443 version = self.sql(expression, "version") 1444 version = f" {version}" if version else "" 1445 alias = self.sql(expression, "alias") 1446 alias = f"{sep}{alias}" if alias else "" 1447 hints = self.expressions(expression, key="hints", sep=" ") 1448 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1449 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 1450 pivots = f" {pivots}" if pivots else "" 1451 joins = self.expressions(expression, key="joins", sep="", skip_first=True) 1452 laterals = self.expressions(expression, key="laterals", sep="") 1453 1454 file_format = self.sql(expression, "format") 1455 if file_format: 1456 pattern = self.sql(expression, "pattern") 1457 pattern = f", PATTERN => {pattern}" if pattern else "" 1458 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1459 1460 ordinality = expression.args.get("ordinality") or "" 1461 if ordinality: 1462 ordinality = f" WITH ORDINALITY{alias}" 1463 alias = "" 1464 1465 when = self.sql(expression, "when") 1466 if when: 1467 table = f"{table} {when}" 1468 1469 return f"{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}" 1470 1471 def tablesample_sql( 1472 self, 1473 expression: exp.TableSample, 1474 sep: str = " AS ", 1475 tablesample_keyword: t.Optional[str] = None, 1476 ) -> str: 1477 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1478 table = expression.this.copy() 1479 table.set("alias", None) 1480 this = self.sql(table) 1481 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1482 else: 1483 this = self.sql(expression, "this") 1484 alias = "" 1485 1486 method = self.sql(expression, "method") 1487 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1488 numerator = self.sql(expression, "bucket_numerator") 1489 denominator = self.sql(expression, "bucket_denominator") 1490 field = self.sql(expression, "bucket_field") 1491 field = f" ON {field}" if field else "" 1492 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1493 seed = self.sql(expression, "seed") 1494 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1495 1496 size = self.sql(expression, "size") 1497 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1498 size = f"{size} ROWS" 1499 1500 percent = self.sql(expression, "percent") 1501 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1502 percent = f"{percent} PERCENT" 1503 1504 expr = f"{bucket}{percent}{size}" 1505 if self.TABLESAMPLE_REQUIRES_PARENS: 1506 expr = f"({expr})" 1507 1508 return ( 1509 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1510 ) 1511 1512 def pivot_sql(self, expression: exp.Pivot) -> str: 1513 expressions = self.expressions(expression, flat=True) 1514 1515 if expression.this: 1516 this = self.sql(expression, "this") 1517 if not expressions: 1518 return f"UNPIVOT {this}" 1519 1520 on = f"{self.seg('ON')} {expressions}" 1521 using = self.expressions(expression, key="using", flat=True) 1522 using = f"{self.seg('USING')} {using}" if using else "" 1523 group = self.sql(expression, "group") 1524 return f"PIVOT {this}{on}{using}{group}" 1525 1526 alias = self.sql(expression, "alias") 1527 alias = f" AS {alias}" if alias else "" 1528 unpivot = expression.args.get("unpivot") 1529 direction = "UNPIVOT" if unpivot else "PIVOT" 1530 field = self.sql(expression, "field") 1531 include_nulls = expression.args.get("include_nulls") 1532 if include_nulls is not None: 1533 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1534 else: 1535 nulls = "" 1536 return f"{direction}{nulls}({expressions} FOR {field}){alias}" 1537 1538 def version_sql(self, expression: exp.Version) -> str: 1539 this = f"FOR {expression.name}" 1540 kind = expression.text("kind") 1541 expr = self.sql(expression, "expression") 1542 return f"{this} {kind} {expr}" 1543 1544 def tuple_sql(self, expression: exp.Tuple) -> str: 1545 return f"({self.expressions(expression, flat=True)})" 1546 1547 def update_sql(self, expression: exp.Update) -> str: 1548 this = self.sql(expression, "this") 1549 set_sql = self.expressions(expression, flat=True) 1550 from_sql = self.sql(expression, "from") 1551 where_sql = self.sql(expression, "where") 1552 returning = self.sql(expression, "returning") 1553 order = self.sql(expression, "order") 1554 limit = self.sql(expression, "limit") 1555 if self.RETURNING_END: 1556 expression_sql = f"{from_sql}{where_sql}{returning}" 1557 else: 1558 expression_sql = f"{returning}{from_sql}{where_sql}" 1559 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1560 return self.prepend_ctes(expression, sql) 1561 1562 def values_sql(self, expression: exp.Values) -> str: 1563 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1564 if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join): 1565 args = self.expressions(expression) 1566 alias = self.sql(expression, "alias") 1567 values = f"VALUES{self.seg('')}{args}" 1568 values = ( 1569 f"({values})" 1570 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1571 else values 1572 ) 1573 return f"{values} AS {alias}" if alias else values 1574 1575 # Converts `VALUES...` expression into a series of select unions. 1576 alias_node = expression.args.get("alias") 1577 column_names = alias_node and alias_node.columns 1578 1579 selects: t.List[exp.Subqueryable] = [] 1580 1581 for i, tup in enumerate(expression.expressions): 1582 row = tup.expressions 1583 1584 if i == 0 and column_names: 1585 row = [ 1586 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1587 ] 1588 1589 selects.append(exp.Select(expressions=row)) 1590 1591 if self.pretty: 1592 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1593 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1594 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1595 subqueryable = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1596 return self.subquery_sql( 1597 subqueryable.subquery(alias_node and alias_node.this, copy=False) 1598 ) 1599 1600 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1601 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1602 return f"({unions}){alias}" 1603 1604 def var_sql(self, expression: exp.Var) -> str: 1605 return self.sql(expression, "this") 1606 1607 def into_sql(self, expression: exp.Into) -> str: 1608 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1609 unlogged = " UNLOGGED" if expression.args.get("unlogged") else "" 1610 return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}" 1611 1612 def from_sql(self, expression: exp.From) -> str: 1613 return f"{self.seg('FROM')} {self.sql(expression, 'this')}" 1614 1615 def group_sql(self, expression: exp.Group) -> str: 1616 group_by = self.op_expressions("GROUP BY", expression) 1617 1618 if expression.args.get("all"): 1619 return f"{group_by} ALL" 1620 1621 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1622 grouping_sets = ( 1623 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1624 ) 1625 1626 cube = expression.args.get("cube", []) 1627 if seq_get(cube, 0) is True: 1628 return f"{group_by}{self.seg('WITH CUBE')}" 1629 else: 1630 cube_sql = self.expressions(expression, key="cube", indent=False) 1631 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1632 1633 rollup = expression.args.get("rollup", []) 1634 if seq_get(rollup, 0) is True: 1635 return f"{group_by}{self.seg('WITH ROLLUP')}" 1636 else: 1637 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1638 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1639 1640 groupings = csv( 1641 grouping_sets, 1642 cube_sql, 1643 rollup_sql, 1644 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1645 sep=self.GROUPINGS_SEP, 1646 ) 1647 1648 if expression.args.get("expressions") and groupings: 1649 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1650 1651 return f"{group_by}{groupings}" 1652 1653 def having_sql(self, expression: exp.Having) -> str: 1654 this = self.indent(self.sql(expression, "this")) 1655 return f"{self.seg('HAVING')}{self.sep()}{this}" 1656 1657 def connect_sql(self, expression: exp.Connect) -> str: 1658 start = self.sql(expression, "start") 1659 start = self.seg(f"START WITH {start}") if start else "" 1660 connect = self.sql(expression, "connect") 1661 connect = self.seg(f"CONNECT BY {connect}") 1662 return start + connect 1663 1664 def prior_sql(self, expression: exp.Prior) -> str: 1665 return f"PRIOR {self.sql(expression, 'this')}" 1666 1667 def join_sql(self, expression: exp.Join) -> str: 1668 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1669 side = None 1670 else: 1671 side = expression.side 1672 1673 op_sql = " ".join( 1674 op 1675 for op in ( 1676 expression.method, 1677 "GLOBAL" if expression.args.get("global") else None, 1678 side, 1679 expression.kind, 1680 expression.hint if self.JOIN_HINTS else None, 1681 ) 1682 if op 1683 ) 1684 on_sql = self.sql(expression, "on") 1685 using = expression.args.get("using") 1686 1687 if not on_sql and using: 1688 on_sql = csv(*(self.sql(column) for column in using)) 1689 1690 this_sql = self.sql(expression, "this") 1691 1692 if on_sql: 1693 on_sql = self.indent(on_sql, skip_first=True) 1694 space = self.seg(" " * self.pad) if self.pretty else " " 1695 if using: 1696 on_sql = f"{space}USING ({on_sql})" 1697 else: 1698 on_sql = f"{space}ON {on_sql}" 1699 elif not op_sql: 1700 return f", {this_sql}" 1701 1702 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1703 return f"{self.seg(op_sql)} {this_sql}{on_sql}" 1704 1705 def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str: 1706 args = self.expressions(expression, flat=True) 1707 args = f"({args})" if len(args.split(",")) > 1 else args 1708 return f"{args} {arrow_sep} {self.sql(expression, 'this')}" 1709 1710 def lateral_sql(self, expression: exp.Lateral) -> str: 1711 this = self.sql(expression, "this") 1712 1713 if expression.args.get("view"): 1714 alias = expression.args["alias"] 1715 columns = self.expressions(alias, key="columns", flat=True) 1716 table = f" {alias.name}" if alias.name else "" 1717 columns = f" AS {columns}" if columns else "" 1718 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1719 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1720 1721 alias = self.sql(expression, "alias") 1722 alias = f" AS {alias}" if alias else "" 1723 return f"LATERAL {this}{alias}" 1724 1725 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1726 this = self.sql(expression, "this") 1727 1728 args = [ 1729 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1730 for e in (expression.args.get(k) for k in ("offset", "expression")) 1731 if e 1732 ] 1733 1734 args_sql = ", ".join(self.sql(e) for e in args) 1735 args_sql = f"({args_sql})" if any(top and not e.is_number for e in args) else args_sql 1736 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}" 1737 1738 def offset_sql(self, expression: exp.Offset) -> str: 1739 this = self.sql(expression, "this") 1740 expression = expression.expression 1741 expression = ( 1742 self._simplify_unless_literal(expression) if self.LIMIT_ONLY_LITERALS else expression 1743 ) 1744 return f"{this}{self.seg('OFFSET')} {self.sql(expression)}" 1745 1746 def setitem_sql(self, expression: exp.SetItem) -> str: 1747 kind = self.sql(expression, "kind") 1748 kind = f"{kind} " if kind else "" 1749 this = self.sql(expression, "this") 1750 expressions = self.expressions(expression) 1751 collate = self.sql(expression, "collate") 1752 collate = f" COLLATE {collate}" if collate else "" 1753 global_ = "GLOBAL " if expression.args.get("global") else "" 1754 return f"{global_}{kind}{this}{expressions}{collate}" 1755 1756 def set_sql(self, expression: exp.Set) -> str: 1757 expressions = ( 1758 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 1759 ) 1760 tag = " TAG" if expression.args.get("tag") else "" 1761 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}" 1762 1763 def pragma_sql(self, expression: exp.Pragma) -> str: 1764 return f"PRAGMA {self.sql(expression, 'this')}" 1765 1766 def lock_sql(self, expression: exp.Lock) -> str: 1767 if not self.LOCKING_READS_SUPPORTED: 1768 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 1769 return "" 1770 1771 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 1772 expressions = self.expressions(expression, flat=True) 1773 expressions = f" OF {expressions}" if expressions else "" 1774 wait = expression.args.get("wait") 1775 1776 if wait is not None: 1777 if isinstance(wait, exp.Literal): 1778 wait = f" WAIT {self.sql(wait)}" 1779 else: 1780 wait = " NOWAIT" if wait else " SKIP LOCKED" 1781 1782 return f"{lock_type}{expressions}{wait or ''}" 1783 1784 def literal_sql(self, expression: exp.Literal) -> str: 1785 text = expression.this or "" 1786 if expression.is_string: 1787 text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}" 1788 return text 1789 1790 def escape_str(self, text: str) -> str: 1791 text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end) 1792 if self.dialect.INVERSE_ESCAPE_SEQUENCES: 1793 text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text) 1794 elif self.pretty: 1795 text = text.replace("\n", self.SENTINEL_LINE_BREAK) 1796 return text 1797 1798 def loaddata_sql(self, expression: exp.LoadData) -> str: 1799 local = " LOCAL" if expression.args.get("local") else "" 1800 inpath = f" INPATH {self.sql(expression, 'inpath')}" 1801 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 1802 this = f" INTO TABLE {self.sql(expression, 'this')}" 1803 partition = self.sql(expression, "partition") 1804 partition = f" {partition}" if partition else "" 1805 input_format = self.sql(expression, "input_format") 1806 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 1807 serde = self.sql(expression, "serde") 1808 serde = f" SERDE {serde}" if serde else "" 1809 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}" 1810 1811 def null_sql(self, *_) -> str: 1812 return "NULL" 1813 1814 def boolean_sql(self, expression: exp.Boolean) -> str: 1815 return "TRUE" if expression.this else "FALSE" 1816 1817 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 1818 this = self.sql(expression, "this") 1819 this = f"{this} " if this else this 1820 order = self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat) # type: ignore 1821 interpolated_values = [ 1822 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 1823 for named_expression in expression.args.get("interpolate") or [] 1824 ] 1825 interpolate = ( 1826 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 1827 ) 1828 return f"{order}{interpolate}" 1829 1830 def withfill_sql(self, expression: exp.WithFill) -> str: 1831 from_sql = self.sql(expression, "from") 1832 from_sql = f" FROM {from_sql}" if from_sql else "" 1833 to_sql = self.sql(expression, "to") 1834 to_sql = f" TO {to_sql}" if to_sql else "" 1835 step_sql = self.sql(expression, "step") 1836 step_sql = f" STEP {step_sql}" if step_sql else "" 1837 return f"WITH FILL{from_sql}{to_sql}{step_sql}" 1838 1839 def cluster_sql(self, expression: exp.Cluster) -> str: 1840 return self.op_expressions("CLUSTER BY", expression) 1841 1842 def distribute_sql(self, expression: exp.Distribute) -> str: 1843 return self.op_expressions("DISTRIBUTE BY", expression) 1844 1845 def sort_sql(self, expression: exp.Sort) -> str: 1846 return self.op_expressions("SORT BY", expression) 1847 1848 def ordered_sql(self, expression: exp.Ordered) -> str: 1849 desc = expression.args.get("desc") 1850 asc = not desc 1851 1852 nulls_first = expression.args.get("nulls_first") 1853 nulls_last = not nulls_first 1854 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 1855 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 1856 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 1857 1858 this = self.sql(expression, "this") 1859 1860 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 1861 nulls_sort_change = "" 1862 if nulls_first and ( 1863 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 1864 ): 1865 nulls_sort_change = " NULLS FIRST" 1866 elif ( 1867 nulls_last 1868 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 1869 and not nulls_are_last 1870 ): 1871 nulls_sort_change = " NULLS LAST" 1872 1873 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 1874 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 1875 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 1876 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 1877 nulls_sort_change = "" 1878 1879 with_fill = self.sql(expression, "with_fill") 1880 with_fill = f" {with_fill}" if with_fill else "" 1881 1882 return f"{this}{sort_order}{nulls_sort_change}{with_fill}" 1883 1884 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 1885 partition = self.partition_by_sql(expression) 1886 order = self.sql(expression, "order") 1887 measures = self.expressions(expression, key="measures") 1888 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 1889 rows = self.sql(expression, "rows") 1890 rows = self.seg(rows) if rows else "" 1891 after = self.sql(expression, "after") 1892 after = self.seg(after) if after else "" 1893 pattern = self.sql(expression, "pattern") 1894 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 1895 definition_sqls = [ 1896 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 1897 for definition in expression.args.get("define", []) 1898 ] 1899 definitions = self.expressions(sqls=definition_sqls) 1900 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 1901 body = "".join( 1902 ( 1903 partition, 1904 order, 1905 measures, 1906 rows, 1907 after, 1908 pattern, 1909 define, 1910 ) 1911 ) 1912 alias = self.sql(expression, "alias") 1913 alias = f" {alias}" if alias else "" 1914 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}" 1915 1916 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 1917 limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit") 1918 1919 # If the limit is generated as TOP, we need to ensure it's not generated twice 1920 with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP 1921 1922 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 1923 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 1924 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 1925 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 1926 1927 fetch = isinstance(limit, exp.Fetch) 1928 1929 offset_limit_modifiers = ( 1930 self.offset_limit_modifiers(expression, fetch, limit) 1931 if with_offset_limit_modifiers 1932 else [] 1933 ) 1934 1935 return csv( 1936 *sqls, 1937 *[self.sql(join) for join in expression.args.get("joins") or []], 1938 self.sql(expression, "connect"), 1939 self.sql(expression, "match"), 1940 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 1941 self.sql(expression, "where"), 1942 self.sql(expression, "group"), 1943 self.sql(expression, "having"), 1944 *self.after_having_modifiers(expression), 1945 self.sql(expression, "order"), 1946 *offset_limit_modifiers, 1947 *self.after_limit_modifiers(expression), 1948 sep="", 1949 ) 1950 1951 def offset_limit_modifiers( 1952 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 1953 ) -> t.List[str]: 1954 return [ 1955 self.sql(expression, "offset") if fetch else self.sql(limit), 1956 self.sql(limit) if fetch else self.sql(expression, "offset"), 1957 ] 1958 1959 def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]: 1960 return [ 1961 self.sql(expression, "qualify"), 1962 self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True) 1963 if expression.args.get("windows") 1964 else "", 1965 self.sql(expression, "distribute"), 1966 self.sql(expression, "sort"), 1967 self.sql(expression, "cluster"), 1968 ] 1969 1970 def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]: 1971 locks = self.expressions(expression, key="locks", sep=" ") 1972 locks = f" {locks}" if locks else "" 1973 return [locks, self.sql(expression, "sample")] 1974 1975 def select_sql(self, expression: exp.Select) -> str: 1976 hint = self.sql(expression, "hint") 1977 distinct = self.sql(expression, "distinct") 1978 distinct = f" {distinct}" if distinct else "" 1979 kind = self.sql(expression, "kind") 1980 limit = expression.args.get("limit") 1981 top = ( 1982 self.limit_sql(limit, top=True) 1983 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP 1984 else "" 1985 ) 1986 1987 expressions = self.expressions(expression) 1988 1989 if kind: 1990 if kind in self.SELECT_KINDS: 1991 kind = f" AS {kind}" 1992 else: 1993 if kind == "STRUCT": 1994 expressions = self.expressions( 1995 sqls=[ 1996 self.sql( 1997 exp.Struct( 1998 expressions=[ 1999 exp.column(e.output_name).eq( 2000 e.this if isinstance(e, exp.Alias) else e 2001 ) 2002 for e in expression.expressions 2003 ] 2004 ) 2005 ) 2006 ] 2007 ) 2008 kind = "" 2009 2010 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2011 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2012 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2013 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2014 sql = self.query_modifiers( 2015 expression, 2016 f"SELECT{top_distinct}{kind}{expressions}", 2017 self.sql(expression, "into", comment=False), 2018 self.sql(expression, "from", comment=False), 2019 ) 2020 return self.prepend_ctes(expression, sql) 2021 2022 def schema_sql(self, expression: exp.Schema) -> str: 2023 this = self.sql(expression, "this") 2024 sql = self.schema_columns_sql(expression) 2025 return f"{this} {sql}" if this and sql else this or sql 2026 2027 def schema_columns_sql(self, expression: exp.Schema) -> str: 2028 if expression.expressions: 2029 return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}" 2030 return "" 2031 2032 def star_sql(self, expression: exp.Star) -> str: 2033 except_ = self.expressions(expression, key="except", flat=True) 2034 except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else "" 2035 replace = self.expressions(expression, key="replace", flat=True) 2036 replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else "" 2037 return f"*{except_}{replace}" 2038 2039 def parameter_sql(self, expression: exp.Parameter) -> str: 2040 this = self.sql(expression, "this") 2041 return f"{self.PARAMETER_TOKEN}{this}" 2042 2043 def sessionparameter_sql(self, expression: exp.SessionParameter) -> str: 2044 this = self.sql(expression, "this") 2045 kind = expression.text("kind") 2046 if kind: 2047 kind = f"{kind}." 2048 return f"@@{kind}{this}" 2049 2050 def placeholder_sql(self, expression: exp.Placeholder) -> str: 2051 return f":{expression.name}" if expression.name else "?" 2052 2053 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2054 alias = self.sql(expression, "alias") 2055 alias = f"{sep}{alias}" if alias else "" 2056 2057 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2058 pivots = f" {pivots}" if pivots else "" 2059 2060 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2061 return self.prepend_ctes(expression, sql) 2062 2063 def qualify_sql(self, expression: exp.Qualify) -> str: 2064 this = self.indent(self.sql(expression, "this")) 2065 return f"{self.seg('QUALIFY')}{self.sep()}{this}" 2066 2067 def union_sql(self, expression: exp.Union) -> str: 2068 return self.prepend_ctes( 2069 expression, 2070 self.set_operation(expression, self.union_op(expression)), 2071 ) 2072 2073 def union_op(self, expression: exp.Union) -> str: 2074 kind = " DISTINCT" if self.EXPLICIT_UNION else "" 2075 kind = kind if expression.args.get("distinct") else " ALL" 2076 by_name = " BY NAME" if expression.args.get("by_name") else "" 2077 return f"UNION{kind}{by_name}" 2078 2079 def unnest_sql(self, expression: exp.Unnest) -> str: 2080 args = self.expressions(expression, flat=True) 2081 2082 alias = expression.args.get("alias") 2083 offset = expression.args.get("offset") 2084 2085 if self.UNNEST_WITH_ORDINALITY: 2086 if alias and isinstance(offset, exp.Expression): 2087 alias.append("columns", offset) 2088 2089 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2090 columns = alias.columns 2091 alias = self.sql(columns[0]) if columns else "" 2092 else: 2093 alias = self.sql(alias) 2094 2095 alias = f" AS {alias}" if alias else alias 2096 if self.UNNEST_WITH_ORDINALITY: 2097 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2098 else: 2099 if isinstance(offset, exp.Expression): 2100 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2101 elif offset: 2102 suffix = f"{alias} WITH OFFSET" 2103 else: 2104 suffix = alias 2105 2106 return f"UNNEST({args}){suffix}" 2107 2108 def where_sql(self, expression: exp.Where) -> str: 2109 this = self.indent(self.sql(expression, "this")) 2110 return f"{self.seg('WHERE')}{self.sep()}{this}" 2111 2112 def window_sql(self, expression: exp.Window) -> str: 2113 this = self.sql(expression, "this") 2114 partition = self.partition_by_sql(expression) 2115 order = expression.args.get("order") 2116 order = self.order_sql(order, flat=True) if order else "" 2117 spec = self.sql(expression, "spec") 2118 alias = self.sql(expression, "alias") 2119 over = self.sql(expression, "over") or "OVER" 2120 2121 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2122 2123 first = expression.args.get("first") 2124 if first is None: 2125 first = "" 2126 else: 2127 first = "FIRST" if first else "LAST" 2128 2129 if not partition and not order and not spec and alias: 2130 return f"{this} {alias}" 2131 2132 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2133 return f"{this} ({args})" 2134 2135 def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str: 2136 partition = self.expressions(expression, key="partition_by", flat=True) 2137 return f"PARTITION BY {partition}" if partition else "" 2138 2139 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2140 kind = self.sql(expression, "kind") 2141 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2142 end = ( 2143 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2144 or "CURRENT ROW" 2145 ) 2146 return f"{kind} BETWEEN {start} AND {end}" 2147 2148 def withingroup_sql(self, expression: exp.WithinGroup) -> str: 2149 this = self.sql(expression, "this") 2150 expression_sql = self.sql(expression, "expression")[1:] # order has a leading space 2151 return f"{this} WITHIN GROUP ({expression_sql})" 2152 2153 def between_sql(self, expression: exp.Between) -> str: 2154 this = self.sql(expression, "this") 2155 low = self.sql(expression, "low") 2156 high = self.sql(expression, "high") 2157 return f"{this} BETWEEN {low} AND {high}" 2158 2159 def bracket_sql(self, expression: exp.Bracket) -> str: 2160 expressions = apply_index_offset( 2161 expression.this, 2162 expression.expressions, 2163 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2164 ) 2165 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2166 return f"{self.sql(expression, 'this')}[{expressions_sql}]" 2167 2168 def all_sql(self, expression: exp.All) -> str: 2169 return f"ALL {self.wrap(expression)}" 2170 2171 def any_sql(self, expression: exp.Any) -> str: 2172 this = self.sql(expression, "this") 2173 if isinstance(expression.this, exp.Subqueryable): 2174 this = self.wrap(this) 2175 return f"ANY {this}" 2176 2177 def exists_sql(self, expression: exp.Exists) -> str: 2178 return f"EXISTS{self.wrap(expression)}" 2179 2180 def case_sql(self, expression: exp.Case) -> str: 2181 this = self.sql(expression, "this") 2182 statements = [f"CASE {this}" if this else "CASE"] 2183 2184 for e in expression.args["ifs"]: 2185 statements.append(f"WHEN {self.sql(e, 'this')}") 2186 statements.append(f"THEN {self.sql(e, 'true')}") 2187 2188 default = self.sql(expression, "default") 2189 2190 if default: 2191 statements.append(f"ELSE {default}") 2192 2193 statements.append("END") 2194 2195 if self.pretty and self.text_width(statements) > self.max_text_width: 2196 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2197 2198 return " ".join(statements) 2199 2200 def constraint_sql(self, expression: exp.Constraint) -> str: 2201 this = self.sql(expression, "this") 2202 expressions = self.expressions(expression, flat=True) 2203 return f"CONSTRAINT {this} {expressions}" 2204 2205 def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str: 2206 order = expression.args.get("order") 2207 order = f" OVER ({self.order_sql(order, flat=True)})" if order else "" 2208 return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}" 2209 2210 def extract_sql(self, expression: exp.Extract) -> str: 2211 this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name 2212 expression_sql = self.sql(expression, "expression") 2213 return f"EXTRACT({this} FROM {expression_sql})" 2214 2215 def trim_sql(self, expression: exp.Trim) -> str: 2216 trim_type = self.sql(expression, "position") 2217 2218 if trim_type == "LEADING": 2219 return self.func("LTRIM", expression.this) 2220 elif trim_type == "TRAILING": 2221 return self.func("RTRIM", expression.this) 2222 else: 2223 return self.func("TRIM", expression.this, expression.expression) 2224 2225 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2226 args = expression.expressions 2227 if isinstance(expression, exp.ConcatWs): 2228 args = args[1:] # Skip the delimiter 2229 2230 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2231 args = [exp.cast(e, "text") for e in args] 2232 2233 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2234 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2235 2236 return args 2237 2238 def concat_sql(self, expression: exp.Concat) -> str: 2239 expressions = self.convert_concat_args(expression) 2240 2241 # Some dialects don't allow a single-argument CONCAT call 2242 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2243 return self.sql(expressions[0]) 2244 2245 return self.func("CONCAT", *expressions) 2246 2247 def concatws_sql(self, expression: exp.ConcatWs) -> str: 2248 return self.func( 2249 "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression) 2250 ) 2251 2252 def check_sql(self, expression: exp.Check) -> str: 2253 this = self.sql(expression, key="this") 2254 return f"CHECK ({this})" 2255 2256 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2257 expressions = self.expressions(expression, flat=True) 2258 reference = self.sql(expression, "reference") 2259 reference = f" {reference}" if reference else "" 2260 delete = self.sql(expression, "delete") 2261 delete = f" ON DELETE {delete}" if delete else "" 2262 update = self.sql(expression, "update") 2263 update = f" ON UPDATE {update}" if update else "" 2264 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}" 2265 2266 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2267 expressions = self.expressions(expression, flat=True) 2268 options = self.expressions(expression, key="options", flat=True, sep=" ") 2269 options = f" {options}" if options else "" 2270 return f"PRIMARY KEY ({expressions}){options}" 2271 2272 def if_sql(self, expression: exp.If) -> str: 2273 return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false"))) 2274 2275 def matchagainst_sql(self, expression: exp.MatchAgainst) -> str: 2276 modifier = expression.args.get("modifier") 2277 modifier = f" {modifier}" if modifier else "" 2278 return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})" 2279 2280 def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str: 2281 return f"{self.sql(expression, 'this')}: {self.sql(expression, 'expression')}" 2282 2283 def formatjson_sql(self, expression: exp.FormatJson) -> str: 2284 return f"{self.sql(expression, 'this')} FORMAT JSON" 2285 2286 def jsonobject_sql(self, expression: exp.JSONObject) -> str: 2287 null_handling = expression.args.get("null_handling") 2288 null_handling = f" {null_handling}" if null_handling else "" 2289 unique_keys = expression.args.get("unique_keys") 2290 if unique_keys is not None: 2291 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2292 else: 2293 unique_keys = "" 2294 return_type = self.sql(expression, "return_type") 2295 return_type = f" RETURNING {return_type}" if return_type else "" 2296 encoding = self.sql(expression, "encoding") 2297 encoding = f" ENCODING {encoding}" if encoding else "" 2298 return self.func( 2299 "JSON_OBJECT", 2300 *expression.expressions, 2301 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2302 ) 2303 2304 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2305 null_handling = expression.args.get("null_handling") 2306 null_handling = f" {null_handling}" if null_handling else "" 2307 return_type = self.sql(expression, "return_type") 2308 return_type = f" RETURNING {return_type}" if return_type else "" 2309 strict = " STRICT" if expression.args.get("strict") else "" 2310 return self.func( 2311 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2312 ) 2313 2314 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2315 this = self.sql(expression, "this") 2316 order = self.sql(expression, "order") 2317 null_handling = expression.args.get("null_handling") 2318 null_handling = f" {null_handling}" if null_handling else "" 2319 return_type = self.sql(expression, "return_type") 2320 return_type = f" RETURNING {return_type}" if return_type else "" 2321 strict = " STRICT" if expression.args.get("strict") else "" 2322 return self.func( 2323 "JSON_ARRAYAGG", 2324 this, 2325 suffix=f"{order}{null_handling}{return_type}{strict})", 2326 ) 2327 2328 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2329 path = self.sql(expression, "path") 2330 path = f" PATH {path}" if path else "" 2331 nested_schema = self.sql(expression, "nested_schema") 2332 2333 if nested_schema: 2334 return f"NESTED{path} {nested_schema}" 2335 2336 this = self.sql(expression, "this") 2337 kind = self.sql(expression, "kind") 2338 kind = f" {kind}" if kind else "" 2339 return f"{this}{kind}{path}" 2340 2341 def jsonschema_sql(self, expression: exp.JSONSchema) -> str: 2342 return self.func("COLUMNS", *expression.expressions) 2343 2344 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2345 this = self.sql(expression, "this") 2346 path = self.sql(expression, "path") 2347 path = f", {path}" if path else "" 2348 error_handling = expression.args.get("error_handling") 2349 error_handling = f" {error_handling}" if error_handling else "" 2350 empty_handling = expression.args.get("empty_handling") 2351 empty_handling = f" {empty_handling}" if empty_handling else "" 2352 schema = self.sql(expression, "schema") 2353 return self.func( 2354 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2355 ) 2356 2357 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2358 this = self.sql(expression, "this") 2359 kind = self.sql(expression, "kind") 2360 path = self.sql(expression, "path") 2361 path = f" {path}" if path else "" 2362 as_json = " AS JSON" if expression.args.get("as_json") else "" 2363 return f"{this} {kind}{path}{as_json}" 2364 2365 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2366 this = self.sql(expression, "this") 2367 path = self.sql(expression, "path") 2368 path = f", {path}" if path else "" 2369 expressions = self.expressions(expression) 2370 with_ = ( 2371 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2372 if expressions 2373 else "" 2374 ) 2375 return f"OPENJSON({this}{path}){with_}" 2376 2377 def in_sql(self, expression: exp.In) -> str: 2378 query = expression.args.get("query") 2379 unnest = expression.args.get("unnest") 2380 field = expression.args.get("field") 2381 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2382 2383 if query: 2384 in_sql = self.wrap(query) 2385 elif unnest: 2386 in_sql = self.in_unnest_op(unnest) 2387 elif field: 2388 in_sql = self.sql(field) 2389 else: 2390 in_sql = f"({self.expressions(expression, flat=True)})" 2391 2392 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}" 2393 2394 def in_unnest_op(self, unnest: exp.Unnest) -> str: 2395 return f"(SELECT {self.sql(unnest)})" 2396 2397 def interval_sql(self, expression: exp.Interval) -> str: 2398 unit = self.sql(expression, "unit") 2399 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2400 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2401 unit = f" {unit}" if unit else "" 2402 2403 if self.SINGLE_STRING_INTERVAL: 2404 this = expression.this.name if expression.this else "" 2405 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2406 2407 this = self.sql(expression, "this") 2408 if this: 2409 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2410 this = f" {this}" if unwrapped else f" ({this})" 2411 2412 return f"INTERVAL{this}{unit}" 2413 2414 def return_sql(self, expression: exp.Return) -> str: 2415 return f"RETURN {self.sql(expression, 'this')}" 2416 2417 def reference_sql(self, expression: exp.Reference) -> str: 2418 this = self.sql(expression, "this") 2419 expressions = self.expressions(expression, flat=True) 2420 expressions = f"({expressions})" if expressions else "" 2421 options = self.expressions(expression, key="options", flat=True, sep=" ") 2422 options = f" {options}" if options else "" 2423 return f"REFERENCES {this}{expressions}{options}" 2424 2425 def anonymous_sql(self, expression: exp.Anonymous) -> str: 2426 return self.func(expression.name, *expression.expressions) 2427 2428 def paren_sql(self, expression: exp.Paren) -> str: 2429 if isinstance(expression.unnest(), exp.Select): 2430 sql = self.wrap(expression) 2431 else: 2432 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2433 sql = f"({sql}{self.seg(')', sep='')}" 2434 2435 return self.prepend_ctes(expression, sql) 2436 2437 def neg_sql(self, expression: exp.Neg) -> str: 2438 # This makes sure we don't convert "- - 5" to "--5", which is a comment 2439 this_sql = self.sql(expression, "this") 2440 sep = " " if this_sql[0] == "-" else "" 2441 return f"-{sep}{this_sql}" 2442 2443 def not_sql(self, expression: exp.Not) -> str: 2444 return f"NOT {self.sql(expression, 'this')}" 2445 2446 def alias_sql(self, expression: exp.Alias) -> str: 2447 alias = self.sql(expression, "alias") 2448 alias = f" AS {alias}" if alias else "" 2449 return f"{self.sql(expression, 'this')}{alias}" 2450 2451 def aliases_sql(self, expression: exp.Aliases) -> str: 2452 return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})" 2453 2454 def atindex_sql(self, expression: exp.AtTimeZone) -> str: 2455 this = self.sql(expression, "this") 2456 index = self.sql(expression, "expression") 2457 return f"{this} AT {index}" 2458 2459 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 2460 this = self.sql(expression, "this") 2461 zone = self.sql(expression, "zone") 2462 return f"{this} AT TIME ZONE {zone}" 2463 2464 def add_sql(self, expression: exp.Add) -> str: 2465 return self.binary(expression, "+") 2466 2467 def and_sql(self, expression: exp.And) -> str: 2468 return self.connector_sql(expression, "AND") 2469 2470 def xor_sql(self, expression: exp.Xor) -> str: 2471 return self.connector_sql(expression, "XOR") 2472 2473 def connector_sql(self, expression: exp.Connector, op: str) -> str: 2474 if not self.pretty: 2475 return self.binary(expression, op) 2476 2477 sqls = tuple( 2478 self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e) 2479 for i, e in enumerate(expression.flatten(unnest=False)) 2480 ) 2481 2482 sep = "\n" if self.text_width(sqls) > self.max_text_width else " " 2483 return f"{sep}{op} ".join(sqls) 2484 2485 def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str: 2486 return self.binary(expression, "&") 2487 2488 def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str: 2489 return self.binary(expression, "<<") 2490 2491 def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str: 2492 return f"~{self.sql(expression, 'this')}" 2493 2494 def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str: 2495 return self.binary(expression, "|") 2496 2497 def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str: 2498 return self.binary(expression, ">>") 2499 2500 def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str: 2501 return self.binary(expression, "^") 2502 2503 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2504 format_sql = self.sql(expression, "format") 2505 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2506 to_sql = self.sql(expression, "to") 2507 to_sql = f" {to_sql}" if to_sql else "" 2508 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})" 2509 2510 def currentdate_sql(self, expression: exp.CurrentDate) -> str: 2511 zone = self.sql(expression, "this") 2512 return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE" 2513 2514 def collate_sql(self, expression: exp.Collate) -> str: 2515 if self.COLLATE_IS_FUNC: 2516 return self.function_fallback_sql(expression) 2517 return self.binary(expression, "COLLATE") 2518 2519 def command_sql(self, expression: exp.Command) -> str: 2520 return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}" 2521 2522 def comment_sql(self, expression: exp.Comment) -> str: 2523 this = self.sql(expression, "this") 2524 kind = expression.args["kind"] 2525 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2526 expression_sql = self.sql(expression, "expression") 2527 return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}" 2528 2529 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2530 this = self.sql(expression, "this") 2531 delete = " DELETE" if expression.args.get("delete") else "" 2532 recompress = self.sql(expression, "recompress") 2533 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2534 to_disk = self.sql(expression, "to_disk") 2535 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2536 to_volume = self.sql(expression, "to_volume") 2537 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2538 return f"{this}{delete}{recompress}{to_disk}{to_volume}" 2539 2540 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2541 where = self.sql(expression, "where") 2542 group = self.sql(expression, "group") 2543 aggregates = self.expressions(expression, key="aggregates") 2544 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2545 2546 if not (where or group or aggregates) and len(expression.expressions) == 1: 2547 return f"TTL {self.expressions(expression, flat=True)}" 2548 2549 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}" 2550 2551 def transaction_sql(self, expression: exp.Transaction) -> str: 2552 return "BEGIN" 2553 2554 def commit_sql(self, expression: exp.Commit) -> str: 2555 chain = expression.args.get("chain") 2556 if chain is not None: 2557 chain = " AND CHAIN" if chain else " AND NO CHAIN" 2558 2559 return f"COMMIT{chain or ''}" 2560 2561 def rollback_sql(self, expression: exp.Rollback) -> str: 2562 savepoint = expression.args.get("savepoint") 2563 savepoint = f" TO {savepoint}" if savepoint else "" 2564 return f"ROLLBACK{savepoint}" 2565 2566 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2567 this = self.sql(expression, "this") 2568 2569 dtype = self.sql(expression, "dtype") 2570 if dtype: 2571 collate = self.sql(expression, "collate") 2572 collate = f" COLLATE {collate}" if collate else "" 2573 using = self.sql(expression, "using") 2574 using = f" USING {using}" if using else "" 2575 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2576 2577 default = self.sql(expression, "default") 2578 if default: 2579 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2580 2581 if not expression.args.get("drop"): 2582 self.unsupported("Unsupported ALTER COLUMN syntax") 2583 2584 return f"ALTER COLUMN {this} DROP DEFAULT" 2585 2586 def renametable_sql(self, expression: exp.RenameTable) -> str: 2587 if not self.RENAME_TABLE_WITH_DB: 2588 # Remove db from tables 2589 expression = expression.transform( 2590 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 2591 ) 2592 this = self.sql(expression, "this") 2593 return f"RENAME TO {this}" 2594 2595 def altertable_sql(self, expression: exp.AlterTable) -> str: 2596 actions = expression.args["actions"] 2597 2598 if isinstance(actions[0], exp.ColumnDef): 2599 actions = self.add_column_sql(expression) 2600 elif isinstance(actions[0], exp.Schema): 2601 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 2602 elif isinstance(actions[0], exp.Delete): 2603 actions = self.expressions(expression, key="actions", flat=True) 2604 else: 2605 actions = self.expressions(expression, key="actions", flat=True) 2606 2607 exists = " IF EXISTS" if expression.args.get("exists") else "" 2608 only = " ONLY" if expression.args.get("only") else "" 2609 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}" 2610 2611 def add_column_sql(self, expression: exp.AlterTable) -> str: 2612 if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD: 2613 return self.expressions( 2614 expression, 2615 key="actions", 2616 prefix="ADD COLUMN ", 2617 ) 2618 return f"ADD {self.expressions(expression, key='actions', flat=True)}" 2619 2620 def droppartition_sql(self, expression: exp.DropPartition) -> str: 2621 expressions = self.expressions(expression) 2622 exists = " IF EXISTS " if expression.args.get("exists") else " " 2623 return f"DROP{exists}{expressions}" 2624 2625 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 2626 this = self.sql(expression, "this") 2627 expression_ = self.sql(expression, "expression") 2628 add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD" 2629 2630 enforced = expression.args.get("enforced") 2631 if enforced is not None: 2632 return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}" 2633 2634 return f"{add_constraint} {expression_}" 2635 2636 def distinct_sql(self, expression: exp.Distinct) -> str: 2637 this = self.expressions(expression, flat=True) 2638 this = f" {this}" if this else "" 2639 2640 on = self.sql(expression, "on") 2641 on = f" ON {on}" if on else "" 2642 return f"DISTINCT{this}{on}" 2643 2644 def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: 2645 return f"{self.sql(expression, 'this')} IGNORE NULLS" 2646 2647 def respectnulls_sql(self, expression: exp.RespectNulls) -> str: 2648 return f"{self.sql(expression, 'this')} RESPECT NULLS" 2649 2650 def intdiv_sql(self, expression: exp.IntDiv) -> str: 2651 return self.sql( 2652 exp.Cast( 2653 this=exp.Div(this=expression.this, expression=expression.expression), 2654 to=exp.DataType(this=exp.DataType.Type.INT), 2655 ) 2656 ) 2657 2658 def dpipe_sql(self, expression: exp.DPipe) -> str: 2659 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2660 return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten())) 2661 return self.binary(expression, "||") 2662 2663 def div_sql(self, expression: exp.Div) -> str: 2664 l, r = expression.left, expression.right 2665 2666 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 2667 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 2668 2669 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 2670 if not l.is_type(*exp.DataType.FLOAT_TYPES) and not r.is_type( 2671 *exp.DataType.FLOAT_TYPES 2672 ): 2673 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 2674 2675 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 2676 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 2677 return self.sql( 2678 exp.cast( 2679 l / r, 2680 to=exp.DataType.Type.BIGINT, 2681 ) 2682 ) 2683 2684 return self.binary(expression, "/") 2685 2686 def overlaps_sql(self, expression: exp.Overlaps) -> str: 2687 return self.binary(expression, "OVERLAPS") 2688 2689 def distance_sql(self, expression: exp.Distance) -> str: 2690 return self.binary(expression, "<->") 2691 2692 def dot_sql(self, expression: exp.Dot) -> str: 2693 return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}" 2694 2695 def eq_sql(self, expression: exp.EQ) -> str: 2696 return self.binary(expression, "=") 2697 2698 def propertyeq_sql(self, expression: exp.PropertyEQ) -> str: 2699 return self.binary(expression, ":=") 2700 2701 def escape_sql(self, expression: exp.Escape) -> str: 2702 return self.binary(expression, "ESCAPE") 2703 2704 def glob_sql(self, expression: exp.Glob) -> str: 2705 return self.binary(expression, "GLOB") 2706 2707 def gt_sql(self, expression: exp.GT) -> str: 2708 return self.binary(expression, ">") 2709 2710 def gte_sql(self, expression: exp.GTE) -> str: 2711 return self.binary(expression, ">=") 2712 2713 def ilike_sql(self, expression: exp.ILike) -> str: 2714 return self.binary(expression, "ILIKE") 2715 2716 def ilikeany_sql(self, expression: exp.ILikeAny) -> str: 2717 return self.binary(expression, "ILIKE ANY") 2718 2719 def is_sql(self, expression: exp.Is) -> str: 2720 if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean): 2721 return self.sql( 2722 expression.this if expression.expression.this else exp.not_(expression.this) 2723 ) 2724 return self.binary(expression, "IS") 2725 2726 def like_sql(self, expression: exp.Like) -> str: 2727 return self.binary(expression, "LIKE") 2728 2729 def likeany_sql(self, expression: exp.LikeAny) -> str: 2730 return self.binary(expression, "LIKE ANY") 2731 2732 def similarto_sql(self, expression: exp.SimilarTo) -> str: 2733 return self.binary(expression, "SIMILAR TO") 2734 2735 def lt_sql(self, expression: exp.LT) -> str: 2736 return self.binary(expression, "<") 2737 2738 def lte_sql(self, expression: exp.LTE) -> str: 2739 return self.binary(expression, "<=") 2740 2741 def mod_sql(self, expression: exp.Mod) -> str: 2742 return self.binary(expression, "%") 2743 2744 def mul_sql(self, expression: exp.Mul) -> str: 2745 return self.binary(expression, "*") 2746 2747 def neq_sql(self, expression: exp.NEQ) -> str: 2748 return self.binary(expression, "<>") 2749 2750 def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str: 2751 return self.binary(expression, "IS NOT DISTINCT FROM") 2752 2753 def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str: 2754 return self.binary(expression, "IS DISTINCT FROM") 2755 2756 def or_sql(self, expression: exp.Or) -> str: 2757 return self.connector_sql(expression, "OR") 2758 2759 def slice_sql(self, expression: exp.Slice) -> str: 2760 return self.binary(expression, ":") 2761 2762 def sub_sql(self, expression: exp.Sub) -> str: 2763 return self.binary(expression, "-") 2764 2765 def trycast_sql(self, expression: exp.TryCast) -> str: 2766 return self.cast_sql(expression, safe_prefix="TRY_") 2767 2768 def log_sql(self, expression: exp.Log) -> str: 2769 this = expression.this 2770 expr = expression.expression 2771 2772 if not self.dialect.LOG_BASE_FIRST: 2773 this, expr = expr, this 2774 2775 return self.func("LOG", this, expr) 2776 2777 def use_sql(self, expression: exp.Use) -> str: 2778 kind = self.sql(expression, "kind") 2779 kind = f" {kind}" if kind else "" 2780 this = self.sql(expression, "this") 2781 this = f" {this}" if this else "" 2782 return f"USE{kind}{this}" 2783 2784 def binary(self, expression: exp.Binary, op: str) -> str: 2785 op = self.maybe_comment(op, comments=expression.comments) 2786 return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}" 2787 2788 def function_fallback_sql(self, expression: exp.Func) -> str: 2789 args = [] 2790 2791 for key in expression.arg_types: 2792 arg_value = expression.args.get(key) 2793 2794 if isinstance(arg_value, list): 2795 for value in arg_value: 2796 args.append(value) 2797 elif arg_value is not None: 2798 args.append(arg_value) 2799 2800 if self.normalize_functions: 2801 name = expression.sql_name() 2802 else: 2803 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 2804 2805 return self.func(name, *args) 2806 2807 def func( 2808 self, 2809 name: str, 2810 *args: t.Optional[exp.Expression | str], 2811 prefix: str = "(", 2812 suffix: str = ")", 2813 ) -> str: 2814 return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}" 2815 2816 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 2817 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 2818 if self.pretty and self.text_width(arg_sqls) > self.max_text_width: 2819 return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 2820 return ", ".join(arg_sqls) 2821 2822 def text_width(self, args: t.Iterable) -> int: 2823 return sum(len(arg) for arg in args) 2824 2825 def format_time(self, expression: exp.Expression) -> t.Optional[str]: 2826 return format_time( 2827 self.sql(expression, "format"), 2828 self.dialect.INVERSE_TIME_MAPPING, 2829 self.dialect.INVERSE_TIME_TRIE, 2830 ) 2831 2832 def expressions( 2833 self, 2834 expression: t.Optional[exp.Expression] = None, 2835 key: t.Optional[str] = None, 2836 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 2837 flat: bool = False, 2838 indent: bool = True, 2839 skip_first: bool = False, 2840 sep: str = ", ", 2841 prefix: str = "", 2842 ) -> str: 2843 expressions = expression.args.get(key or "expressions") if expression else sqls 2844 2845 if not expressions: 2846 return "" 2847 2848 if flat: 2849 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 2850 2851 num_sqls = len(expressions) 2852 2853 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 2854 pad = " " * self.pad 2855 stripped_sep = sep.strip() 2856 2857 result_sqls = [] 2858 for i, e in enumerate(expressions): 2859 sql = self.sql(e, comment=False) 2860 if not sql: 2861 continue 2862 2863 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 2864 2865 if self.pretty: 2866 if self.leading_comma: 2867 result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}") 2868 else: 2869 result_sqls.append( 2870 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 2871 ) 2872 else: 2873 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 2874 2875 result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls) 2876 return self.indent(result_sql, skip_first=skip_first) if indent else result_sql 2877 2878 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 2879 flat = flat or isinstance(expression.parent, exp.Properties) 2880 expressions_sql = self.expressions(expression, flat=flat) 2881 if flat: 2882 return f"{op} {expressions_sql}" 2883 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}" 2884 2885 def naked_property(self, expression: exp.Property) -> str: 2886 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 2887 if not property_name: 2888 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 2889 return f"{property_name} {self.sql(expression, 'this')}" 2890 2891 def set_operation(self, expression: exp.Union, op: str) -> str: 2892 this = self.maybe_comment(self.sql(expression, "this"), comments=expression.comments) 2893 op = self.seg(op) 2894 return self.query_modifiers( 2895 expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}" 2896 ) 2897 2898 def tag_sql(self, expression: exp.Tag) -> str: 2899 return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}" 2900 2901 def token_sql(self, token_type: TokenType) -> str: 2902 return self.TOKEN_MAPPING.get(token_type, token_type.name) 2903 2904 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 2905 this = self.sql(expression, "this") 2906 expressions = self.no_identify(self.expressions, expression) 2907 expressions = ( 2908 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 2909 ) 2910 return f"{this}{expressions}" 2911 2912 def joinhint_sql(self, expression: exp.JoinHint) -> str: 2913 this = self.sql(expression, "this") 2914 expressions = self.expressions(expression, flat=True) 2915 return f"{this}({expressions})" 2916 2917 def kwarg_sql(self, expression: exp.Kwarg) -> str: 2918 return self.binary(expression, "=>") 2919 2920 def when_sql(self, expression: exp.When) -> str: 2921 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 2922 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 2923 condition = self.sql(expression, "condition") 2924 condition = f" AND {condition}" if condition else "" 2925 2926 then_expression = expression.args.get("then") 2927 if isinstance(then_expression, exp.Insert): 2928 then = f"INSERT {self.sql(then_expression, 'this')}" 2929 if "expression" in then_expression.args: 2930 then += f" VALUES {self.sql(then_expression, 'expression')}" 2931 elif isinstance(then_expression, exp.Update): 2932 if isinstance(then_expression.args.get("expressions"), exp.Star): 2933 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 2934 else: 2935 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 2936 else: 2937 then = self.sql(then_expression) 2938 return f"WHEN {matched}{source}{condition} THEN {then}" 2939 2940 def merge_sql(self, expression: exp.Merge) -> str: 2941 table = expression.this 2942 table_alias = "" 2943 2944 hints = table.args.get("hints") 2945 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 2946 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 2947 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 2948 2949 this = self.sql(table) 2950 using = f"USING {self.sql(expression, 'using')}" 2951 on = f"ON {self.sql(expression, 'on')}" 2952 expressions = self.expressions(expression, sep=" ") 2953 2954 return self.prepend_ctes( 2955 expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}" 2956 ) 2957 2958 def tochar_sql(self, expression: exp.ToChar) -> str: 2959 if expression.args.get("format"): 2960 self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function") 2961 2962 return self.sql(exp.cast(expression.this, "text")) 2963 2964 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 2965 this = self.sql(expression, "this") 2966 kind = self.sql(expression, "kind") 2967 settings_sql = self.expressions(expression, key="settings", sep=" ") 2968 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 2969 return f"{this}({kind}{args})" 2970 2971 def dictrange_sql(self, expression: exp.DictRange) -> str: 2972 this = self.sql(expression, "this") 2973 max = self.sql(expression, "max") 2974 min = self.sql(expression, "min") 2975 return f"{this}(MIN {min} MAX {max})" 2976 2977 def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str: 2978 return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}" 2979 2980 def oncluster_sql(self, expression: exp.OnCluster) -> str: 2981 return "" 2982 2983 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 2984 expressions = self.expressions(expression, key="expressions", flat=True) 2985 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 2986 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 2987 buckets = self.sql(expression, "buckets") 2988 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS" 2989 2990 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 2991 this = self.sql(expression, "this") 2992 having = self.sql(expression, "having") 2993 2994 if having: 2995 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 2996 2997 return self.func("ANY_VALUE", this) 2998 2999 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3000 transform = self.func("TRANSFORM", *expression.expressions) 3001 row_format_before = self.sql(expression, "row_format_before") 3002 row_format_before = f" {row_format_before}" if row_format_before else "" 3003 record_writer = self.sql(expression, "record_writer") 3004 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3005 using = f" USING {self.sql(expression, 'command_script')}" 3006 schema = self.sql(expression, "schema") 3007 schema = f" AS {schema}" if schema else "" 3008 row_format_after = self.sql(expression, "row_format_after") 3009 row_format_after = f" {row_format_after}" if row_format_after else "" 3010 record_reader = self.sql(expression, "record_reader") 3011 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3012 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}" 3013 3014 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3015 key_block_size = self.sql(expression, "key_block_size") 3016 if key_block_size: 3017 return f"KEY_BLOCK_SIZE = {key_block_size}" 3018 3019 using = self.sql(expression, "using") 3020 if using: 3021 return f"USING {using}" 3022 3023 parser = self.sql(expression, "parser") 3024 if parser: 3025 return f"WITH PARSER {parser}" 3026 3027 comment = self.sql(expression, "comment") 3028 if comment: 3029 return f"COMMENT {comment}" 3030 3031 visible = expression.args.get("visible") 3032 if visible is not None: 3033 return "VISIBLE" if visible else "INVISIBLE" 3034 3035 engine_attr = self.sql(expression, "engine_attr") 3036 if engine_attr: 3037 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3038 3039 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3040 if secondary_engine_attr: 3041 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3042 3043 self.unsupported("Unsupported index constraint option.") 3044 return "" 3045 3046 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3047 kind = self.sql(expression, "kind") 3048 kind = f"{kind} INDEX" if kind else "INDEX" 3049 this = self.sql(expression, "this") 3050 this = f" {this}" if this else "" 3051 index_type = self.sql(expression, "index_type") 3052 index_type = f" USING {index_type}" if index_type else "" 3053 schema = self.sql(expression, "schema") 3054 schema = f" {schema}" if schema else "" 3055 options = self.expressions(expression, key="options", sep=" ") 3056 options = f" {options}" if options else "" 3057 return f"{kind}{this}{index_type}{schema}{options}" 3058 3059 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3060 if self.NVL2_SUPPORTED: 3061 return self.function_fallback_sql(expression) 3062 3063 case = exp.Case().when( 3064 expression.this.is_(exp.null()).not_(copy=False), 3065 expression.args["true"], 3066 copy=False, 3067 ) 3068 else_cond = expression.args.get("false") 3069 if else_cond: 3070 case.else_(else_cond, copy=False) 3071 3072 return self.sql(case) 3073 3074 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3075 this = self.sql(expression, "this") 3076 expr = self.sql(expression, "expression") 3077 iterator = self.sql(expression, "iterator") 3078 condition = self.sql(expression, "condition") 3079 condition = f" IF {condition}" if condition else "" 3080 return f"{this} FOR {expr} IN {iterator}{condition}" 3081 3082 def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str: 3083 return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})" 3084 3085 def opclass_sql(self, expression: exp.Opclass) -> str: 3086 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 3087 3088 def predict_sql(self, expression: exp.Predict) -> str: 3089 model = self.sql(expression, "this") 3090 model = f"MODEL {model}" 3091 table = self.sql(expression, "expression") 3092 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3093 parameters = self.sql(expression, "params_struct") 3094 return self.func("PREDICT", model, table, parameters or None) 3095 3096 def forin_sql(self, expression: exp.ForIn) -> str: 3097 this = self.sql(expression, "this") 3098 expression_sql = self.sql(expression, "expression") 3099 return f"FOR {this} DO {expression_sql}" 3100 3101 def refresh_sql(self, expression: exp.Refresh) -> str: 3102 this = self.sql(expression, "this") 3103 table = "" if isinstance(expression.this, exp.Literal) else "TABLE " 3104 return f"REFRESH {table}{this}" 3105 3106 def operator_sql(self, expression: exp.Operator) -> str: 3107 return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})") 3108 3109 def toarray_sql(self, expression: exp.ToArray) -> str: 3110 arg = expression.this 3111 if not arg.type: 3112 from sqlglot.optimizer.annotate_types import annotate_types 3113 3114 arg = annotate_types(arg) 3115 3116 if arg.is_type(exp.DataType.Type.ARRAY): 3117 return self.sql(arg) 3118 3119 cond_for_null = arg.is_(exp.null()) 3120 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.Array(expressions=[arg]))) 3121 3122 def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str: 3123 this = expression.this 3124 if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME): 3125 return self.sql(this) 3126 3127 return self.sql(exp.cast(this, "time")) 3128 3129 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3130 this = expression.this 3131 time_format = self.format_time(expression) 3132 3133 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3134 return self.sql( 3135 exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date") 3136 ) 3137 3138 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3139 return self.sql(this) 3140 3141 return self.sql(exp.cast(this, "date")) 3142 3143 def unixdate_sql(self, expression: exp.UnixDate) -> str: 3144 return self.sql( 3145 exp.func( 3146 "DATEDIFF", 3147 expression.this, 3148 exp.cast(exp.Literal.string("1970-01-01"), "date"), 3149 "day", 3150 ) 3151 ) 3152 3153 def lastday_sql(self, expression: exp.LastDay) -> str: 3154 if self.LAST_DAY_SUPPORTS_DATE_PART: 3155 return self.function_fallback_sql(expression) 3156 3157 unit = expression.text("unit") 3158 if unit and unit != "MONTH": 3159 self.unsupported("Date parts are not supported in LAST_DAY.") 3160 3161 return self.func("LAST_DAY", expression.this) 3162 3163 def _simplify_unless_literal(self, expression: E) -> E: 3164 if not isinstance(expression, exp.Literal): 3165 from sqlglot.optimizer.simplify import simplify 3166 3167 expression = simplify(expression, dialect=self.dialect) 3168 3169 return expression 3170 3171 def _ensure_string_if_null(self, values: t.List[exp.Expression]) -> t.List[exp.Expression]: 3172 return [ 3173 exp.func("COALESCE", exp.cast(value, "text"), exp.Literal.string("")) 3174 for value in values 3175 if value 3176 ]
Generator converts a given syntax tree to the corresponding SQL string.
Arguments:
- pretty: Whether or not 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 or not to normalize identifiers to lowercase. Default: False.
- pad: Determines the pad size in a formatted string. Default: 2.
- indent: Determines the indentation size in a formatted string. Default: 2.
- normalize_functions: Whether or not to normalize all 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: Determines whether or not 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 or not 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)
407 def __init__( 408 self, 409 pretty: t.Optional[bool] = None, 410 identify: str | bool = False, 411 normalize: bool = False, 412 pad: int = 2, 413 indent: int = 2, 414 normalize_functions: t.Optional[str | bool] = None, 415 unsupported_level: ErrorLevel = ErrorLevel.WARN, 416 max_unsupported: int = 3, 417 leading_comma: bool = False, 418 max_text_width: int = 80, 419 comments: bool = True, 420 dialect: DialectType = None, 421 ): 422 import sqlglot 423 from sqlglot.dialects import Dialect 424 425 self.pretty = pretty if pretty is not None else sqlglot.pretty 426 self.identify = identify 427 self.normalize = normalize 428 self.pad = pad 429 self._indent = indent 430 self.unsupported_level = unsupported_level 431 self.max_unsupported = max_unsupported 432 self.leading_comma = leading_comma 433 self.max_text_width = max_text_width 434 self.comments = comments 435 self.dialect = Dialect.get_or_raise(dialect) 436 437 # This is both a Dialect property and a Generator argument, so we prioritize the latter 438 self.normalize_functions = ( 439 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 440 ) 441 442 self.unsupported_messages: t.List[str] = [] 443 self._escaped_quote_end: str = ( 444 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 445 ) 446 self._escaped_identifier_end: str = ( 447 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 448 )
TRANSFORMS =
{<class 'sqlglot.expressions.DateAdd'>: <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.CheckColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CommentColumnConstraint'>: <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.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <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.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.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NonClusteredColumnConstraint'>: <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.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <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.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>}
TYPE_MAPPING =
{<Type.NCHAR: 'NCHAR'>: 'CHAR', <Type.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <Type.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <Type.LONGTEXT: 'LONGTEXT'>: 'TEXT', <Type.TINYTEXT: 'TINYTEXT'>: 'TEXT', <Type.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <Type.LONGBLOB: 'LONGBLOB'>: 'BLOB', <Type.TINYBLOB: 'TINYBLOB'>: 'BLOB', <Type.INET: 'INET'>: 'INET'}
TIME_PART_SINGULARS =
{'MICROSECONDS': 'MICROSECOND', 'SECONDS': 'SECOND', 'MINUTES': 'MINUTE', 'HOURS': 'HOUR', 'DAYS': 'DAY', 'WEEKS': 'WEEK', 'MONTHS': 'MONTH', 'QUARTERS': 'QUARTER', 'YEARS': 'YEAR'}
PROPERTIES_LOCATION =
{<class 'sqlglot.expressions.AlgorithmProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.AutoIncrementProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.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.HeapProperty'>: <Location.POST_WITH: 'POST_WITH'>, <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.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.SortKeyProperty'>: <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.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.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'>)
KEY_VALUE_DEFINITIONS =
(<class 'sqlglot.expressions.Bracket'>, <class 'sqlglot.expressions.EQ'>, <class 'sqlglot.expressions.PropertyEQ'>, <class 'sqlglot.expressions.Slice'>)
450 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 451 """ 452 Generates the SQL string corresponding to the given syntax tree. 453 454 Args: 455 expression: The syntax tree. 456 copy: Whether or not to copy the expression. The generator performs mutations so 457 it is safer to copy. 458 459 Returns: 460 The SQL string corresponding to `expression`. 461 """ 462 if copy: 463 expression = expression.copy() 464 465 expression = self.preprocess(expression) 466 467 self.unsupported_messages = [] 468 sql = self.sql(expression).strip() 469 470 if self.pretty: 471 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 472 473 if self.unsupported_level == ErrorLevel.IGNORE: 474 return sql 475 476 if self.unsupported_level == ErrorLevel.WARN: 477 for msg in self.unsupported_messages: 478 logger.warning(msg) 479 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 480 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 481 482 return sql
Generates the SQL string corresponding to the given syntax tree.
Arguments:
- expression: The syntax tree.
- copy: Whether or not 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:
484 def preprocess(self, expression: exp.Expression) -> exp.Expression: 485 """Apply generic preprocessing transformations to a given expression.""" 486 if ( 487 not expression.parent 488 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 489 and any(node.parent is not expression for node in expression.find_all(exp.With)) 490 ): 491 from sqlglot.transforms import move_ctes_to_top_level 492 493 expression = move_ctes_to_top_level(expression) 494 495 if self.ENSURE_BOOLS: 496 from sqlglot.transforms import ensure_bools 497 498 expression = ensure_bools(expression) 499 500 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) -> str:
518 def maybe_comment( 519 self, 520 sql: str, 521 expression: t.Optional[exp.Expression] = None, 522 comments: t.Optional[t.List[str]] = None, 523 ) -> str: 524 comments = ( 525 ((expression and expression.comments) if comments is None else comments) # type: ignore 526 if self.comments 527 else None 528 ) 529 530 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 531 return sql 532 533 comments_sql = " ".join( 534 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 535 ) 536 537 if not comments_sql: 538 return sql 539 540 if isinstance(expression, self.WITH_SEPARATED_COMMENTS): 541 return ( 542 f"{self.sep()}{comments_sql}{sql}" 543 if sql[0].isspace() 544 else f"{comments_sql}{self.sep()}{sql}" 545 ) 546 547 return f"{sql} {comments_sql}"
549 def wrap(self, expression: exp.Expression | str) -> str: 550 this_sql = self.indent( 551 self.sql(expression) 552 if isinstance(expression, (exp.Select, exp.Union)) 553 else self.sql(expression, "this"), 554 level=1, 555 pad=0, 556 ) 557 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:
573 def indent( 574 self, 575 sql: str, 576 level: int = 0, 577 pad: t.Optional[int] = None, 578 skip_first: bool = False, 579 skip_last: bool = False, 580 ) -> str: 581 if not self.pretty: 582 return sql 583 584 pad = self.pad if pad is None else pad 585 lines = sql.split("\n") 586 587 return "\n".join( 588 line 589 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 590 else f"{' ' * (level * self._indent + pad)}{line}" 591 for i, line in enumerate(lines) 592 )
def
sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str:
594 def sql( 595 self, 596 expression: t.Optional[str | exp.Expression], 597 key: t.Optional[str] = None, 598 comment: bool = True, 599 ) -> str: 600 if not expression: 601 return "" 602 603 if isinstance(expression, str): 604 return expression 605 606 if key: 607 value = expression.args.get(key) 608 if value: 609 return self.sql(value) 610 return "" 611 612 transform = self.TRANSFORMS.get(expression.__class__) 613 614 if callable(transform): 615 sql = transform(self, expression) 616 elif transform: 617 sql = transform 618 elif isinstance(expression, exp.Expression): 619 exp_handler_name = f"{expression.key}_sql" 620 621 if hasattr(self, exp_handler_name): 622 sql = getattr(self, exp_handler_name)(expression) 623 elif isinstance(expression, exp.Func): 624 sql = self.function_fallback_sql(expression) 625 elif isinstance(expression, exp.Property): 626 sql = self.property_sql(expression) 627 else: 628 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 629 else: 630 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 631 632 return self.maybe_comment(sql, expression) if self.comments and comment else sql
639 def cache_sql(self, expression: exp.Cache) -> str: 640 lazy = " LAZY" if expression.args.get("lazy") else "" 641 table = self.sql(expression, "this") 642 options = expression.args.get("options") 643 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 644 sql = self.sql(expression, "expression") 645 sql = f" AS{self.sep()}{sql}" if sql else "" 646 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 647 return self.prepend_ctes(expression, sql)
649 def characterset_sql(self, expression: exp.CharacterSet) -> str: 650 if isinstance(expression.parent, exp.Cast): 651 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 652 default = "DEFAULT " if expression.args.get("default") else "" 653 return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
655 def column_sql(self, expression: exp.Column) -> str: 656 join_mark = " (+)" if expression.args.get("join_mark") else "" 657 658 if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED: 659 join_mark = "" 660 self.unsupported("Outer join syntax using the (+) operator is not supported.") 661 662 column = ".".join( 663 self.sql(part) 664 for part in ( 665 expression.args.get("catalog"), 666 expression.args.get("db"), 667 expression.args.get("table"), 668 expression.args.get("this"), 669 ) 670 if part 671 ) 672 673 return f"{column}{join_mark}"
681 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 682 column = self.sql(expression, "this") 683 kind = self.sql(expression, "kind") 684 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 685 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 686 kind = f"{sep}{kind}" if kind else "" 687 constraints = f" {constraints}" if constraints else "" 688 position = self.sql(expression, "position") 689 position = f" {position}" if position else "" 690 691 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 692 kind = "" 693 694 return f"{exists}{column}{kind}{constraints}{position}"
def
computedcolumnconstraint_sql(self, expression: sqlglot.expressions.ComputedColumnConstraint) -> str:
701 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 702 this = self.sql(expression, "this") 703 if expression.args.get("not_null"): 704 persisted = " PERSISTED NOT NULL" 705 elif expression.args.get("persisted"): 706 persisted = " PERSISTED" 707 else: 708 persisted = "" 709 return f"AS {this}{persisted}"
def
compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str:
def
generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str:
722 def generatedasidentitycolumnconstraint_sql( 723 self, expression: exp.GeneratedAsIdentityColumnConstraint 724 ) -> str: 725 this = "" 726 if expression.this is not None: 727 on_null = " ON NULL" if expression.args.get("on_null") else "" 728 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 729 730 start = expression.args.get("start") 731 start = f"START WITH {start}" if start else "" 732 increment = expression.args.get("increment") 733 increment = f" INCREMENT BY {increment}" if increment else "" 734 minvalue = expression.args.get("minvalue") 735 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 736 maxvalue = expression.args.get("maxvalue") 737 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 738 cycle = expression.args.get("cycle") 739 cycle_sql = "" 740 741 if cycle is not None: 742 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 743 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 744 745 sequence_opts = "" 746 if start or increment or cycle_sql: 747 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 748 sequence_opts = f" ({sequence_opts.strip()})" 749 750 expr = self.sql(expression, "expression") 751 expr = f"({expr})" if expr else "IDENTITY" 752 753 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:
779 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 780 this = self.sql(expression, "this") 781 this = f" {this}" if this else "" 782 index_type = expression.args.get("index_type") 783 index_type = f" USING {index_type}" if index_type else "" 784 return f"UNIQUE{this}{index_type}"
789 def create_sql(self, expression: exp.Create) -> str: 790 kind = self.sql(expression, "kind") 791 properties = expression.args.get("properties") 792 properties_locs = self.locate_properties(properties) if properties else defaultdict() 793 794 this = self.createable_sql(expression, properties_locs) 795 796 properties_sql = "" 797 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 798 exp.Properties.Location.POST_WITH 799 ): 800 properties_sql = self.sql( 801 exp.Properties( 802 expressions=[ 803 *properties_locs[exp.Properties.Location.POST_SCHEMA], 804 *properties_locs[exp.Properties.Location.POST_WITH], 805 ] 806 ) 807 ) 808 809 begin = " BEGIN" if expression.args.get("begin") else "" 810 end = " END" if expression.args.get("end") else "" 811 812 expression_sql = self.sql(expression, "expression") 813 if expression_sql: 814 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 815 816 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 817 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 818 postalias_props_sql = self.properties( 819 exp.Properties( 820 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 821 ), 822 wrapped=False, 823 ) 824 expression_sql = f" AS {postalias_props_sql}{expression_sql}" 825 else: 826 expression_sql = f" AS{expression_sql}" 827 828 postindex_props_sql = "" 829 if properties_locs.get(exp.Properties.Location.POST_INDEX): 830 postindex_props_sql = self.properties( 831 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 832 wrapped=False, 833 prefix=" ", 834 ) 835 836 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 837 indexes = f" {indexes}" if indexes else "" 838 index_sql = indexes + postindex_props_sql 839 840 replace = " OR REPLACE" if expression.args.get("replace") else "" 841 unique = " UNIQUE" if expression.args.get("unique") else "" 842 843 postcreate_props_sql = "" 844 if properties_locs.get(exp.Properties.Location.POST_CREATE): 845 postcreate_props_sql = self.properties( 846 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 847 sep=" ", 848 prefix=" ", 849 wrapped=False, 850 ) 851 852 modifiers = "".join((replace, unique, postcreate_props_sql)) 853 854 postexpression_props_sql = "" 855 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 856 postexpression_props_sql = self.properties( 857 exp.Properties( 858 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 859 ), 860 sep=" ", 861 prefix=" ", 862 wrapped=False, 863 ) 864 865 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 866 no_schema_binding = ( 867 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 868 ) 869 870 clone = self.sql(expression, "clone") 871 clone = f" {clone}" if clone else "" 872 873 expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 874 return self.prepend_ctes(expression, expression_sql)
905 def tablealias_sql(self, expression: exp.TableAlias) -> str: 906 alias = self.sql(expression, "this") 907 columns = self.expressions(expression, key="columns", flat=True) 908 columns = f"({columns})" if columns else "" 909 910 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 911 alias = "_t" 912 913 return f"{alias}{columns}"
933 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 934 this = self.sql(expression, "this") 935 escape = expression.args.get("escape") 936 937 if self.dialect.UNICODE_START: 938 escape = f" UESCAPE {self.sql(escape)}" if escape else "" 939 return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}" 940 941 if escape: 942 pattern = re.compile(rf"{escape.name}(\d+)") 943 else: 944 pattern = ESCAPED_UNICODE_RE 945 946 this = pattern.sub(r"\\u\1", this) 947 return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}"
959 def datatype_sql(self, expression: exp.DataType) -> str: 960 type_value = expression.this 961 962 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 963 type_sql = self.sql(expression, "kind") 964 else: 965 type_sql = ( 966 self.TYPE_MAPPING.get(type_value, type_value.value) 967 if isinstance(type_value, exp.DataType.Type) 968 else type_value 969 ) 970 971 nested = "" 972 interior = self.expressions(expression, flat=True) 973 values = "" 974 975 if interior: 976 if expression.args.get("nested"): 977 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 978 if expression.args.get("values") is not None: 979 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 980 values = self.expressions(expression, key="values", flat=True) 981 values = f"{delimiters[0]}{values}{delimiters[1]}" 982 elif type_value == exp.DataType.Type.INTERVAL: 983 nested = f" {interior}" 984 else: 985 nested = f"({interior})" 986 987 type_sql = f"{type_sql}{nested}{values}" 988 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 989 exp.DataType.Type.TIMETZ, 990 exp.DataType.Type.TIMESTAMPTZ, 991 ): 992 type_sql = f"{type_sql} WITH TIME ZONE" 993 994 return type_sql
996 def directory_sql(self, expression: exp.Directory) -> str: 997 local = "LOCAL " if expression.args.get("local") else "" 998 row_format = self.sql(expression, "row_format") 999 row_format = f" {row_format}" if row_format else "" 1000 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
1002 def delete_sql(self, expression: exp.Delete) -> str: 1003 this = self.sql(expression, "this") 1004 this = f" FROM {this}" if this else "" 1005 using = self.sql(expression, "using") 1006 using = f" USING {using}" if using else "" 1007 where = self.sql(expression, "where") 1008 returning = self.sql(expression, "returning") 1009 limit = self.sql(expression, "limit") 1010 tables = self.expressions(expression, key="tables") 1011 tables = f" {tables}" if tables else "" 1012 if self.RETURNING_END: 1013 expression_sql = f"{this}{using}{where}{returning}{limit}" 1014 else: 1015 expression_sql = f"{returning}{this}{using}{where}{limit}" 1016 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
1018 def drop_sql(self, expression: exp.Drop) -> str: 1019 this = self.sql(expression, "this") 1020 kind = expression.args["kind"] 1021 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1022 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1023 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1024 cascade = " CASCADE" if expression.args.get("cascade") else "" 1025 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1026 purge = " PURGE" if expression.args.get("purge") else "" 1027 return ( 1028 f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{cascade}{constraints}{purge}" 1029 )
1040 def fetch_sql(self, expression: exp.Fetch) -> str: 1041 direction = expression.args.get("direction") 1042 direction = f" {direction}" if direction else "" 1043 count = expression.args.get("count") 1044 count = f" {count}" if count else "" 1045 if expression.args.get("percent"): 1046 count = f"{count} PERCENT" 1047 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1048 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
1050 def filter_sql(self, expression: exp.Filter) -> str: 1051 if self.AGGREGATE_FILTER_SUPPORTED: 1052 this = self.sql(expression, "this") 1053 where = self.sql(expression, "expression").strip() 1054 return f"{this} FILTER({where})" 1055 1056 agg = expression.this 1057 agg_arg = agg.this 1058 cond = expression.expression.this 1059 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1060 return self.sql(agg)
1069 def index_sql(self, expression: exp.Index) -> str: 1070 unique = "UNIQUE " if expression.args.get("unique") else "" 1071 primary = "PRIMARY " if expression.args.get("primary") else "" 1072 amp = "AMP " if expression.args.get("amp") else "" 1073 name = self.sql(expression, "this") 1074 name = f"{name} " if name else "" 1075 table = self.sql(expression, "table") 1076 table = f"{self.INDEX_ON} {table}" if table else "" 1077 using = self.sql(expression, "using") 1078 using = f" USING {using}" if using else "" 1079 index = "INDEX " if not table else "" 1080 columns = self.expressions(expression, key="columns", flat=True) 1081 columns = f"({columns})" if columns else "" 1082 partition_by = self.expressions(expression, key="partition_by", flat=True) 1083 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1084 where = self.sql(expression, "where") 1085 return f"{unique}{primary}{amp}{index}{name}{table}{using}{columns}{partition_by}{where}"
1087 def identifier_sql(self, expression: exp.Identifier) -> str: 1088 text = expression.name 1089 lower = text.lower() 1090 text = lower if self.normalize and not expression.quoted else text 1091 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1092 if ( 1093 expression.quoted 1094 or self.dialect.can_identify(text, self.identify) 1095 or lower in self.RESERVED_KEYWORDS 1096 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1097 ): 1098 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1099 return text
1101 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1102 input_format = self.sql(expression, "input_format") 1103 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1104 output_format = self.sql(expression, "output_format") 1105 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1106 return self.sep().join((input_format, output_format))
1115 def properties_sql(self, expression: exp.Properties) -> str: 1116 root_properties = [] 1117 with_properties = [] 1118 1119 for p in expression.expressions: 1120 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1121 if p_loc == exp.Properties.Location.POST_WITH: 1122 with_properties.append(p) 1123 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1124 root_properties.append(p) 1125 1126 return self.root_properties( 1127 exp.Properties(expressions=root_properties) 1128 ) + 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:
1135 def properties( 1136 self, 1137 properties: exp.Properties, 1138 prefix: str = "", 1139 sep: str = ", ", 1140 suffix: str = "", 1141 wrapped: bool = True, 1142 ) -> str: 1143 if properties.expressions: 1144 expressions = self.expressions(properties, sep=sep, indent=False) 1145 if expressions: 1146 expressions = self.wrap(expressions) if wrapped else expressions 1147 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1148 return ""
1153 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1154 properties_locs = defaultdict(list) 1155 for p in properties.expressions: 1156 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1157 if p_loc != exp.Properties.Location.UNSUPPORTED: 1158 properties_locs[p_loc].append(p) 1159 else: 1160 self.unsupported(f"Unsupported property {p.key}") 1161 1162 return properties_locs
def
property_name( self, expression: sqlglot.expressions.Property, string_key: bool = False) -> str:
1169 def property_sql(self, expression: exp.Property) -> str: 1170 property_cls = expression.__class__ 1171 if property_cls == exp.Property: 1172 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1173 1174 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1175 if not property_name: 1176 self.unsupported(f"Unsupported property {expression.key}") 1177 1178 return f"{property_name}={self.sql(expression, 'this')}"
1190 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1191 no = "NO " if expression.args.get("no") else "" 1192 local = expression.args.get("local") 1193 local = f"{local} " if local else "" 1194 dual = "DUAL " if expression.args.get("dual") else "" 1195 before = "BEFORE " if expression.args.get("before") else "" 1196 after = "AFTER " if expression.args.get("after") else "" 1197 return f"{no}{local}{dual}{before}{after}JOURNAL"
def
mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str:
1213 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1214 if expression.args.get("no"): 1215 return "NO MERGEBLOCKRATIO" 1216 if expression.args.get("default"): 1217 return "DEFAULT MERGEBLOCKRATIO" 1218 1219 percent = " PERCENT" if expression.args.get("percent") else "" 1220 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
1222 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1223 default = expression.args.get("default") 1224 minimum = expression.args.get("minimum") 1225 maximum = expression.args.get("maximum") 1226 if default or minimum or maximum: 1227 if default: 1228 prop = "DEFAULT" 1229 elif minimum: 1230 prop = "MINIMUM" 1231 else: 1232 prop = "MAXIMUM" 1233 return f"{prop} DATABLOCKSIZE" 1234 units = expression.args.get("units") 1235 units = f" {units}" if units else "" 1236 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
def
blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str:
1238 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1239 autotemp = expression.args.get("autotemp") 1240 always = expression.args.get("always") 1241 default = expression.args.get("default") 1242 manual = expression.args.get("manual") 1243 never = expression.args.get("never") 1244 1245 if autotemp is not None: 1246 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1247 elif always: 1248 prop = "ALWAYS" 1249 elif default: 1250 prop = "DEFAULT" 1251 elif manual: 1252 prop = "MANUAL" 1253 elif never: 1254 prop = "NEVER" 1255 return f"BLOCKCOMPRESSION={prop}"
def
isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str:
1257 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1258 no = expression.args.get("no") 1259 no = " NO" if no else "" 1260 concurrent = expression.args.get("concurrent") 1261 concurrent = " CONCURRENT" if concurrent else "" 1262 1263 for_ = "" 1264 if expression.args.get("for_all"): 1265 for_ = " FOR ALL" 1266 elif expression.args.get("for_insert"): 1267 for_ = " FOR INSERT" 1268 elif expression.args.get("for_none"): 1269 for_ = " FOR NONE" 1270 return f"WITH{no}{concurrent} ISOLATED LOADING{for_}"
1272 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1273 if isinstance(expression.this, list): 1274 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1275 if expression.this: 1276 modulus = self.sql(expression, "this") 1277 remainder = self.sql(expression, "expression") 1278 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1279 1280 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1281 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1282 return f"FROM ({from_expressions}) TO ({to_expressions})"
1284 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1285 this = self.sql(expression, "this") 1286 1287 for_values_or_default = expression.expression 1288 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1289 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1290 else: 1291 for_values_or_default = " DEFAULT" 1292 1293 return f"PARTITION OF {this}{for_values_or_default}"
1295 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1296 kind = expression.args.get("kind") 1297 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1298 for_or_in = expression.args.get("for_or_in") 1299 for_or_in = f" {for_or_in}" if for_or_in else "" 1300 lock_type = expression.args.get("lock_type") 1301 override = " OVERRIDE" if expression.args.get("override") else "" 1302 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
1304 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1305 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1306 statistics = expression.args.get("statistics") 1307 statistics_sql = "" 1308 if statistics is not None: 1309 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1310 return f"{data_sql}{statistics_sql}"
def
withsystemversioningproperty_sql( self, expression: sqlglot.expressions.WithSystemVersioningProperty) -> str:
1312 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1313 sql = "WITH(SYSTEM_VERSIONING=ON" 1314 1315 if expression.this: 1316 history_table = self.sql(expression, "this") 1317 sql = f"{sql}(HISTORY_TABLE={history_table}" 1318 1319 if expression.expression: 1320 data_consistency_check = self.sql(expression, "expression") 1321 sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}" 1322 1323 sql = f"{sql})" 1324 1325 return f"{sql})"
1327 def insert_sql(self, expression: exp.Insert) -> str: 1328 overwrite = expression.args.get("overwrite") 1329 1330 if isinstance(expression.this, exp.Directory): 1331 this = " OVERWRITE" if overwrite else " INTO" 1332 else: 1333 this = " OVERWRITE TABLE" if overwrite else " INTO" 1334 1335 alternative = expression.args.get("alternative") 1336 alternative = f" OR {alternative}" if alternative else "" 1337 ignore = " IGNORE" if expression.args.get("ignore") else "" 1338 1339 this = f"{this} {self.sql(expression, 'this')}" 1340 1341 exists = " IF EXISTS" if expression.args.get("exists") else "" 1342 partition_sql = ( 1343 f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else "" 1344 ) 1345 where = self.sql(expression, "where") 1346 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1347 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1348 conflict = self.sql(expression, "conflict") 1349 by_name = " BY NAME" if expression.args.get("by_name") else "" 1350 returning = self.sql(expression, "returning") 1351 1352 if self.RETURNING_END: 1353 expression_sql = f"{expression_sql}{conflict}{returning}" 1354 else: 1355 expression_sql = f"{returning}{expression_sql}{conflict}" 1356 1357 sql = f"INSERT{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}" 1358 return self.prepend_ctes(expression, sql)
1385 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1386 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1387 constraint = self.sql(expression, "constraint") 1388 if constraint: 1389 constraint = f"ON CONSTRAINT {constraint}" 1390 key = self.expressions(expression, key="key", flat=True) 1391 do = "" if expression.args.get("duplicate") else " DO " 1392 nothing = "NOTHING" if expression.args.get("nothing") else "" 1393 expressions = self.expressions(expression, flat=True) 1394 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1395 if expressions: 1396 expressions = f"UPDATE {set_keyword}{expressions}" 1397 return f"{self.seg(conflict)} {constraint}{key}{do}{nothing}{expressions}"
def
rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str:
1402 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1403 fields = expression.args.get("fields") 1404 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1405 escaped = expression.args.get("escaped") 1406 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1407 items = expression.args.get("collection_items") 1408 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1409 keys = expression.args.get("map_keys") 1410 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1411 lines = expression.args.get("lines") 1412 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1413 null = expression.args.get("null") 1414 null = f" NULL DEFINED AS {null}" if null else "" 1415 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
1432 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1433 table = ".".join( 1434 self.sql(part) 1435 for part in ( 1436 expression.args.get("catalog"), 1437 expression.args.get("db"), 1438 expression.args.get("this"), 1439 ) 1440 if part is not None 1441 ) 1442 1443 version = self.sql(expression, "version") 1444 version = f" {version}" if version else "" 1445 alias = self.sql(expression, "alias") 1446 alias = f"{sep}{alias}" if alias else "" 1447 hints = self.expressions(expression, key="hints", sep=" ") 1448 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1449 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 1450 pivots = f" {pivots}" if pivots else "" 1451 joins = self.expressions(expression, key="joins", sep="", skip_first=True) 1452 laterals = self.expressions(expression, key="laterals", sep="") 1453 1454 file_format = self.sql(expression, "format") 1455 if file_format: 1456 pattern = self.sql(expression, "pattern") 1457 pattern = f", PATTERN => {pattern}" if pattern else "" 1458 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1459 1460 ordinality = expression.args.get("ordinality") or "" 1461 if ordinality: 1462 ordinality = f" WITH ORDINALITY{alias}" 1463 alias = "" 1464 1465 when = self.sql(expression, "when") 1466 if when: 1467 table = f"{table} {when}" 1468 1469 return f"{table}{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:
1471 def tablesample_sql( 1472 self, 1473 expression: exp.TableSample, 1474 sep: str = " AS ", 1475 tablesample_keyword: t.Optional[str] = None, 1476 ) -> str: 1477 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1478 table = expression.this.copy() 1479 table.set("alias", None) 1480 this = self.sql(table) 1481 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1482 else: 1483 this = self.sql(expression, "this") 1484 alias = "" 1485 1486 method = self.sql(expression, "method") 1487 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1488 numerator = self.sql(expression, "bucket_numerator") 1489 denominator = self.sql(expression, "bucket_denominator") 1490 field = self.sql(expression, "bucket_field") 1491 field = f" ON {field}" if field else "" 1492 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1493 seed = self.sql(expression, "seed") 1494 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1495 1496 size = self.sql(expression, "size") 1497 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1498 size = f"{size} ROWS" 1499 1500 percent = self.sql(expression, "percent") 1501 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1502 percent = f"{percent} PERCENT" 1503 1504 expr = f"{bucket}{percent}{size}" 1505 if self.TABLESAMPLE_REQUIRES_PARENS: 1506 expr = f"({expr})" 1507 1508 return ( 1509 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1510 )
1512 def pivot_sql(self, expression: exp.Pivot) -> str: 1513 expressions = self.expressions(expression, flat=True) 1514 1515 if expression.this: 1516 this = self.sql(expression, "this") 1517 if not expressions: 1518 return f"UNPIVOT {this}" 1519 1520 on = f"{self.seg('ON')} {expressions}" 1521 using = self.expressions(expression, key="using", flat=True) 1522 using = f"{self.seg('USING')} {using}" if using else "" 1523 group = self.sql(expression, "group") 1524 return f"PIVOT {this}{on}{using}{group}" 1525 1526 alias = self.sql(expression, "alias") 1527 alias = f" AS {alias}" if alias else "" 1528 unpivot = expression.args.get("unpivot") 1529 direction = "UNPIVOT" if unpivot else "PIVOT" 1530 field = self.sql(expression, "field") 1531 include_nulls = expression.args.get("include_nulls") 1532 if include_nulls is not None: 1533 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1534 else: 1535 nulls = "" 1536 return f"{direction}{nulls}({expressions} FOR {field}){alias}"
1547 def update_sql(self, expression: exp.Update) -> str: 1548 this = self.sql(expression, "this") 1549 set_sql = self.expressions(expression, flat=True) 1550 from_sql = self.sql(expression, "from") 1551 where_sql = self.sql(expression, "where") 1552 returning = self.sql(expression, "returning") 1553 order = self.sql(expression, "order") 1554 limit = self.sql(expression, "limit") 1555 if self.RETURNING_END: 1556 expression_sql = f"{from_sql}{where_sql}{returning}" 1557 else: 1558 expression_sql = f"{returning}{from_sql}{where_sql}" 1559 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1560 return self.prepend_ctes(expression, sql)
1562 def values_sql(self, expression: exp.Values) -> str: 1563 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1564 if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join): 1565 args = self.expressions(expression) 1566 alias = self.sql(expression, "alias") 1567 values = f"VALUES{self.seg('')}{args}" 1568 values = ( 1569 f"({values})" 1570 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1571 else values 1572 ) 1573 return f"{values} AS {alias}" if alias else values 1574 1575 # Converts `VALUES...` expression into a series of select unions. 1576 alias_node = expression.args.get("alias") 1577 column_names = alias_node and alias_node.columns 1578 1579 selects: t.List[exp.Subqueryable] = [] 1580 1581 for i, tup in enumerate(expression.expressions): 1582 row = tup.expressions 1583 1584 if i == 0 and column_names: 1585 row = [ 1586 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1587 ] 1588 1589 selects.append(exp.Select(expressions=row)) 1590 1591 if self.pretty: 1592 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1593 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1594 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1595 subqueryable = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1596 return self.subquery_sql( 1597 subqueryable.subquery(alias_node and alias_node.this, copy=False) 1598 ) 1599 1600 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1601 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1602 return f"({unions}){alias}"
1615 def group_sql(self, expression: exp.Group) -> str: 1616 group_by = self.op_expressions("GROUP BY", expression) 1617 1618 if expression.args.get("all"): 1619 return f"{group_by} ALL" 1620 1621 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1622 grouping_sets = ( 1623 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1624 ) 1625 1626 cube = expression.args.get("cube", []) 1627 if seq_get(cube, 0) is True: 1628 return f"{group_by}{self.seg('WITH CUBE')}" 1629 else: 1630 cube_sql = self.expressions(expression, key="cube", indent=False) 1631 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1632 1633 rollup = expression.args.get("rollup", []) 1634 if seq_get(rollup, 0) is True: 1635 return f"{group_by}{self.seg('WITH ROLLUP')}" 1636 else: 1637 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1638 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1639 1640 groupings = csv( 1641 grouping_sets, 1642 cube_sql, 1643 rollup_sql, 1644 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1645 sep=self.GROUPINGS_SEP, 1646 ) 1647 1648 if expression.args.get("expressions") and groupings: 1649 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1650 1651 return f"{group_by}{groupings}"
1667 def join_sql(self, expression: exp.Join) -> str: 1668 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1669 side = None 1670 else: 1671 side = expression.side 1672 1673 op_sql = " ".join( 1674 op 1675 for op in ( 1676 expression.method, 1677 "GLOBAL" if expression.args.get("global") else None, 1678 side, 1679 expression.kind, 1680 expression.hint if self.JOIN_HINTS else None, 1681 ) 1682 if op 1683 ) 1684 on_sql = self.sql(expression, "on") 1685 using = expression.args.get("using") 1686 1687 if not on_sql and using: 1688 on_sql = csv(*(self.sql(column) for column in using)) 1689 1690 this_sql = self.sql(expression, "this") 1691 1692 if on_sql: 1693 on_sql = self.indent(on_sql, skip_first=True) 1694 space = self.seg(" " * self.pad) if self.pretty else " " 1695 if using: 1696 on_sql = f"{space}USING ({on_sql})" 1697 else: 1698 on_sql = f"{space}ON {on_sql}" 1699 elif not op_sql: 1700 return f", {this_sql}" 1701 1702 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1703 return f"{self.seg(op_sql)} {this_sql}{on_sql}"
1710 def lateral_sql(self, expression: exp.Lateral) -> str: 1711 this = self.sql(expression, "this") 1712 1713 if expression.args.get("view"): 1714 alias = expression.args["alias"] 1715 columns = self.expressions(alias, key="columns", flat=True) 1716 table = f" {alias.name}" if alias.name else "" 1717 columns = f" AS {columns}" if columns else "" 1718 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1719 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1720 1721 alias = self.sql(expression, "alias") 1722 alias = f" AS {alias}" if alias else "" 1723 return f"LATERAL {this}{alias}"
1725 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1726 this = self.sql(expression, "this") 1727 1728 args = [ 1729 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1730 for e in (expression.args.get(k) for k in ("offset", "expression")) 1731 if e 1732 ] 1733 1734 args_sql = ", ".join(self.sql(e) for e in args) 1735 args_sql = f"({args_sql})" if any(top and not e.is_number for e in args) else args_sql 1736 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}"
1738 def offset_sql(self, expression: exp.Offset) -> str: 1739 this = self.sql(expression, "this") 1740 expression = expression.expression 1741 expression = ( 1742 self._simplify_unless_literal(expression) if self.LIMIT_ONLY_LITERALS else expression 1743 ) 1744 return f"{this}{self.seg('OFFSET')} {self.sql(expression)}"
1746 def setitem_sql(self, expression: exp.SetItem) -> str: 1747 kind = self.sql(expression, "kind") 1748 kind = f"{kind} " if kind else "" 1749 this = self.sql(expression, "this") 1750 expressions = self.expressions(expression) 1751 collate = self.sql(expression, "collate") 1752 collate = f" COLLATE {collate}" if collate else "" 1753 global_ = "GLOBAL " if expression.args.get("global") else "" 1754 return f"{global_}{kind}{this}{expressions}{collate}"
1756 def set_sql(self, expression: exp.Set) -> str: 1757 expressions = ( 1758 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 1759 ) 1760 tag = " TAG" if expression.args.get("tag") else "" 1761 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
1766 def lock_sql(self, expression: exp.Lock) -> str: 1767 if not self.LOCKING_READS_SUPPORTED: 1768 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 1769 return "" 1770 1771 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 1772 expressions = self.expressions(expression, flat=True) 1773 expressions = f" OF {expressions}" if expressions else "" 1774 wait = expression.args.get("wait") 1775 1776 if wait is not None: 1777 if isinstance(wait, exp.Literal): 1778 wait = f" WAIT {self.sql(wait)}" 1779 else: 1780 wait = " NOWAIT" if wait else " SKIP LOCKED" 1781 1782 return f"{lock_type}{expressions}{wait or ''}"
def
escape_str(self, text: str) -> str:
1790 def escape_str(self, text: str) -> str: 1791 text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end) 1792 if self.dialect.INVERSE_ESCAPE_SEQUENCES: 1793 text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text) 1794 elif self.pretty: 1795 text = text.replace("\n", self.SENTINEL_LINE_BREAK) 1796 return text
1798 def loaddata_sql(self, expression: exp.LoadData) -> str: 1799 local = " LOCAL" if expression.args.get("local") else "" 1800 inpath = f" INPATH {self.sql(expression, 'inpath')}" 1801 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 1802 this = f" INTO TABLE {self.sql(expression, 'this')}" 1803 partition = self.sql(expression, "partition") 1804 partition = f" {partition}" if partition else "" 1805 input_format = self.sql(expression, "input_format") 1806 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 1807 serde = self.sql(expression, "serde") 1808 serde = f" SERDE {serde}" if serde else "" 1809 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
1817 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 1818 this = self.sql(expression, "this") 1819 this = f"{this} " if this else this 1820 order = self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat) # type: ignore 1821 interpolated_values = [ 1822 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 1823 for named_expression in expression.args.get("interpolate") or [] 1824 ] 1825 interpolate = ( 1826 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 1827 ) 1828 return f"{order}{interpolate}"
1830 def withfill_sql(self, expression: exp.WithFill) -> str: 1831 from_sql = self.sql(expression, "from") 1832 from_sql = f" FROM {from_sql}" if from_sql else "" 1833 to_sql = self.sql(expression, "to") 1834 to_sql = f" TO {to_sql}" if to_sql else "" 1835 step_sql = self.sql(expression, "step") 1836 step_sql = f" STEP {step_sql}" if step_sql else "" 1837 return f"WITH FILL{from_sql}{to_sql}{step_sql}"
1848 def ordered_sql(self, expression: exp.Ordered) -> str: 1849 desc = expression.args.get("desc") 1850 asc = not desc 1851 1852 nulls_first = expression.args.get("nulls_first") 1853 nulls_last = not nulls_first 1854 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 1855 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 1856 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 1857 1858 this = self.sql(expression, "this") 1859 1860 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 1861 nulls_sort_change = "" 1862 if nulls_first and ( 1863 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 1864 ): 1865 nulls_sort_change = " NULLS FIRST" 1866 elif ( 1867 nulls_last 1868 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 1869 and not nulls_are_last 1870 ): 1871 nulls_sort_change = " NULLS LAST" 1872 1873 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 1874 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 1875 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 1876 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 1877 nulls_sort_change = "" 1878 1879 with_fill = self.sql(expression, "with_fill") 1880 with_fill = f" {with_fill}" if with_fill else "" 1881 1882 return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
1884 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 1885 partition = self.partition_by_sql(expression) 1886 order = self.sql(expression, "order") 1887 measures = self.expressions(expression, key="measures") 1888 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 1889 rows = self.sql(expression, "rows") 1890 rows = self.seg(rows) if rows else "" 1891 after = self.sql(expression, "after") 1892 after = self.seg(after) if after else "" 1893 pattern = self.sql(expression, "pattern") 1894 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 1895 definition_sqls = [ 1896 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 1897 for definition in expression.args.get("define", []) 1898 ] 1899 definitions = self.expressions(sqls=definition_sqls) 1900 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 1901 body = "".join( 1902 ( 1903 partition, 1904 order, 1905 measures, 1906 rows, 1907 after, 1908 pattern, 1909 define, 1910 ) 1911 ) 1912 alias = self.sql(expression, "alias") 1913 alias = f" {alias}" if alias else "" 1914 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
1916 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 1917 limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit") 1918 1919 # If the limit is generated as TOP, we need to ensure it's not generated twice 1920 with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP 1921 1922 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 1923 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 1924 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 1925 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 1926 1927 fetch = isinstance(limit, exp.Fetch) 1928 1929 offset_limit_modifiers = ( 1930 self.offset_limit_modifiers(expression, fetch, limit) 1931 if with_offset_limit_modifiers 1932 else [] 1933 ) 1934 1935 return csv( 1936 *sqls, 1937 *[self.sql(join) for join in expression.args.get("joins") or []], 1938 self.sql(expression, "connect"), 1939 self.sql(expression, "match"), 1940 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 1941 self.sql(expression, "where"), 1942 self.sql(expression, "group"), 1943 self.sql(expression, "having"), 1944 *self.after_having_modifiers(expression), 1945 self.sql(expression, "order"), 1946 *offset_limit_modifiers, 1947 *self.after_limit_modifiers(expression), 1948 sep="", 1949 )
def
offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]:
1951 def offset_limit_modifiers( 1952 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 1953 ) -> t.List[str]: 1954 return [ 1955 self.sql(expression, "offset") if fetch else self.sql(limit), 1956 self.sql(limit) if fetch else self.sql(expression, "offset"), 1957 ]
1959 def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]: 1960 return [ 1961 self.sql(expression, "qualify"), 1962 self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True) 1963 if expression.args.get("windows") 1964 else "", 1965 self.sql(expression, "distribute"), 1966 self.sql(expression, "sort"), 1967 self.sql(expression, "cluster"), 1968 ]
1975 def select_sql(self, expression: exp.Select) -> str: 1976 hint = self.sql(expression, "hint") 1977 distinct = self.sql(expression, "distinct") 1978 distinct = f" {distinct}" if distinct else "" 1979 kind = self.sql(expression, "kind") 1980 limit = expression.args.get("limit") 1981 top = ( 1982 self.limit_sql(limit, top=True) 1983 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP 1984 else "" 1985 ) 1986 1987 expressions = self.expressions(expression) 1988 1989 if kind: 1990 if kind in self.SELECT_KINDS: 1991 kind = f" AS {kind}" 1992 else: 1993 if kind == "STRUCT": 1994 expressions = self.expressions( 1995 sqls=[ 1996 self.sql( 1997 exp.Struct( 1998 expressions=[ 1999 exp.column(e.output_name).eq( 2000 e.this if isinstance(e, exp.Alias) else e 2001 ) 2002 for e in expression.expressions 2003 ] 2004 ) 2005 ) 2006 ] 2007 ) 2008 kind = "" 2009 2010 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2011 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2012 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2013 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2014 sql = self.query_modifiers( 2015 expression, 2016 f"SELECT{top_distinct}{kind}{expressions}", 2017 self.sql(expression, "into", comment=False), 2018 self.sql(expression, "from", comment=False), 2019 ) 2020 return self.prepend_ctes(expression, sql)
2032 def star_sql(self, expression: exp.Star) -> str: 2033 except_ = self.expressions(expression, key="except", flat=True) 2034 except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else "" 2035 replace = self.expressions(expression, key="replace", flat=True) 2036 replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else "" 2037 return f"*{except_}{replace}"
2053 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2054 alias = self.sql(expression, "alias") 2055 alias = f"{sep}{alias}" if alias else "" 2056 2057 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2058 pivots = f" {pivots}" if pivots else "" 2059 2060 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2061 return self.prepend_ctes(expression, sql)
2079 def unnest_sql(self, expression: exp.Unnest) -> str: 2080 args = self.expressions(expression, flat=True) 2081 2082 alias = expression.args.get("alias") 2083 offset = expression.args.get("offset") 2084 2085 if self.UNNEST_WITH_ORDINALITY: 2086 if alias and isinstance(offset, exp.Expression): 2087 alias.append("columns", offset) 2088 2089 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2090 columns = alias.columns 2091 alias = self.sql(columns[0]) if columns else "" 2092 else: 2093 alias = self.sql(alias) 2094 2095 alias = f" AS {alias}" if alias else alias 2096 if self.UNNEST_WITH_ORDINALITY: 2097 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2098 else: 2099 if isinstance(offset, exp.Expression): 2100 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2101 elif offset: 2102 suffix = f"{alias} WITH OFFSET" 2103 else: 2104 suffix = alias 2105 2106 return f"UNNEST({args}){suffix}"
2112 def window_sql(self, expression: exp.Window) -> str: 2113 this = self.sql(expression, "this") 2114 partition = self.partition_by_sql(expression) 2115 order = expression.args.get("order") 2116 order = self.order_sql(order, flat=True) if order else "" 2117 spec = self.sql(expression, "spec") 2118 alias = self.sql(expression, "alias") 2119 over = self.sql(expression, "over") or "OVER" 2120 2121 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2122 2123 first = expression.args.get("first") 2124 if first is None: 2125 first = "" 2126 else: 2127 first = "FIRST" if first else "LAST" 2128 2129 if not partition and not order and not spec and alias: 2130 return f"{this} {alias}" 2131 2132 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2133 return f"{this} ({args})"
def
partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
2139 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2140 kind = self.sql(expression, "kind") 2141 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2142 end = ( 2143 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2144 or "CURRENT ROW" 2145 ) 2146 return f"{kind} BETWEEN {start} AND {end}"
2159 def bracket_sql(self, expression: exp.Bracket) -> str: 2160 expressions = apply_index_offset( 2161 expression.this, 2162 expression.expressions, 2163 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2164 ) 2165 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2166 return f"{self.sql(expression, 'this')}[{expressions_sql}]"
2180 def case_sql(self, expression: exp.Case) -> str: 2181 this = self.sql(expression, "this") 2182 statements = [f"CASE {this}" if this else "CASE"] 2183 2184 for e in expression.args["ifs"]: 2185 statements.append(f"WHEN {self.sql(e, 'this')}") 2186 statements.append(f"THEN {self.sql(e, 'true')}") 2187 2188 default = self.sql(expression, "default") 2189 2190 if default: 2191 statements.append(f"ELSE {default}") 2192 2193 statements.append("END") 2194 2195 if self.pretty and self.text_width(statements) > self.max_text_width: 2196 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2197 2198 return " ".join(statements)
2215 def trim_sql(self, expression: exp.Trim) -> str: 2216 trim_type = self.sql(expression, "position") 2217 2218 if trim_type == "LEADING": 2219 return self.func("LTRIM", expression.this) 2220 elif trim_type == "TRAILING": 2221 return self.func("RTRIM", expression.this) 2222 else: 2223 return self.func("TRIM", expression.this, expression.expression)
def
convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]:
2225 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2226 args = expression.expressions 2227 if isinstance(expression, exp.ConcatWs): 2228 args = args[1:] # Skip the delimiter 2229 2230 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2231 args = [exp.cast(e, "text") for e in args] 2232 2233 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2234 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2235 2236 return args
2238 def concat_sql(self, expression: exp.Concat) -> str: 2239 expressions = self.convert_concat_args(expression) 2240 2241 # Some dialects don't allow a single-argument CONCAT call 2242 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2243 return self.sql(expressions[0]) 2244 2245 return self.func("CONCAT", *expressions)
2256 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2257 expressions = self.expressions(expression, flat=True) 2258 reference = self.sql(expression, "reference") 2259 reference = f" {reference}" if reference else "" 2260 delete = self.sql(expression, "delete") 2261 delete = f" ON DELETE {delete}" if delete else "" 2262 update = self.sql(expression, "update") 2263 update = f" ON UPDATE {update}" if update else "" 2264 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
2266 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2267 expressions = self.expressions(expression, flat=True) 2268 options = self.expressions(expression, key="options", flat=True, sep=" ") 2269 options = f" {options}" if options else "" 2270 return f"PRIMARY KEY ({expressions}){options}"
2286 def jsonobject_sql(self, expression: exp.JSONObject) -> str: 2287 null_handling = expression.args.get("null_handling") 2288 null_handling = f" {null_handling}" if null_handling else "" 2289 unique_keys = expression.args.get("unique_keys") 2290 if unique_keys is not None: 2291 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2292 else: 2293 unique_keys = "" 2294 return_type = self.sql(expression, "return_type") 2295 return_type = f" RETURNING {return_type}" if return_type else "" 2296 encoding = self.sql(expression, "encoding") 2297 encoding = f" ENCODING {encoding}" if encoding else "" 2298 return self.func( 2299 "JSON_OBJECT", 2300 *expression.expressions, 2301 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2302 )
2304 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2305 null_handling = expression.args.get("null_handling") 2306 null_handling = f" {null_handling}" if null_handling else "" 2307 return_type = self.sql(expression, "return_type") 2308 return_type = f" RETURNING {return_type}" if return_type else "" 2309 strict = " STRICT" if expression.args.get("strict") else "" 2310 return self.func( 2311 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2312 )
2314 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2315 this = self.sql(expression, "this") 2316 order = self.sql(expression, "order") 2317 null_handling = expression.args.get("null_handling") 2318 null_handling = f" {null_handling}" if null_handling else "" 2319 return_type = self.sql(expression, "return_type") 2320 return_type = f" RETURNING {return_type}" if return_type else "" 2321 strict = " STRICT" if expression.args.get("strict") else "" 2322 return self.func( 2323 "JSON_ARRAYAGG", 2324 this, 2325 suffix=f"{order}{null_handling}{return_type}{strict})", 2326 )
2328 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2329 path = self.sql(expression, "path") 2330 path = f" PATH {path}" if path else "" 2331 nested_schema = self.sql(expression, "nested_schema") 2332 2333 if nested_schema: 2334 return f"NESTED{path} {nested_schema}" 2335 2336 this = self.sql(expression, "this") 2337 kind = self.sql(expression, "kind") 2338 kind = f" {kind}" if kind else "" 2339 return f"{this}{kind}{path}"
2344 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2345 this = self.sql(expression, "this") 2346 path = self.sql(expression, "path") 2347 path = f", {path}" if path else "" 2348 error_handling = expression.args.get("error_handling") 2349 error_handling = f" {error_handling}" if error_handling else "" 2350 empty_handling = expression.args.get("empty_handling") 2351 empty_handling = f" {empty_handling}" if empty_handling else "" 2352 schema = self.sql(expression, "schema") 2353 return self.func( 2354 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2355 )
2357 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2358 this = self.sql(expression, "this") 2359 kind = self.sql(expression, "kind") 2360 path = self.sql(expression, "path") 2361 path = f" {path}" if path else "" 2362 as_json = " AS JSON" if expression.args.get("as_json") else "" 2363 return f"{this} {kind}{path}{as_json}"
2365 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2366 this = self.sql(expression, "this") 2367 path = self.sql(expression, "path") 2368 path = f", {path}" if path else "" 2369 expressions = self.expressions(expression) 2370 with_ = ( 2371 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2372 if expressions 2373 else "" 2374 ) 2375 return f"OPENJSON({this}{path}){with_}"
2377 def in_sql(self, expression: exp.In) -> str: 2378 query = expression.args.get("query") 2379 unnest = expression.args.get("unnest") 2380 field = expression.args.get("field") 2381 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2382 2383 if query: 2384 in_sql = self.wrap(query) 2385 elif unnest: 2386 in_sql = self.in_unnest_op(unnest) 2387 elif field: 2388 in_sql = self.sql(field) 2389 else: 2390 in_sql = f"({self.expressions(expression, flat=True)})" 2391 2392 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
2397 def interval_sql(self, expression: exp.Interval) -> str: 2398 unit = self.sql(expression, "unit") 2399 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2400 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2401 unit = f" {unit}" if unit else "" 2402 2403 if self.SINGLE_STRING_INTERVAL: 2404 this = expression.this.name if expression.this else "" 2405 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2406 2407 this = self.sql(expression, "this") 2408 if this: 2409 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2410 this = f" {this}" if unwrapped else f" ({this})" 2411 2412 return f"INTERVAL{this}{unit}"
2417 def reference_sql(self, expression: exp.Reference) -> str: 2418 this = self.sql(expression, "this") 2419 expressions = self.expressions(expression, flat=True) 2420 expressions = f"({expressions})" if expressions else "" 2421 options = self.expressions(expression, key="options", flat=True, sep=" ") 2422 options = f" {options}" if options else "" 2423 return f"REFERENCES {this}{expressions}{options}"
2428 def paren_sql(self, expression: exp.Paren) -> str: 2429 if isinstance(expression.unnest(), exp.Select): 2430 sql = self.wrap(expression) 2431 else: 2432 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2433 sql = f"({sql}{self.seg(')', sep='')}" 2434 2435 return self.prepend_ctes(expression, sql)
2473 def connector_sql(self, expression: exp.Connector, op: str) -> str: 2474 if not self.pretty: 2475 return self.binary(expression, op) 2476 2477 sqls = tuple( 2478 self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e) 2479 for i, e in enumerate(expression.flatten(unnest=False)) 2480 ) 2481 2482 sep = "\n" if self.text_width(sqls) > self.max_text_width else " " 2483 return f"{sep}{op} ".join(sqls)
def
cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str:
2503 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2504 format_sql = self.sql(expression, "format") 2505 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2506 to_sql = self.sql(expression, "to") 2507 to_sql = f" {to_sql}" if to_sql else "" 2508 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})"
2522 def comment_sql(self, expression: exp.Comment) -> str: 2523 this = self.sql(expression, "this") 2524 kind = expression.args["kind"] 2525 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2526 expression_sql = self.sql(expression, "expression") 2527 return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
2529 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2530 this = self.sql(expression, "this") 2531 delete = " DELETE" if expression.args.get("delete") else "" 2532 recompress = self.sql(expression, "recompress") 2533 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2534 to_disk = self.sql(expression, "to_disk") 2535 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2536 to_volume = self.sql(expression, "to_volume") 2537 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2538 return f"{this}{delete}{recompress}{to_disk}{to_volume}"
2540 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2541 where = self.sql(expression, "where") 2542 group = self.sql(expression, "group") 2543 aggregates = self.expressions(expression, key="aggregates") 2544 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2545 2546 if not (where or group or aggregates) and len(expression.expressions) == 1: 2547 return f"TTL {self.expressions(expression, flat=True)}" 2548 2549 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
2566 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2567 this = self.sql(expression, "this") 2568 2569 dtype = self.sql(expression, "dtype") 2570 if dtype: 2571 collate = self.sql(expression, "collate") 2572 collate = f" COLLATE {collate}" if collate else "" 2573 using = self.sql(expression, "using") 2574 using = f" USING {using}" if using else "" 2575 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2576 2577 default = self.sql(expression, "default") 2578 if default: 2579 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2580 2581 if not expression.args.get("drop"): 2582 self.unsupported("Unsupported ALTER COLUMN syntax") 2583 2584 return f"ALTER COLUMN {this} DROP DEFAULT"
2586 def renametable_sql(self, expression: exp.RenameTable) -> str: 2587 if not self.RENAME_TABLE_WITH_DB: 2588 # Remove db from tables 2589 expression = expression.transform( 2590 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 2591 ) 2592 this = self.sql(expression, "this") 2593 return f"RENAME TO {this}"
2595 def altertable_sql(self, expression: exp.AlterTable) -> str: 2596 actions = expression.args["actions"] 2597 2598 if isinstance(actions[0], exp.ColumnDef): 2599 actions = self.add_column_sql(expression) 2600 elif isinstance(actions[0], exp.Schema): 2601 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 2602 elif isinstance(actions[0], exp.Delete): 2603 actions = self.expressions(expression, key="actions", flat=True) 2604 else: 2605 actions = self.expressions(expression, key="actions", flat=True) 2606 2607 exists = " IF EXISTS" if expression.args.get("exists") else "" 2608 only = " ONLY" if expression.args.get("only") else "" 2609 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}"
2625 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 2626 this = self.sql(expression, "this") 2627 expression_ = self.sql(expression, "expression") 2628 add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD" 2629 2630 enforced = expression.args.get("enforced") 2631 if enforced is not None: 2632 return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}" 2633 2634 return f"{add_constraint} {expression_}"
2663 def div_sql(self, expression: exp.Div) -> str: 2664 l, r = expression.left, expression.right 2665 2666 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 2667 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 2668 2669 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 2670 if not l.is_type(*exp.DataType.FLOAT_TYPES) and not r.is_type( 2671 *exp.DataType.FLOAT_TYPES 2672 ): 2673 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 2674 2675 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 2676 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 2677 return self.sql( 2678 exp.cast( 2679 l / r, 2680 to=exp.DataType.Type.BIGINT, 2681 ) 2682 ) 2683 2684 return self.binary(expression, "/")
2788 def function_fallback_sql(self, expression: exp.Func) -> str: 2789 args = [] 2790 2791 for key in expression.arg_types: 2792 arg_value = expression.args.get(key) 2793 2794 if isinstance(arg_value, list): 2795 for value in arg_value: 2796 args.append(value) 2797 elif arg_value is not None: 2798 args.append(arg_value) 2799 2800 if self.normalize_functions: 2801 name = expression.sql_name() 2802 else: 2803 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 2804 2805 return self.func(name, *args)
def
func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')') -> str:
2816 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 2817 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 2818 if self.pretty and self.text_width(arg_sqls) > self.max_text_width: 2819 return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 2820 return ", ".join(arg_sqls)
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, sep: str = ', ', prefix: str = '') -> str:
2832 def expressions( 2833 self, 2834 expression: t.Optional[exp.Expression] = None, 2835 key: t.Optional[str] = None, 2836 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 2837 flat: bool = False, 2838 indent: bool = True, 2839 skip_first: bool = False, 2840 sep: str = ", ", 2841 prefix: str = "", 2842 ) -> str: 2843 expressions = expression.args.get(key or "expressions") if expression else sqls 2844 2845 if not expressions: 2846 return "" 2847 2848 if flat: 2849 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 2850 2851 num_sqls = len(expressions) 2852 2853 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 2854 pad = " " * self.pad 2855 stripped_sep = sep.strip() 2856 2857 result_sqls = [] 2858 for i, e in enumerate(expressions): 2859 sql = self.sql(e, comment=False) 2860 if not sql: 2861 continue 2862 2863 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 2864 2865 if self.pretty: 2866 if self.leading_comma: 2867 result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}") 2868 else: 2869 result_sqls.append( 2870 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 2871 ) 2872 else: 2873 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 2874 2875 result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls) 2876 return self.indent(result_sql, skip_first=skip_first) if indent else result_sql
def
op_expressions( self, op: str, expression: sqlglot.expressions.Expression, flat: bool = False) -> str:
2878 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 2879 flat = flat or isinstance(expression.parent, exp.Properties) 2880 expressions_sql = self.expressions(expression, flat=flat) 2881 if flat: 2882 return f"{op} {expressions_sql}" 2883 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
2885 def naked_property(self, expression: exp.Property) -> str: 2886 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 2887 if not property_name: 2888 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 2889 return f"{property_name} {self.sql(expression, 'this')}"
2891 def set_operation(self, expression: exp.Union, op: str) -> str: 2892 this = self.maybe_comment(self.sql(expression, "this"), comments=expression.comments) 2893 op = self.seg(op) 2894 return self.query_modifiers( 2895 expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}" 2896 )
2904 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 2905 this = self.sql(expression, "this") 2906 expressions = self.no_identify(self.expressions, expression) 2907 expressions = ( 2908 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 2909 ) 2910 return f"{this}{expressions}"
2920 def when_sql(self, expression: exp.When) -> str: 2921 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 2922 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 2923 condition = self.sql(expression, "condition") 2924 condition = f" AND {condition}" if condition else "" 2925 2926 then_expression = expression.args.get("then") 2927 if isinstance(then_expression, exp.Insert): 2928 then = f"INSERT {self.sql(then_expression, 'this')}" 2929 if "expression" in then_expression.args: 2930 then += f" VALUES {self.sql(then_expression, 'expression')}" 2931 elif isinstance(then_expression, exp.Update): 2932 if isinstance(then_expression.args.get("expressions"), exp.Star): 2933 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 2934 else: 2935 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 2936 else: 2937 then = self.sql(then_expression) 2938 return f"WHEN {matched}{source}{condition} THEN {then}"
2940 def merge_sql(self, expression: exp.Merge) -> str: 2941 table = expression.this 2942 table_alias = "" 2943 2944 hints = table.args.get("hints") 2945 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 2946 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 2947 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 2948 2949 this = self.sql(table) 2950 using = f"USING {self.sql(expression, 'using')}" 2951 on = f"ON {self.sql(expression, 'on')}" 2952 expressions = self.expressions(expression, sep=" ") 2953 2954 return self.prepend_ctes( 2955 expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}" 2956 )
2964 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 2965 this = self.sql(expression, "this") 2966 kind = self.sql(expression, "kind") 2967 settings_sql = self.expressions(expression, key="settings", sep=" ") 2968 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 2969 return f"{this}({kind}{args})"
2983 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 2984 expressions = self.expressions(expression, key="expressions", flat=True) 2985 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 2986 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 2987 buckets = self.sql(expression, "buckets") 2988 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
2990 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 2991 this = self.sql(expression, "this") 2992 having = self.sql(expression, "having") 2993 2994 if having: 2995 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 2996 2997 return self.func("ANY_VALUE", this)
2999 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3000 transform = self.func("TRANSFORM", *expression.expressions) 3001 row_format_before = self.sql(expression, "row_format_before") 3002 row_format_before = f" {row_format_before}" if row_format_before else "" 3003 record_writer = self.sql(expression, "record_writer") 3004 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3005 using = f" USING {self.sql(expression, 'command_script')}" 3006 schema = self.sql(expression, "schema") 3007 schema = f" AS {schema}" if schema else "" 3008 row_format_after = self.sql(expression, "row_format_after") 3009 row_format_after = f" {row_format_after}" if row_format_after else "" 3010 record_reader = self.sql(expression, "record_reader") 3011 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3012 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
3014 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3015 key_block_size = self.sql(expression, "key_block_size") 3016 if key_block_size: 3017 return f"KEY_BLOCK_SIZE = {key_block_size}" 3018 3019 using = self.sql(expression, "using") 3020 if using: 3021 return f"USING {using}" 3022 3023 parser = self.sql(expression, "parser") 3024 if parser: 3025 return f"WITH PARSER {parser}" 3026 3027 comment = self.sql(expression, "comment") 3028 if comment: 3029 return f"COMMENT {comment}" 3030 3031 visible = expression.args.get("visible") 3032 if visible is not None: 3033 return "VISIBLE" if visible else "INVISIBLE" 3034 3035 engine_attr = self.sql(expression, "engine_attr") 3036 if engine_attr: 3037 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3038 3039 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3040 if secondary_engine_attr: 3041 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3042 3043 self.unsupported("Unsupported index constraint option.") 3044 return ""
3046 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3047 kind = self.sql(expression, "kind") 3048 kind = f"{kind} INDEX" if kind else "INDEX" 3049 this = self.sql(expression, "this") 3050 this = f" {this}" if this else "" 3051 index_type = self.sql(expression, "index_type") 3052 index_type = f" USING {index_type}" if index_type else "" 3053 schema = self.sql(expression, "schema") 3054 schema = f" {schema}" if schema else "" 3055 options = self.expressions(expression, key="options", sep=" ") 3056 options = f" {options}" if options else "" 3057 return f"{kind}{this}{index_type}{schema}{options}"
3059 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3060 if self.NVL2_SUPPORTED: 3061 return self.function_fallback_sql(expression) 3062 3063 case = exp.Case().when( 3064 expression.this.is_(exp.null()).not_(copy=False), 3065 expression.args["true"], 3066 copy=False, 3067 ) 3068 else_cond = expression.args.get("false") 3069 if else_cond: 3070 case.else_(else_cond, copy=False) 3071 3072 return self.sql(case)
3074 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3075 this = self.sql(expression, "this") 3076 expr = self.sql(expression, "expression") 3077 iterator = self.sql(expression, "iterator") 3078 condition = self.sql(expression, "condition") 3079 condition = f" IF {condition}" if condition else "" 3080 return f"{this} FOR {expr} IN {iterator}{condition}"
3088 def predict_sql(self, expression: exp.Predict) -> str: 3089 model = self.sql(expression, "this") 3090 model = f"MODEL {model}" 3091 table = self.sql(expression, "expression") 3092 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3093 parameters = self.sql(expression, "params_struct") 3094 return self.func("PREDICT", model, table, parameters or None)
3109 def toarray_sql(self, expression: exp.ToArray) -> str: 3110 arg = expression.this 3111 if not arg.type: 3112 from sqlglot.optimizer.annotate_types import annotate_types 3113 3114 arg = annotate_types(arg) 3115 3116 if arg.is_type(exp.DataType.Type.ARRAY): 3117 return self.sql(arg) 3118 3119 cond_for_null = arg.is_(exp.null()) 3120 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.Array(expressions=[arg])))
3129 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3130 this = expression.this 3131 time_format = self.format_time(expression) 3132 3133 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3134 return self.sql( 3135 exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date") 3136 ) 3137 3138 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3139 return self.sql(this) 3140 3141 return self.sql(exp.cast(this, "date"))
3153 def lastday_sql(self, expression: exp.LastDay) -> str: 3154 if self.LAST_DAY_SUPPORTS_DATE_PART: 3155 return self.function_fallback_sql(expression) 3156 3157 unit = expression.text("unit") 3158 if unit and unit != "MONTH": 3159 self.unsupported("Date parts are not supported in LAST_DAY.") 3160 3161 return self.func("LAST_DAY", expression.this)