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 = expression.this 1690 this_sql = self.sql(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 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 1701 return f" {this_sql}" 1702 1703 return f", {this_sql}" 1704 1705 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1706 return f"{self.seg(op_sql)} {this_sql}{on_sql}" 1707 1708 def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str: 1709 args = self.expressions(expression, flat=True) 1710 args = f"({args})" if len(args.split(",")) > 1 else args 1711 return f"{args} {arrow_sep} {self.sql(expression, 'this')}" 1712 1713 def lateral_op(self, expression: exp.Lateral) -> str: 1714 cross_apply = expression.args.get("cross_apply") 1715 1716 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 1717 if cross_apply is True: 1718 op = "INNER JOIN " 1719 elif cross_apply is False: 1720 op = "LEFT JOIN " 1721 else: 1722 op = "" 1723 1724 return f"{op}LATERAL" 1725 1726 def lateral_sql(self, expression: exp.Lateral) -> str: 1727 this = self.sql(expression, "this") 1728 1729 if expression.args.get("view"): 1730 alias = expression.args["alias"] 1731 columns = self.expressions(alias, key="columns", flat=True) 1732 table = f" {alias.name}" if alias.name else "" 1733 columns = f" AS {columns}" if columns else "" 1734 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1735 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1736 1737 alias = self.sql(expression, "alias") 1738 alias = f" AS {alias}" if alias else "" 1739 return f"{self.lateral_op(expression)} {this}{alias}" 1740 1741 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1742 this = self.sql(expression, "this") 1743 1744 args = [ 1745 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1746 for e in (expression.args.get(k) for k in ("offset", "expression")) 1747 if e 1748 ] 1749 1750 args_sql = ", ".join(self.sql(e) for e in args) 1751 args_sql = f"({args_sql})" if any(top and not e.is_number for e in args) else args_sql 1752 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}" 1753 1754 def offset_sql(self, expression: exp.Offset) -> str: 1755 this = self.sql(expression, "this") 1756 expression = expression.expression 1757 expression = ( 1758 self._simplify_unless_literal(expression) if self.LIMIT_ONLY_LITERALS else expression 1759 ) 1760 return f"{this}{self.seg('OFFSET')} {self.sql(expression)}" 1761 1762 def setitem_sql(self, expression: exp.SetItem) -> str: 1763 kind = self.sql(expression, "kind") 1764 kind = f"{kind} " if kind else "" 1765 this = self.sql(expression, "this") 1766 expressions = self.expressions(expression) 1767 collate = self.sql(expression, "collate") 1768 collate = f" COLLATE {collate}" if collate else "" 1769 global_ = "GLOBAL " if expression.args.get("global") else "" 1770 return f"{global_}{kind}{this}{expressions}{collate}" 1771 1772 def set_sql(self, expression: exp.Set) -> str: 1773 expressions = ( 1774 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 1775 ) 1776 tag = " TAG" if expression.args.get("tag") else "" 1777 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}" 1778 1779 def pragma_sql(self, expression: exp.Pragma) -> str: 1780 return f"PRAGMA {self.sql(expression, 'this')}" 1781 1782 def lock_sql(self, expression: exp.Lock) -> str: 1783 if not self.LOCKING_READS_SUPPORTED: 1784 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 1785 return "" 1786 1787 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 1788 expressions = self.expressions(expression, flat=True) 1789 expressions = f" OF {expressions}" if expressions else "" 1790 wait = expression.args.get("wait") 1791 1792 if wait is not None: 1793 if isinstance(wait, exp.Literal): 1794 wait = f" WAIT {self.sql(wait)}" 1795 else: 1796 wait = " NOWAIT" if wait else " SKIP LOCKED" 1797 1798 return f"{lock_type}{expressions}{wait or ''}" 1799 1800 def literal_sql(self, expression: exp.Literal) -> str: 1801 text = expression.this or "" 1802 if expression.is_string: 1803 text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}" 1804 return text 1805 1806 def escape_str(self, text: str) -> str: 1807 text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end) 1808 if self.dialect.INVERSE_ESCAPE_SEQUENCES: 1809 text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text) 1810 elif self.pretty: 1811 text = text.replace("\n", self.SENTINEL_LINE_BREAK) 1812 return text 1813 1814 def loaddata_sql(self, expression: exp.LoadData) -> str: 1815 local = " LOCAL" if expression.args.get("local") else "" 1816 inpath = f" INPATH {self.sql(expression, 'inpath')}" 1817 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 1818 this = f" INTO TABLE {self.sql(expression, 'this')}" 1819 partition = self.sql(expression, "partition") 1820 partition = f" {partition}" if partition else "" 1821 input_format = self.sql(expression, "input_format") 1822 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 1823 serde = self.sql(expression, "serde") 1824 serde = f" SERDE {serde}" if serde else "" 1825 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}" 1826 1827 def null_sql(self, *_) -> str: 1828 return "NULL" 1829 1830 def boolean_sql(self, expression: exp.Boolean) -> str: 1831 return "TRUE" if expression.this else "FALSE" 1832 1833 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 1834 this = self.sql(expression, "this") 1835 this = f"{this} " if this else this 1836 order = self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat) # type: ignore 1837 interpolated_values = [ 1838 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 1839 for named_expression in expression.args.get("interpolate") or [] 1840 ] 1841 interpolate = ( 1842 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 1843 ) 1844 return f"{order}{interpolate}" 1845 1846 def withfill_sql(self, expression: exp.WithFill) -> str: 1847 from_sql = self.sql(expression, "from") 1848 from_sql = f" FROM {from_sql}" if from_sql else "" 1849 to_sql = self.sql(expression, "to") 1850 to_sql = f" TO {to_sql}" if to_sql else "" 1851 step_sql = self.sql(expression, "step") 1852 step_sql = f" STEP {step_sql}" if step_sql else "" 1853 return f"WITH FILL{from_sql}{to_sql}{step_sql}" 1854 1855 def cluster_sql(self, expression: exp.Cluster) -> str: 1856 return self.op_expressions("CLUSTER BY", expression) 1857 1858 def distribute_sql(self, expression: exp.Distribute) -> str: 1859 return self.op_expressions("DISTRIBUTE BY", expression) 1860 1861 def sort_sql(self, expression: exp.Sort) -> str: 1862 return self.op_expressions("SORT BY", expression) 1863 1864 def ordered_sql(self, expression: exp.Ordered) -> str: 1865 desc = expression.args.get("desc") 1866 asc = not desc 1867 1868 nulls_first = expression.args.get("nulls_first") 1869 nulls_last = not nulls_first 1870 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 1871 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 1872 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 1873 1874 this = self.sql(expression, "this") 1875 1876 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 1877 nulls_sort_change = "" 1878 if nulls_first and ( 1879 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 1880 ): 1881 nulls_sort_change = " NULLS FIRST" 1882 elif ( 1883 nulls_last 1884 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 1885 and not nulls_are_last 1886 ): 1887 nulls_sort_change = " NULLS LAST" 1888 1889 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 1890 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 1891 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 1892 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 1893 nulls_sort_change = "" 1894 1895 with_fill = self.sql(expression, "with_fill") 1896 with_fill = f" {with_fill}" if with_fill else "" 1897 1898 return f"{this}{sort_order}{nulls_sort_change}{with_fill}" 1899 1900 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 1901 partition = self.partition_by_sql(expression) 1902 order = self.sql(expression, "order") 1903 measures = self.expressions(expression, key="measures") 1904 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 1905 rows = self.sql(expression, "rows") 1906 rows = self.seg(rows) if rows else "" 1907 after = self.sql(expression, "after") 1908 after = self.seg(after) if after else "" 1909 pattern = self.sql(expression, "pattern") 1910 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 1911 definition_sqls = [ 1912 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 1913 for definition in expression.args.get("define", []) 1914 ] 1915 definitions = self.expressions(sqls=definition_sqls) 1916 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 1917 body = "".join( 1918 ( 1919 partition, 1920 order, 1921 measures, 1922 rows, 1923 after, 1924 pattern, 1925 define, 1926 ) 1927 ) 1928 alias = self.sql(expression, "alias") 1929 alias = f" {alias}" if alias else "" 1930 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}" 1931 1932 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 1933 limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit") 1934 1935 # If the limit is generated as TOP, we need to ensure it's not generated twice 1936 with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP 1937 1938 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 1939 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 1940 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 1941 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 1942 1943 fetch = isinstance(limit, exp.Fetch) 1944 1945 offset_limit_modifiers = ( 1946 self.offset_limit_modifiers(expression, fetch, limit) 1947 if with_offset_limit_modifiers 1948 else [] 1949 ) 1950 1951 return csv( 1952 *sqls, 1953 *[self.sql(join) for join in expression.args.get("joins") or []], 1954 self.sql(expression, "connect"), 1955 self.sql(expression, "match"), 1956 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 1957 self.sql(expression, "where"), 1958 self.sql(expression, "group"), 1959 self.sql(expression, "having"), 1960 *self.after_having_modifiers(expression), 1961 self.sql(expression, "order"), 1962 *offset_limit_modifiers, 1963 *self.after_limit_modifiers(expression), 1964 sep="", 1965 ) 1966 1967 def offset_limit_modifiers( 1968 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 1969 ) -> t.List[str]: 1970 return [ 1971 self.sql(expression, "offset") if fetch else self.sql(limit), 1972 self.sql(limit) if fetch else self.sql(expression, "offset"), 1973 ] 1974 1975 def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]: 1976 return [ 1977 self.sql(expression, "qualify"), 1978 self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True) 1979 if expression.args.get("windows") 1980 else "", 1981 self.sql(expression, "distribute"), 1982 self.sql(expression, "sort"), 1983 self.sql(expression, "cluster"), 1984 ] 1985 1986 def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]: 1987 locks = self.expressions(expression, key="locks", sep=" ") 1988 locks = f" {locks}" if locks else "" 1989 return [locks, self.sql(expression, "sample")] 1990 1991 def select_sql(self, expression: exp.Select) -> str: 1992 hint = self.sql(expression, "hint") 1993 distinct = self.sql(expression, "distinct") 1994 distinct = f" {distinct}" if distinct else "" 1995 kind = self.sql(expression, "kind") 1996 limit = expression.args.get("limit") 1997 top = ( 1998 self.limit_sql(limit, top=True) 1999 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP 2000 else "" 2001 ) 2002 2003 expressions = self.expressions(expression) 2004 2005 if kind: 2006 if kind in self.SELECT_KINDS: 2007 kind = f" AS {kind}" 2008 else: 2009 if kind == "STRUCT": 2010 expressions = self.expressions( 2011 sqls=[ 2012 self.sql( 2013 exp.Struct( 2014 expressions=[ 2015 exp.column(e.output_name).eq( 2016 e.this if isinstance(e, exp.Alias) else e 2017 ) 2018 for e in expression.expressions 2019 ] 2020 ) 2021 ) 2022 ] 2023 ) 2024 kind = "" 2025 2026 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2027 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2028 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2029 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2030 sql = self.query_modifiers( 2031 expression, 2032 f"SELECT{top_distinct}{kind}{expressions}", 2033 self.sql(expression, "into", comment=False), 2034 self.sql(expression, "from", comment=False), 2035 ) 2036 return self.prepend_ctes(expression, sql) 2037 2038 def schema_sql(self, expression: exp.Schema) -> str: 2039 this = self.sql(expression, "this") 2040 sql = self.schema_columns_sql(expression) 2041 return f"{this} {sql}" if this and sql else this or sql 2042 2043 def schema_columns_sql(self, expression: exp.Schema) -> str: 2044 if expression.expressions: 2045 return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}" 2046 return "" 2047 2048 def star_sql(self, expression: exp.Star) -> str: 2049 except_ = self.expressions(expression, key="except", flat=True) 2050 except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else "" 2051 replace = self.expressions(expression, key="replace", flat=True) 2052 replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else "" 2053 return f"*{except_}{replace}" 2054 2055 def parameter_sql(self, expression: exp.Parameter) -> str: 2056 this = self.sql(expression, "this") 2057 return f"{self.PARAMETER_TOKEN}{this}" 2058 2059 def sessionparameter_sql(self, expression: exp.SessionParameter) -> str: 2060 this = self.sql(expression, "this") 2061 kind = expression.text("kind") 2062 if kind: 2063 kind = f"{kind}." 2064 return f"@@{kind}{this}" 2065 2066 def placeholder_sql(self, expression: exp.Placeholder) -> str: 2067 return f":{expression.name}" if expression.name else "?" 2068 2069 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2070 alias = self.sql(expression, "alias") 2071 alias = f"{sep}{alias}" if alias else "" 2072 2073 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2074 pivots = f" {pivots}" if pivots else "" 2075 2076 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2077 return self.prepend_ctes(expression, sql) 2078 2079 def qualify_sql(self, expression: exp.Qualify) -> str: 2080 this = self.indent(self.sql(expression, "this")) 2081 return f"{self.seg('QUALIFY')}{self.sep()}{this}" 2082 2083 def union_sql(self, expression: exp.Union) -> str: 2084 return self.prepend_ctes( 2085 expression, 2086 self.set_operation(expression, self.union_op(expression)), 2087 ) 2088 2089 def union_op(self, expression: exp.Union) -> str: 2090 kind = " DISTINCT" if self.EXPLICIT_UNION else "" 2091 kind = kind if expression.args.get("distinct") else " ALL" 2092 by_name = " BY NAME" if expression.args.get("by_name") else "" 2093 return f"UNION{kind}{by_name}" 2094 2095 def unnest_sql(self, expression: exp.Unnest) -> str: 2096 args = self.expressions(expression, flat=True) 2097 2098 alias = expression.args.get("alias") 2099 offset = expression.args.get("offset") 2100 2101 if self.UNNEST_WITH_ORDINALITY: 2102 if alias and isinstance(offset, exp.Expression): 2103 alias.append("columns", offset) 2104 2105 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2106 columns = alias.columns 2107 alias = self.sql(columns[0]) if columns else "" 2108 else: 2109 alias = self.sql(alias) 2110 2111 alias = f" AS {alias}" if alias else alias 2112 if self.UNNEST_WITH_ORDINALITY: 2113 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2114 else: 2115 if isinstance(offset, exp.Expression): 2116 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2117 elif offset: 2118 suffix = f"{alias} WITH OFFSET" 2119 else: 2120 suffix = alias 2121 2122 return f"UNNEST({args}){suffix}" 2123 2124 def where_sql(self, expression: exp.Where) -> str: 2125 this = self.indent(self.sql(expression, "this")) 2126 return f"{self.seg('WHERE')}{self.sep()}{this}" 2127 2128 def window_sql(self, expression: exp.Window) -> str: 2129 this = self.sql(expression, "this") 2130 partition = self.partition_by_sql(expression) 2131 order = expression.args.get("order") 2132 order = self.order_sql(order, flat=True) if order else "" 2133 spec = self.sql(expression, "spec") 2134 alias = self.sql(expression, "alias") 2135 over = self.sql(expression, "over") or "OVER" 2136 2137 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2138 2139 first = expression.args.get("first") 2140 if first is None: 2141 first = "" 2142 else: 2143 first = "FIRST" if first else "LAST" 2144 2145 if not partition and not order and not spec and alias: 2146 return f"{this} {alias}" 2147 2148 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2149 return f"{this} ({args})" 2150 2151 def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str: 2152 partition = self.expressions(expression, key="partition_by", flat=True) 2153 return f"PARTITION BY {partition}" if partition else "" 2154 2155 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2156 kind = self.sql(expression, "kind") 2157 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2158 end = ( 2159 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2160 or "CURRENT ROW" 2161 ) 2162 return f"{kind} BETWEEN {start} AND {end}" 2163 2164 def withingroup_sql(self, expression: exp.WithinGroup) -> str: 2165 this = self.sql(expression, "this") 2166 expression_sql = self.sql(expression, "expression")[1:] # order has a leading space 2167 return f"{this} WITHIN GROUP ({expression_sql})" 2168 2169 def between_sql(self, expression: exp.Between) -> str: 2170 this = self.sql(expression, "this") 2171 low = self.sql(expression, "low") 2172 high = self.sql(expression, "high") 2173 return f"{this} BETWEEN {low} AND {high}" 2174 2175 def bracket_sql(self, expression: exp.Bracket) -> str: 2176 expressions = apply_index_offset( 2177 expression.this, 2178 expression.expressions, 2179 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2180 ) 2181 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2182 return f"{self.sql(expression, 'this')}[{expressions_sql}]" 2183 2184 def all_sql(self, expression: exp.All) -> str: 2185 return f"ALL {self.wrap(expression)}" 2186 2187 def any_sql(self, expression: exp.Any) -> str: 2188 this = self.sql(expression, "this") 2189 if isinstance(expression.this, exp.Subqueryable): 2190 this = self.wrap(this) 2191 return f"ANY {this}" 2192 2193 def exists_sql(self, expression: exp.Exists) -> str: 2194 return f"EXISTS{self.wrap(expression)}" 2195 2196 def case_sql(self, expression: exp.Case) -> str: 2197 this = self.sql(expression, "this") 2198 statements = [f"CASE {this}" if this else "CASE"] 2199 2200 for e in expression.args["ifs"]: 2201 statements.append(f"WHEN {self.sql(e, 'this')}") 2202 statements.append(f"THEN {self.sql(e, 'true')}") 2203 2204 default = self.sql(expression, "default") 2205 2206 if default: 2207 statements.append(f"ELSE {default}") 2208 2209 statements.append("END") 2210 2211 if self.pretty and self.text_width(statements) > self.max_text_width: 2212 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2213 2214 return " ".join(statements) 2215 2216 def constraint_sql(self, expression: exp.Constraint) -> str: 2217 this = self.sql(expression, "this") 2218 expressions = self.expressions(expression, flat=True) 2219 return f"CONSTRAINT {this} {expressions}" 2220 2221 def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str: 2222 order = expression.args.get("order") 2223 order = f" OVER ({self.order_sql(order, flat=True)})" if order else "" 2224 return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}" 2225 2226 def extract_sql(self, expression: exp.Extract) -> str: 2227 this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name 2228 expression_sql = self.sql(expression, "expression") 2229 return f"EXTRACT({this} FROM {expression_sql})" 2230 2231 def trim_sql(self, expression: exp.Trim) -> str: 2232 trim_type = self.sql(expression, "position") 2233 2234 if trim_type == "LEADING": 2235 return self.func("LTRIM", expression.this) 2236 elif trim_type == "TRAILING": 2237 return self.func("RTRIM", expression.this) 2238 else: 2239 return self.func("TRIM", expression.this, expression.expression) 2240 2241 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2242 args = expression.expressions 2243 if isinstance(expression, exp.ConcatWs): 2244 args = args[1:] # Skip the delimiter 2245 2246 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2247 args = [exp.cast(e, "text") for e in args] 2248 2249 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2250 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2251 2252 return args 2253 2254 def concat_sql(self, expression: exp.Concat) -> str: 2255 expressions = self.convert_concat_args(expression) 2256 2257 # Some dialects don't allow a single-argument CONCAT call 2258 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2259 return self.sql(expressions[0]) 2260 2261 return self.func("CONCAT", *expressions) 2262 2263 def concatws_sql(self, expression: exp.ConcatWs) -> str: 2264 return self.func( 2265 "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression) 2266 ) 2267 2268 def check_sql(self, expression: exp.Check) -> str: 2269 this = self.sql(expression, key="this") 2270 return f"CHECK ({this})" 2271 2272 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2273 expressions = self.expressions(expression, flat=True) 2274 reference = self.sql(expression, "reference") 2275 reference = f" {reference}" if reference else "" 2276 delete = self.sql(expression, "delete") 2277 delete = f" ON DELETE {delete}" if delete else "" 2278 update = self.sql(expression, "update") 2279 update = f" ON UPDATE {update}" if update else "" 2280 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}" 2281 2282 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2283 expressions = self.expressions(expression, flat=True) 2284 options = self.expressions(expression, key="options", flat=True, sep=" ") 2285 options = f" {options}" if options else "" 2286 return f"PRIMARY KEY ({expressions}){options}" 2287 2288 def if_sql(self, expression: exp.If) -> str: 2289 return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false"))) 2290 2291 def matchagainst_sql(self, expression: exp.MatchAgainst) -> str: 2292 modifier = expression.args.get("modifier") 2293 modifier = f" {modifier}" if modifier else "" 2294 return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})" 2295 2296 def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str: 2297 return f"{self.sql(expression, 'this')}: {self.sql(expression, 'expression')}" 2298 2299 def formatjson_sql(self, expression: exp.FormatJson) -> str: 2300 return f"{self.sql(expression, 'this')} FORMAT JSON" 2301 2302 def jsonobject_sql(self, expression: exp.JSONObject) -> str: 2303 null_handling = expression.args.get("null_handling") 2304 null_handling = f" {null_handling}" if null_handling else "" 2305 unique_keys = expression.args.get("unique_keys") 2306 if unique_keys is not None: 2307 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2308 else: 2309 unique_keys = "" 2310 return_type = self.sql(expression, "return_type") 2311 return_type = f" RETURNING {return_type}" if return_type else "" 2312 encoding = self.sql(expression, "encoding") 2313 encoding = f" ENCODING {encoding}" if encoding else "" 2314 return self.func( 2315 "JSON_OBJECT", 2316 *expression.expressions, 2317 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2318 ) 2319 2320 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2321 null_handling = expression.args.get("null_handling") 2322 null_handling = f" {null_handling}" if null_handling else "" 2323 return_type = self.sql(expression, "return_type") 2324 return_type = f" RETURNING {return_type}" if return_type else "" 2325 strict = " STRICT" if expression.args.get("strict") else "" 2326 return self.func( 2327 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2328 ) 2329 2330 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2331 this = self.sql(expression, "this") 2332 order = self.sql(expression, "order") 2333 null_handling = expression.args.get("null_handling") 2334 null_handling = f" {null_handling}" if null_handling else "" 2335 return_type = self.sql(expression, "return_type") 2336 return_type = f" RETURNING {return_type}" if return_type else "" 2337 strict = " STRICT" if expression.args.get("strict") else "" 2338 return self.func( 2339 "JSON_ARRAYAGG", 2340 this, 2341 suffix=f"{order}{null_handling}{return_type}{strict})", 2342 ) 2343 2344 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2345 path = self.sql(expression, "path") 2346 path = f" PATH {path}" if path else "" 2347 nested_schema = self.sql(expression, "nested_schema") 2348 2349 if nested_schema: 2350 return f"NESTED{path} {nested_schema}" 2351 2352 this = self.sql(expression, "this") 2353 kind = self.sql(expression, "kind") 2354 kind = f" {kind}" if kind else "" 2355 return f"{this}{kind}{path}" 2356 2357 def jsonschema_sql(self, expression: exp.JSONSchema) -> str: 2358 return self.func("COLUMNS", *expression.expressions) 2359 2360 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2361 this = self.sql(expression, "this") 2362 path = self.sql(expression, "path") 2363 path = f", {path}" if path else "" 2364 error_handling = expression.args.get("error_handling") 2365 error_handling = f" {error_handling}" if error_handling else "" 2366 empty_handling = expression.args.get("empty_handling") 2367 empty_handling = f" {empty_handling}" if empty_handling else "" 2368 schema = self.sql(expression, "schema") 2369 return self.func( 2370 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2371 ) 2372 2373 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2374 this = self.sql(expression, "this") 2375 kind = self.sql(expression, "kind") 2376 path = self.sql(expression, "path") 2377 path = f" {path}" if path else "" 2378 as_json = " AS JSON" if expression.args.get("as_json") else "" 2379 return f"{this} {kind}{path}{as_json}" 2380 2381 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2382 this = self.sql(expression, "this") 2383 path = self.sql(expression, "path") 2384 path = f", {path}" if path else "" 2385 expressions = self.expressions(expression) 2386 with_ = ( 2387 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2388 if expressions 2389 else "" 2390 ) 2391 return f"OPENJSON({this}{path}){with_}" 2392 2393 def in_sql(self, expression: exp.In) -> str: 2394 query = expression.args.get("query") 2395 unnest = expression.args.get("unnest") 2396 field = expression.args.get("field") 2397 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2398 2399 if query: 2400 in_sql = self.wrap(query) 2401 elif unnest: 2402 in_sql = self.in_unnest_op(unnest) 2403 elif field: 2404 in_sql = self.sql(field) 2405 else: 2406 in_sql = f"({self.expressions(expression, flat=True)})" 2407 2408 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}" 2409 2410 def in_unnest_op(self, unnest: exp.Unnest) -> str: 2411 return f"(SELECT {self.sql(unnest)})" 2412 2413 def interval_sql(self, expression: exp.Interval) -> str: 2414 unit = self.sql(expression, "unit") 2415 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2416 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2417 unit = f" {unit}" if unit else "" 2418 2419 if self.SINGLE_STRING_INTERVAL: 2420 this = expression.this.name if expression.this else "" 2421 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2422 2423 this = self.sql(expression, "this") 2424 if this: 2425 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2426 this = f" {this}" if unwrapped else f" ({this})" 2427 2428 return f"INTERVAL{this}{unit}" 2429 2430 def return_sql(self, expression: exp.Return) -> str: 2431 return f"RETURN {self.sql(expression, 'this')}" 2432 2433 def reference_sql(self, expression: exp.Reference) -> str: 2434 this = self.sql(expression, "this") 2435 expressions = self.expressions(expression, flat=True) 2436 expressions = f"({expressions})" if expressions else "" 2437 options = self.expressions(expression, key="options", flat=True, sep=" ") 2438 options = f" {options}" if options else "" 2439 return f"REFERENCES {this}{expressions}{options}" 2440 2441 def anonymous_sql(self, expression: exp.Anonymous) -> str: 2442 return self.func(expression.name, *expression.expressions) 2443 2444 def paren_sql(self, expression: exp.Paren) -> str: 2445 if isinstance(expression.unnest(), exp.Select): 2446 sql = self.wrap(expression) 2447 else: 2448 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2449 sql = f"({sql}{self.seg(')', sep='')}" 2450 2451 return self.prepend_ctes(expression, sql) 2452 2453 def neg_sql(self, expression: exp.Neg) -> str: 2454 # This makes sure we don't convert "- - 5" to "--5", which is a comment 2455 this_sql = self.sql(expression, "this") 2456 sep = " " if this_sql[0] == "-" else "" 2457 return f"-{sep}{this_sql}" 2458 2459 def not_sql(self, expression: exp.Not) -> str: 2460 return f"NOT {self.sql(expression, 'this')}" 2461 2462 def alias_sql(self, expression: exp.Alias) -> str: 2463 alias = self.sql(expression, "alias") 2464 alias = f" AS {alias}" if alias else "" 2465 return f"{self.sql(expression, 'this')}{alias}" 2466 2467 def aliases_sql(self, expression: exp.Aliases) -> str: 2468 return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})" 2469 2470 def atindex_sql(self, expression: exp.AtTimeZone) -> str: 2471 this = self.sql(expression, "this") 2472 index = self.sql(expression, "expression") 2473 return f"{this} AT {index}" 2474 2475 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 2476 this = self.sql(expression, "this") 2477 zone = self.sql(expression, "zone") 2478 return f"{this} AT TIME ZONE {zone}" 2479 2480 def add_sql(self, expression: exp.Add) -> str: 2481 return self.binary(expression, "+") 2482 2483 def and_sql(self, expression: exp.And) -> str: 2484 return self.connector_sql(expression, "AND") 2485 2486 def xor_sql(self, expression: exp.Xor) -> str: 2487 return self.connector_sql(expression, "XOR") 2488 2489 def connector_sql(self, expression: exp.Connector, op: str) -> str: 2490 if not self.pretty: 2491 return self.binary(expression, op) 2492 2493 sqls = tuple( 2494 self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e) 2495 for i, e in enumerate(expression.flatten(unnest=False)) 2496 ) 2497 2498 sep = "\n" if self.text_width(sqls) > self.max_text_width else " " 2499 return f"{sep}{op} ".join(sqls) 2500 2501 def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str: 2502 return self.binary(expression, "&") 2503 2504 def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str: 2505 return self.binary(expression, "<<") 2506 2507 def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str: 2508 return f"~{self.sql(expression, 'this')}" 2509 2510 def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str: 2511 return self.binary(expression, "|") 2512 2513 def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str: 2514 return self.binary(expression, ">>") 2515 2516 def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str: 2517 return self.binary(expression, "^") 2518 2519 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2520 format_sql = self.sql(expression, "format") 2521 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2522 to_sql = self.sql(expression, "to") 2523 to_sql = f" {to_sql}" if to_sql else "" 2524 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})" 2525 2526 def currentdate_sql(self, expression: exp.CurrentDate) -> str: 2527 zone = self.sql(expression, "this") 2528 return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE" 2529 2530 def collate_sql(self, expression: exp.Collate) -> str: 2531 if self.COLLATE_IS_FUNC: 2532 return self.function_fallback_sql(expression) 2533 return self.binary(expression, "COLLATE") 2534 2535 def command_sql(self, expression: exp.Command) -> str: 2536 return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}" 2537 2538 def comment_sql(self, expression: exp.Comment) -> str: 2539 this = self.sql(expression, "this") 2540 kind = expression.args["kind"] 2541 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2542 expression_sql = self.sql(expression, "expression") 2543 return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}" 2544 2545 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2546 this = self.sql(expression, "this") 2547 delete = " DELETE" if expression.args.get("delete") else "" 2548 recompress = self.sql(expression, "recompress") 2549 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2550 to_disk = self.sql(expression, "to_disk") 2551 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2552 to_volume = self.sql(expression, "to_volume") 2553 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2554 return f"{this}{delete}{recompress}{to_disk}{to_volume}" 2555 2556 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2557 where = self.sql(expression, "where") 2558 group = self.sql(expression, "group") 2559 aggregates = self.expressions(expression, key="aggregates") 2560 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2561 2562 if not (where or group or aggregates) and len(expression.expressions) == 1: 2563 return f"TTL {self.expressions(expression, flat=True)}" 2564 2565 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}" 2566 2567 def transaction_sql(self, expression: exp.Transaction) -> str: 2568 return "BEGIN" 2569 2570 def commit_sql(self, expression: exp.Commit) -> str: 2571 chain = expression.args.get("chain") 2572 if chain is not None: 2573 chain = " AND CHAIN" if chain else " AND NO CHAIN" 2574 2575 return f"COMMIT{chain or ''}" 2576 2577 def rollback_sql(self, expression: exp.Rollback) -> str: 2578 savepoint = expression.args.get("savepoint") 2579 savepoint = f" TO {savepoint}" if savepoint else "" 2580 return f"ROLLBACK{savepoint}" 2581 2582 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2583 this = self.sql(expression, "this") 2584 2585 dtype = self.sql(expression, "dtype") 2586 if dtype: 2587 collate = self.sql(expression, "collate") 2588 collate = f" COLLATE {collate}" if collate else "" 2589 using = self.sql(expression, "using") 2590 using = f" USING {using}" if using else "" 2591 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2592 2593 default = self.sql(expression, "default") 2594 if default: 2595 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2596 2597 if not expression.args.get("drop"): 2598 self.unsupported("Unsupported ALTER COLUMN syntax") 2599 2600 return f"ALTER COLUMN {this} DROP DEFAULT" 2601 2602 def renametable_sql(self, expression: exp.RenameTable) -> str: 2603 if not self.RENAME_TABLE_WITH_DB: 2604 # Remove db from tables 2605 expression = expression.transform( 2606 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 2607 ) 2608 this = self.sql(expression, "this") 2609 return f"RENAME TO {this}" 2610 2611 def altertable_sql(self, expression: exp.AlterTable) -> str: 2612 actions = expression.args["actions"] 2613 2614 if isinstance(actions[0], exp.ColumnDef): 2615 actions = self.add_column_sql(expression) 2616 elif isinstance(actions[0], exp.Schema): 2617 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 2618 elif isinstance(actions[0], exp.Delete): 2619 actions = self.expressions(expression, key="actions", flat=True) 2620 else: 2621 actions = self.expressions(expression, key="actions", flat=True) 2622 2623 exists = " IF EXISTS" if expression.args.get("exists") else "" 2624 only = " ONLY" if expression.args.get("only") else "" 2625 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}" 2626 2627 def add_column_sql(self, expression: exp.AlterTable) -> str: 2628 if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD: 2629 return self.expressions( 2630 expression, 2631 key="actions", 2632 prefix="ADD COLUMN ", 2633 ) 2634 return f"ADD {self.expressions(expression, key='actions', flat=True)}" 2635 2636 def droppartition_sql(self, expression: exp.DropPartition) -> str: 2637 expressions = self.expressions(expression) 2638 exists = " IF EXISTS " if expression.args.get("exists") else " " 2639 return f"DROP{exists}{expressions}" 2640 2641 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 2642 this = self.sql(expression, "this") 2643 expression_ = self.sql(expression, "expression") 2644 add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD" 2645 2646 enforced = expression.args.get("enforced") 2647 if enforced is not None: 2648 return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}" 2649 2650 return f"{add_constraint} {expression_}" 2651 2652 def distinct_sql(self, expression: exp.Distinct) -> str: 2653 this = self.expressions(expression, flat=True) 2654 this = f" {this}" if this else "" 2655 2656 on = self.sql(expression, "on") 2657 on = f" ON {on}" if on else "" 2658 return f"DISTINCT{this}{on}" 2659 2660 def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: 2661 return f"{self.sql(expression, 'this')} IGNORE NULLS" 2662 2663 def respectnulls_sql(self, expression: exp.RespectNulls) -> str: 2664 return f"{self.sql(expression, 'this')} RESPECT NULLS" 2665 2666 def intdiv_sql(self, expression: exp.IntDiv) -> str: 2667 return self.sql( 2668 exp.Cast( 2669 this=exp.Div(this=expression.this, expression=expression.expression), 2670 to=exp.DataType(this=exp.DataType.Type.INT), 2671 ) 2672 ) 2673 2674 def dpipe_sql(self, expression: exp.DPipe) -> str: 2675 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2676 return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten())) 2677 return self.binary(expression, "||") 2678 2679 def div_sql(self, expression: exp.Div) -> str: 2680 l, r = expression.left, expression.right 2681 2682 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 2683 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 2684 2685 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 2686 if not l.is_type(*exp.DataType.FLOAT_TYPES) and not r.is_type( 2687 *exp.DataType.FLOAT_TYPES 2688 ): 2689 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 2690 2691 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 2692 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 2693 return self.sql( 2694 exp.cast( 2695 l / r, 2696 to=exp.DataType.Type.BIGINT, 2697 ) 2698 ) 2699 2700 return self.binary(expression, "/") 2701 2702 def overlaps_sql(self, expression: exp.Overlaps) -> str: 2703 return self.binary(expression, "OVERLAPS") 2704 2705 def distance_sql(self, expression: exp.Distance) -> str: 2706 return self.binary(expression, "<->") 2707 2708 def dot_sql(self, expression: exp.Dot) -> str: 2709 return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}" 2710 2711 def eq_sql(self, expression: exp.EQ) -> str: 2712 return self.binary(expression, "=") 2713 2714 def propertyeq_sql(self, expression: exp.PropertyEQ) -> str: 2715 return self.binary(expression, ":=") 2716 2717 def escape_sql(self, expression: exp.Escape) -> str: 2718 return self.binary(expression, "ESCAPE") 2719 2720 def glob_sql(self, expression: exp.Glob) -> str: 2721 return self.binary(expression, "GLOB") 2722 2723 def gt_sql(self, expression: exp.GT) -> str: 2724 return self.binary(expression, ">") 2725 2726 def gte_sql(self, expression: exp.GTE) -> str: 2727 return self.binary(expression, ">=") 2728 2729 def ilike_sql(self, expression: exp.ILike) -> str: 2730 return self.binary(expression, "ILIKE") 2731 2732 def ilikeany_sql(self, expression: exp.ILikeAny) -> str: 2733 return self.binary(expression, "ILIKE ANY") 2734 2735 def is_sql(self, expression: exp.Is) -> str: 2736 if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean): 2737 return self.sql( 2738 expression.this if expression.expression.this else exp.not_(expression.this) 2739 ) 2740 return self.binary(expression, "IS") 2741 2742 def like_sql(self, expression: exp.Like) -> str: 2743 return self.binary(expression, "LIKE") 2744 2745 def likeany_sql(self, expression: exp.LikeAny) -> str: 2746 return self.binary(expression, "LIKE ANY") 2747 2748 def similarto_sql(self, expression: exp.SimilarTo) -> str: 2749 return self.binary(expression, "SIMILAR TO") 2750 2751 def lt_sql(self, expression: exp.LT) -> str: 2752 return self.binary(expression, "<") 2753 2754 def lte_sql(self, expression: exp.LTE) -> str: 2755 return self.binary(expression, "<=") 2756 2757 def mod_sql(self, expression: exp.Mod) -> str: 2758 return self.binary(expression, "%") 2759 2760 def mul_sql(self, expression: exp.Mul) -> str: 2761 return self.binary(expression, "*") 2762 2763 def neq_sql(self, expression: exp.NEQ) -> str: 2764 return self.binary(expression, "<>") 2765 2766 def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str: 2767 return self.binary(expression, "IS NOT DISTINCT FROM") 2768 2769 def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str: 2770 return self.binary(expression, "IS DISTINCT FROM") 2771 2772 def or_sql(self, expression: exp.Or) -> str: 2773 return self.connector_sql(expression, "OR") 2774 2775 def slice_sql(self, expression: exp.Slice) -> str: 2776 return self.binary(expression, ":") 2777 2778 def sub_sql(self, expression: exp.Sub) -> str: 2779 return self.binary(expression, "-") 2780 2781 def trycast_sql(self, expression: exp.TryCast) -> str: 2782 return self.cast_sql(expression, safe_prefix="TRY_") 2783 2784 def log_sql(self, expression: exp.Log) -> str: 2785 this = expression.this 2786 expr = expression.expression 2787 2788 if not self.dialect.LOG_BASE_FIRST: 2789 this, expr = expr, this 2790 2791 return self.func("LOG", this, expr) 2792 2793 def use_sql(self, expression: exp.Use) -> str: 2794 kind = self.sql(expression, "kind") 2795 kind = f" {kind}" if kind else "" 2796 this = self.sql(expression, "this") 2797 this = f" {this}" if this else "" 2798 return f"USE{kind}{this}" 2799 2800 def binary(self, expression: exp.Binary, op: str) -> str: 2801 op = self.maybe_comment(op, comments=expression.comments) 2802 return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}" 2803 2804 def function_fallback_sql(self, expression: exp.Func) -> str: 2805 args = [] 2806 2807 for key in expression.arg_types: 2808 arg_value = expression.args.get(key) 2809 2810 if isinstance(arg_value, list): 2811 for value in arg_value: 2812 args.append(value) 2813 elif arg_value is not None: 2814 args.append(arg_value) 2815 2816 if self.normalize_functions: 2817 name = expression.sql_name() 2818 else: 2819 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 2820 2821 return self.func(name, *args) 2822 2823 def func( 2824 self, 2825 name: str, 2826 *args: t.Optional[exp.Expression | str], 2827 prefix: str = "(", 2828 suffix: str = ")", 2829 ) -> str: 2830 return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}" 2831 2832 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 2833 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 2834 if self.pretty and self.text_width(arg_sqls) > self.max_text_width: 2835 return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 2836 return ", ".join(arg_sqls) 2837 2838 def text_width(self, args: t.Iterable) -> int: 2839 return sum(len(arg) for arg in args) 2840 2841 def format_time(self, expression: exp.Expression) -> t.Optional[str]: 2842 return format_time( 2843 self.sql(expression, "format"), 2844 self.dialect.INVERSE_TIME_MAPPING, 2845 self.dialect.INVERSE_TIME_TRIE, 2846 ) 2847 2848 def expressions( 2849 self, 2850 expression: t.Optional[exp.Expression] = None, 2851 key: t.Optional[str] = None, 2852 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 2853 flat: bool = False, 2854 indent: bool = True, 2855 skip_first: bool = False, 2856 sep: str = ", ", 2857 prefix: str = "", 2858 ) -> str: 2859 expressions = expression.args.get(key or "expressions") if expression else sqls 2860 2861 if not expressions: 2862 return "" 2863 2864 if flat: 2865 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 2866 2867 num_sqls = len(expressions) 2868 2869 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 2870 pad = " " * self.pad 2871 stripped_sep = sep.strip() 2872 2873 result_sqls = [] 2874 for i, e in enumerate(expressions): 2875 sql = self.sql(e, comment=False) 2876 if not sql: 2877 continue 2878 2879 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 2880 2881 if self.pretty: 2882 if self.leading_comma: 2883 result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}") 2884 else: 2885 result_sqls.append( 2886 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 2887 ) 2888 else: 2889 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 2890 2891 result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls) 2892 return self.indent(result_sql, skip_first=skip_first) if indent else result_sql 2893 2894 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 2895 flat = flat or isinstance(expression.parent, exp.Properties) 2896 expressions_sql = self.expressions(expression, flat=flat) 2897 if flat: 2898 return f"{op} {expressions_sql}" 2899 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}" 2900 2901 def naked_property(self, expression: exp.Property) -> str: 2902 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 2903 if not property_name: 2904 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 2905 return f"{property_name} {self.sql(expression, 'this')}" 2906 2907 def set_operation(self, expression: exp.Union, op: str) -> str: 2908 this = self.maybe_comment(self.sql(expression, "this"), comments=expression.comments) 2909 op = self.seg(op) 2910 return self.query_modifiers( 2911 expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}" 2912 ) 2913 2914 def tag_sql(self, expression: exp.Tag) -> str: 2915 return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}" 2916 2917 def token_sql(self, token_type: TokenType) -> str: 2918 return self.TOKEN_MAPPING.get(token_type, token_type.name) 2919 2920 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 2921 this = self.sql(expression, "this") 2922 expressions = self.no_identify(self.expressions, expression) 2923 expressions = ( 2924 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 2925 ) 2926 return f"{this}{expressions}" 2927 2928 def joinhint_sql(self, expression: exp.JoinHint) -> str: 2929 this = self.sql(expression, "this") 2930 expressions = self.expressions(expression, flat=True) 2931 return f"{this}({expressions})" 2932 2933 def kwarg_sql(self, expression: exp.Kwarg) -> str: 2934 return self.binary(expression, "=>") 2935 2936 def when_sql(self, expression: exp.When) -> str: 2937 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 2938 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 2939 condition = self.sql(expression, "condition") 2940 condition = f" AND {condition}" if condition else "" 2941 2942 then_expression = expression.args.get("then") 2943 if isinstance(then_expression, exp.Insert): 2944 then = f"INSERT {self.sql(then_expression, 'this')}" 2945 if "expression" in then_expression.args: 2946 then += f" VALUES {self.sql(then_expression, 'expression')}" 2947 elif isinstance(then_expression, exp.Update): 2948 if isinstance(then_expression.args.get("expressions"), exp.Star): 2949 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 2950 else: 2951 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 2952 else: 2953 then = self.sql(then_expression) 2954 return f"WHEN {matched}{source}{condition} THEN {then}" 2955 2956 def merge_sql(self, expression: exp.Merge) -> str: 2957 table = expression.this 2958 table_alias = "" 2959 2960 hints = table.args.get("hints") 2961 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 2962 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 2963 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 2964 2965 this = self.sql(table) 2966 using = f"USING {self.sql(expression, 'using')}" 2967 on = f"ON {self.sql(expression, 'on')}" 2968 expressions = self.expressions(expression, sep=" ") 2969 2970 return self.prepend_ctes( 2971 expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}" 2972 ) 2973 2974 def tochar_sql(self, expression: exp.ToChar) -> str: 2975 if expression.args.get("format"): 2976 self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function") 2977 2978 return self.sql(exp.cast(expression.this, "text")) 2979 2980 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 2981 this = self.sql(expression, "this") 2982 kind = self.sql(expression, "kind") 2983 settings_sql = self.expressions(expression, key="settings", sep=" ") 2984 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 2985 return f"{this}({kind}{args})" 2986 2987 def dictrange_sql(self, expression: exp.DictRange) -> str: 2988 this = self.sql(expression, "this") 2989 max = self.sql(expression, "max") 2990 min = self.sql(expression, "min") 2991 return f"{this}(MIN {min} MAX {max})" 2992 2993 def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str: 2994 return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}" 2995 2996 def oncluster_sql(self, expression: exp.OnCluster) -> str: 2997 return "" 2998 2999 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3000 expressions = self.expressions(expression, key="expressions", flat=True) 3001 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3002 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3003 buckets = self.sql(expression, "buckets") 3004 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS" 3005 3006 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3007 this = self.sql(expression, "this") 3008 having = self.sql(expression, "having") 3009 3010 if having: 3011 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3012 3013 return self.func("ANY_VALUE", this) 3014 3015 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3016 transform = self.func("TRANSFORM", *expression.expressions) 3017 row_format_before = self.sql(expression, "row_format_before") 3018 row_format_before = f" {row_format_before}" if row_format_before else "" 3019 record_writer = self.sql(expression, "record_writer") 3020 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3021 using = f" USING {self.sql(expression, 'command_script')}" 3022 schema = self.sql(expression, "schema") 3023 schema = f" AS {schema}" if schema else "" 3024 row_format_after = self.sql(expression, "row_format_after") 3025 row_format_after = f" {row_format_after}" if row_format_after else "" 3026 record_reader = self.sql(expression, "record_reader") 3027 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3028 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}" 3029 3030 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3031 key_block_size = self.sql(expression, "key_block_size") 3032 if key_block_size: 3033 return f"KEY_BLOCK_SIZE = {key_block_size}" 3034 3035 using = self.sql(expression, "using") 3036 if using: 3037 return f"USING {using}" 3038 3039 parser = self.sql(expression, "parser") 3040 if parser: 3041 return f"WITH PARSER {parser}" 3042 3043 comment = self.sql(expression, "comment") 3044 if comment: 3045 return f"COMMENT {comment}" 3046 3047 visible = expression.args.get("visible") 3048 if visible is not None: 3049 return "VISIBLE" if visible else "INVISIBLE" 3050 3051 engine_attr = self.sql(expression, "engine_attr") 3052 if engine_attr: 3053 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3054 3055 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3056 if secondary_engine_attr: 3057 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3058 3059 self.unsupported("Unsupported index constraint option.") 3060 return "" 3061 3062 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3063 kind = self.sql(expression, "kind") 3064 kind = f"{kind} INDEX" if kind else "INDEX" 3065 this = self.sql(expression, "this") 3066 this = f" {this}" if this else "" 3067 index_type = self.sql(expression, "index_type") 3068 index_type = f" USING {index_type}" if index_type else "" 3069 schema = self.sql(expression, "schema") 3070 schema = f" {schema}" if schema else "" 3071 options = self.expressions(expression, key="options", sep=" ") 3072 options = f" {options}" if options else "" 3073 return f"{kind}{this}{index_type}{schema}{options}" 3074 3075 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3076 if self.NVL2_SUPPORTED: 3077 return self.function_fallback_sql(expression) 3078 3079 case = exp.Case().when( 3080 expression.this.is_(exp.null()).not_(copy=False), 3081 expression.args["true"], 3082 copy=False, 3083 ) 3084 else_cond = expression.args.get("false") 3085 if else_cond: 3086 case.else_(else_cond, copy=False) 3087 3088 return self.sql(case) 3089 3090 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3091 this = self.sql(expression, "this") 3092 expr = self.sql(expression, "expression") 3093 iterator = self.sql(expression, "iterator") 3094 condition = self.sql(expression, "condition") 3095 condition = f" IF {condition}" if condition else "" 3096 return f"{this} FOR {expr} IN {iterator}{condition}" 3097 3098 def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str: 3099 return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})" 3100 3101 def opclass_sql(self, expression: exp.Opclass) -> str: 3102 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 3103 3104 def predict_sql(self, expression: exp.Predict) -> str: 3105 model = self.sql(expression, "this") 3106 model = f"MODEL {model}" 3107 table = self.sql(expression, "expression") 3108 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3109 parameters = self.sql(expression, "params_struct") 3110 return self.func("PREDICT", model, table, parameters or None) 3111 3112 def forin_sql(self, expression: exp.ForIn) -> str: 3113 this = self.sql(expression, "this") 3114 expression_sql = self.sql(expression, "expression") 3115 return f"FOR {this} DO {expression_sql}" 3116 3117 def refresh_sql(self, expression: exp.Refresh) -> str: 3118 this = self.sql(expression, "this") 3119 table = "" if isinstance(expression.this, exp.Literal) else "TABLE " 3120 return f"REFRESH {table}{this}" 3121 3122 def operator_sql(self, expression: exp.Operator) -> str: 3123 return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})") 3124 3125 def toarray_sql(self, expression: exp.ToArray) -> str: 3126 arg = expression.this 3127 if not arg.type: 3128 from sqlglot.optimizer.annotate_types import annotate_types 3129 3130 arg = annotate_types(arg) 3131 3132 if arg.is_type(exp.DataType.Type.ARRAY): 3133 return self.sql(arg) 3134 3135 cond_for_null = arg.is_(exp.null()) 3136 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.Array(expressions=[arg]))) 3137 3138 def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str: 3139 this = expression.this 3140 if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME): 3141 return self.sql(this) 3142 3143 return self.sql(exp.cast(this, "time")) 3144 3145 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3146 this = expression.this 3147 time_format = self.format_time(expression) 3148 3149 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3150 return self.sql( 3151 exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date") 3152 ) 3153 3154 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3155 return self.sql(this) 3156 3157 return self.sql(exp.cast(this, "date")) 3158 3159 def unixdate_sql(self, expression: exp.UnixDate) -> str: 3160 return self.sql( 3161 exp.func( 3162 "DATEDIFF", 3163 expression.this, 3164 exp.cast(exp.Literal.string("1970-01-01"), "date"), 3165 "day", 3166 ) 3167 ) 3168 3169 def lastday_sql(self, expression: exp.LastDay) -> str: 3170 if self.LAST_DAY_SUPPORTS_DATE_PART: 3171 return self.function_fallback_sql(expression) 3172 3173 unit = expression.text("unit") 3174 if unit and unit != "MONTH": 3175 self.unsupported("Date parts are not supported in LAST_DAY.") 3176 3177 return self.func("LAST_DAY", expression.this) 3178 3179 def _simplify_unless_literal(self, expression: E) -> E: 3180 if not isinstance(expression, exp.Literal): 3181 from sqlglot.optimizer.simplify import simplify 3182 3183 expression = simplify(expression, dialect=self.dialect) 3184 3185 return expression 3186 3187 def _ensure_string_if_null(self, values: t.List[exp.Expression]) -> t.List[exp.Expression]: 3188 return [ 3189 exp.func("COALESCE", exp.cast(value, "text"), exp.Literal.string("")) 3190 for value in values 3191 if value 3192 ]
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 = expression.this 1691 this_sql = self.sql(this) 1692 1693 if on_sql: 1694 on_sql = self.indent(on_sql, skip_first=True) 1695 space = self.seg(" " * self.pad) if self.pretty else " " 1696 if using: 1697 on_sql = f"{space}USING ({on_sql})" 1698 else: 1699 on_sql = f"{space}ON {on_sql}" 1700 elif not op_sql: 1701 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 1702 return f" {this_sql}" 1703 1704 return f", {this_sql}" 1705 1706 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1707 return f"{self.seg(op_sql)} {this_sql}{on_sql}" 1708 1709 def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str: 1710 args = self.expressions(expression, flat=True) 1711 args = f"({args})" if len(args.split(",")) > 1 else args 1712 return f"{args} {arrow_sep} {self.sql(expression, 'this')}" 1713 1714 def lateral_op(self, expression: exp.Lateral) -> str: 1715 cross_apply = expression.args.get("cross_apply") 1716 1717 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 1718 if cross_apply is True: 1719 op = "INNER JOIN " 1720 elif cross_apply is False: 1721 op = "LEFT JOIN " 1722 else: 1723 op = "" 1724 1725 return f"{op}LATERAL" 1726 1727 def lateral_sql(self, expression: exp.Lateral) -> str: 1728 this = self.sql(expression, "this") 1729 1730 if expression.args.get("view"): 1731 alias = expression.args["alias"] 1732 columns = self.expressions(alias, key="columns", flat=True) 1733 table = f" {alias.name}" if alias.name else "" 1734 columns = f" AS {columns}" if columns else "" 1735 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1736 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1737 1738 alias = self.sql(expression, "alias") 1739 alias = f" AS {alias}" if alias else "" 1740 return f"{self.lateral_op(expression)} {this}{alias}" 1741 1742 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1743 this = self.sql(expression, "this") 1744 1745 args = [ 1746 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1747 for e in (expression.args.get(k) for k in ("offset", "expression")) 1748 if e 1749 ] 1750 1751 args_sql = ", ".join(self.sql(e) for e in args) 1752 args_sql = f"({args_sql})" if any(top and not e.is_number for e in args) else args_sql 1753 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}" 1754 1755 def offset_sql(self, expression: exp.Offset) -> str: 1756 this = self.sql(expression, "this") 1757 expression = expression.expression 1758 expression = ( 1759 self._simplify_unless_literal(expression) if self.LIMIT_ONLY_LITERALS else expression 1760 ) 1761 return f"{this}{self.seg('OFFSET')} {self.sql(expression)}" 1762 1763 def setitem_sql(self, expression: exp.SetItem) -> str: 1764 kind = self.sql(expression, "kind") 1765 kind = f"{kind} " if kind else "" 1766 this = self.sql(expression, "this") 1767 expressions = self.expressions(expression) 1768 collate = self.sql(expression, "collate") 1769 collate = f" COLLATE {collate}" if collate else "" 1770 global_ = "GLOBAL " if expression.args.get("global") else "" 1771 return f"{global_}{kind}{this}{expressions}{collate}" 1772 1773 def set_sql(self, expression: exp.Set) -> str: 1774 expressions = ( 1775 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 1776 ) 1777 tag = " TAG" if expression.args.get("tag") else "" 1778 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}" 1779 1780 def pragma_sql(self, expression: exp.Pragma) -> str: 1781 return f"PRAGMA {self.sql(expression, 'this')}" 1782 1783 def lock_sql(self, expression: exp.Lock) -> str: 1784 if not self.LOCKING_READS_SUPPORTED: 1785 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 1786 return "" 1787 1788 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 1789 expressions = self.expressions(expression, flat=True) 1790 expressions = f" OF {expressions}" if expressions else "" 1791 wait = expression.args.get("wait") 1792 1793 if wait is not None: 1794 if isinstance(wait, exp.Literal): 1795 wait = f" WAIT {self.sql(wait)}" 1796 else: 1797 wait = " NOWAIT" if wait else " SKIP LOCKED" 1798 1799 return f"{lock_type}{expressions}{wait or ''}" 1800 1801 def literal_sql(self, expression: exp.Literal) -> str: 1802 text = expression.this or "" 1803 if expression.is_string: 1804 text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}" 1805 return text 1806 1807 def escape_str(self, text: str) -> str: 1808 text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end) 1809 if self.dialect.INVERSE_ESCAPE_SEQUENCES: 1810 text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text) 1811 elif self.pretty: 1812 text = text.replace("\n", self.SENTINEL_LINE_BREAK) 1813 return text 1814 1815 def loaddata_sql(self, expression: exp.LoadData) -> str: 1816 local = " LOCAL" if expression.args.get("local") else "" 1817 inpath = f" INPATH {self.sql(expression, 'inpath')}" 1818 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 1819 this = f" INTO TABLE {self.sql(expression, 'this')}" 1820 partition = self.sql(expression, "partition") 1821 partition = f" {partition}" if partition else "" 1822 input_format = self.sql(expression, "input_format") 1823 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 1824 serde = self.sql(expression, "serde") 1825 serde = f" SERDE {serde}" if serde else "" 1826 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}" 1827 1828 def null_sql(self, *_) -> str: 1829 return "NULL" 1830 1831 def boolean_sql(self, expression: exp.Boolean) -> str: 1832 return "TRUE" if expression.this else "FALSE" 1833 1834 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 1835 this = self.sql(expression, "this") 1836 this = f"{this} " if this else this 1837 order = self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat) # type: ignore 1838 interpolated_values = [ 1839 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 1840 for named_expression in expression.args.get("interpolate") or [] 1841 ] 1842 interpolate = ( 1843 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 1844 ) 1845 return f"{order}{interpolate}" 1846 1847 def withfill_sql(self, expression: exp.WithFill) -> str: 1848 from_sql = self.sql(expression, "from") 1849 from_sql = f" FROM {from_sql}" if from_sql else "" 1850 to_sql = self.sql(expression, "to") 1851 to_sql = f" TO {to_sql}" if to_sql else "" 1852 step_sql = self.sql(expression, "step") 1853 step_sql = f" STEP {step_sql}" if step_sql else "" 1854 return f"WITH FILL{from_sql}{to_sql}{step_sql}" 1855 1856 def cluster_sql(self, expression: exp.Cluster) -> str: 1857 return self.op_expressions("CLUSTER BY", expression) 1858 1859 def distribute_sql(self, expression: exp.Distribute) -> str: 1860 return self.op_expressions("DISTRIBUTE BY", expression) 1861 1862 def sort_sql(self, expression: exp.Sort) -> str: 1863 return self.op_expressions("SORT BY", expression) 1864 1865 def ordered_sql(self, expression: exp.Ordered) -> str: 1866 desc = expression.args.get("desc") 1867 asc = not desc 1868 1869 nulls_first = expression.args.get("nulls_first") 1870 nulls_last = not nulls_first 1871 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 1872 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 1873 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 1874 1875 this = self.sql(expression, "this") 1876 1877 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 1878 nulls_sort_change = "" 1879 if nulls_first and ( 1880 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 1881 ): 1882 nulls_sort_change = " NULLS FIRST" 1883 elif ( 1884 nulls_last 1885 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 1886 and not nulls_are_last 1887 ): 1888 nulls_sort_change = " NULLS LAST" 1889 1890 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 1891 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 1892 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 1893 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 1894 nulls_sort_change = "" 1895 1896 with_fill = self.sql(expression, "with_fill") 1897 with_fill = f" {with_fill}" if with_fill else "" 1898 1899 return f"{this}{sort_order}{nulls_sort_change}{with_fill}" 1900 1901 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 1902 partition = self.partition_by_sql(expression) 1903 order = self.sql(expression, "order") 1904 measures = self.expressions(expression, key="measures") 1905 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 1906 rows = self.sql(expression, "rows") 1907 rows = self.seg(rows) if rows else "" 1908 after = self.sql(expression, "after") 1909 after = self.seg(after) if after else "" 1910 pattern = self.sql(expression, "pattern") 1911 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 1912 definition_sqls = [ 1913 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 1914 for definition in expression.args.get("define", []) 1915 ] 1916 definitions = self.expressions(sqls=definition_sqls) 1917 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 1918 body = "".join( 1919 ( 1920 partition, 1921 order, 1922 measures, 1923 rows, 1924 after, 1925 pattern, 1926 define, 1927 ) 1928 ) 1929 alias = self.sql(expression, "alias") 1930 alias = f" {alias}" if alias else "" 1931 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}" 1932 1933 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 1934 limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit") 1935 1936 # If the limit is generated as TOP, we need to ensure it's not generated twice 1937 with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP 1938 1939 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 1940 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 1941 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 1942 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 1943 1944 fetch = isinstance(limit, exp.Fetch) 1945 1946 offset_limit_modifiers = ( 1947 self.offset_limit_modifiers(expression, fetch, limit) 1948 if with_offset_limit_modifiers 1949 else [] 1950 ) 1951 1952 return csv( 1953 *sqls, 1954 *[self.sql(join) for join in expression.args.get("joins") or []], 1955 self.sql(expression, "connect"), 1956 self.sql(expression, "match"), 1957 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 1958 self.sql(expression, "where"), 1959 self.sql(expression, "group"), 1960 self.sql(expression, "having"), 1961 *self.after_having_modifiers(expression), 1962 self.sql(expression, "order"), 1963 *offset_limit_modifiers, 1964 *self.after_limit_modifiers(expression), 1965 sep="", 1966 ) 1967 1968 def offset_limit_modifiers( 1969 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 1970 ) -> t.List[str]: 1971 return [ 1972 self.sql(expression, "offset") if fetch else self.sql(limit), 1973 self.sql(limit) if fetch else self.sql(expression, "offset"), 1974 ] 1975 1976 def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]: 1977 return [ 1978 self.sql(expression, "qualify"), 1979 self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True) 1980 if expression.args.get("windows") 1981 else "", 1982 self.sql(expression, "distribute"), 1983 self.sql(expression, "sort"), 1984 self.sql(expression, "cluster"), 1985 ] 1986 1987 def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]: 1988 locks = self.expressions(expression, key="locks", sep=" ") 1989 locks = f" {locks}" if locks else "" 1990 return [locks, self.sql(expression, "sample")] 1991 1992 def select_sql(self, expression: exp.Select) -> str: 1993 hint = self.sql(expression, "hint") 1994 distinct = self.sql(expression, "distinct") 1995 distinct = f" {distinct}" if distinct else "" 1996 kind = self.sql(expression, "kind") 1997 limit = expression.args.get("limit") 1998 top = ( 1999 self.limit_sql(limit, top=True) 2000 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP 2001 else "" 2002 ) 2003 2004 expressions = self.expressions(expression) 2005 2006 if kind: 2007 if kind in self.SELECT_KINDS: 2008 kind = f" AS {kind}" 2009 else: 2010 if kind == "STRUCT": 2011 expressions = self.expressions( 2012 sqls=[ 2013 self.sql( 2014 exp.Struct( 2015 expressions=[ 2016 exp.column(e.output_name).eq( 2017 e.this if isinstance(e, exp.Alias) else e 2018 ) 2019 for e in expression.expressions 2020 ] 2021 ) 2022 ) 2023 ] 2024 ) 2025 kind = "" 2026 2027 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2028 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2029 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2030 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2031 sql = self.query_modifiers( 2032 expression, 2033 f"SELECT{top_distinct}{kind}{expressions}", 2034 self.sql(expression, "into", comment=False), 2035 self.sql(expression, "from", comment=False), 2036 ) 2037 return self.prepend_ctes(expression, sql) 2038 2039 def schema_sql(self, expression: exp.Schema) -> str: 2040 this = self.sql(expression, "this") 2041 sql = self.schema_columns_sql(expression) 2042 return f"{this} {sql}" if this and sql else this or sql 2043 2044 def schema_columns_sql(self, expression: exp.Schema) -> str: 2045 if expression.expressions: 2046 return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}" 2047 return "" 2048 2049 def star_sql(self, expression: exp.Star) -> str: 2050 except_ = self.expressions(expression, key="except", flat=True) 2051 except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else "" 2052 replace = self.expressions(expression, key="replace", flat=True) 2053 replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else "" 2054 return f"*{except_}{replace}" 2055 2056 def parameter_sql(self, expression: exp.Parameter) -> str: 2057 this = self.sql(expression, "this") 2058 return f"{self.PARAMETER_TOKEN}{this}" 2059 2060 def sessionparameter_sql(self, expression: exp.SessionParameter) -> str: 2061 this = self.sql(expression, "this") 2062 kind = expression.text("kind") 2063 if kind: 2064 kind = f"{kind}." 2065 return f"@@{kind}{this}" 2066 2067 def placeholder_sql(self, expression: exp.Placeholder) -> str: 2068 return f":{expression.name}" if expression.name else "?" 2069 2070 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2071 alias = self.sql(expression, "alias") 2072 alias = f"{sep}{alias}" if alias else "" 2073 2074 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2075 pivots = f" {pivots}" if pivots else "" 2076 2077 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2078 return self.prepend_ctes(expression, sql) 2079 2080 def qualify_sql(self, expression: exp.Qualify) -> str: 2081 this = self.indent(self.sql(expression, "this")) 2082 return f"{self.seg('QUALIFY')}{self.sep()}{this}" 2083 2084 def union_sql(self, expression: exp.Union) -> str: 2085 return self.prepend_ctes( 2086 expression, 2087 self.set_operation(expression, self.union_op(expression)), 2088 ) 2089 2090 def union_op(self, expression: exp.Union) -> str: 2091 kind = " DISTINCT" if self.EXPLICIT_UNION else "" 2092 kind = kind if expression.args.get("distinct") else " ALL" 2093 by_name = " BY NAME" if expression.args.get("by_name") else "" 2094 return f"UNION{kind}{by_name}" 2095 2096 def unnest_sql(self, expression: exp.Unnest) -> str: 2097 args = self.expressions(expression, flat=True) 2098 2099 alias = expression.args.get("alias") 2100 offset = expression.args.get("offset") 2101 2102 if self.UNNEST_WITH_ORDINALITY: 2103 if alias and isinstance(offset, exp.Expression): 2104 alias.append("columns", offset) 2105 2106 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2107 columns = alias.columns 2108 alias = self.sql(columns[0]) if columns else "" 2109 else: 2110 alias = self.sql(alias) 2111 2112 alias = f" AS {alias}" if alias else alias 2113 if self.UNNEST_WITH_ORDINALITY: 2114 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2115 else: 2116 if isinstance(offset, exp.Expression): 2117 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2118 elif offset: 2119 suffix = f"{alias} WITH OFFSET" 2120 else: 2121 suffix = alias 2122 2123 return f"UNNEST({args}){suffix}" 2124 2125 def where_sql(self, expression: exp.Where) -> str: 2126 this = self.indent(self.sql(expression, "this")) 2127 return f"{self.seg('WHERE')}{self.sep()}{this}" 2128 2129 def window_sql(self, expression: exp.Window) -> str: 2130 this = self.sql(expression, "this") 2131 partition = self.partition_by_sql(expression) 2132 order = expression.args.get("order") 2133 order = self.order_sql(order, flat=True) if order else "" 2134 spec = self.sql(expression, "spec") 2135 alias = self.sql(expression, "alias") 2136 over = self.sql(expression, "over") or "OVER" 2137 2138 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2139 2140 first = expression.args.get("first") 2141 if first is None: 2142 first = "" 2143 else: 2144 first = "FIRST" if first else "LAST" 2145 2146 if not partition and not order and not spec and alias: 2147 return f"{this} {alias}" 2148 2149 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2150 return f"{this} ({args})" 2151 2152 def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str: 2153 partition = self.expressions(expression, key="partition_by", flat=True) 2154 return f"PARTITION BY {partition}" if partition else "" 2155 2156 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2157 kind = self.sql(expression, "kind") 2158 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2159 end = ( 2160 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2161 or "CURRENT ROW" 2162 ) 2163 return f"{kind} BETWEEN {start} AND {end}" 2164 2165 def withingroup_sql(self, expression: exp.WithinGroup) -> str: 2166 this = self.sql(expression, "this") 2167 expression_sql = self.sql(expression, "expression")[1:] # order has a leading space 2168 return f"{this} WITHIN GROUP ({expression_sql})" 2169 2170 def between_sql(self, expression: exp.Between) -> str: 2171 this = self.sql(expression, "this") 2172 low = self.sql(expression, "low") 2173 high = self.sql(expression, "high") 2174 return f"{this} BETWEEN {low} AND {high}" 2175 2176 def bracket_sql(self, expression: exp.Bracket) -> str: 2177 expressions = apply_index_offset( 2178 expression.this, 2179 expression.expressions, 2180 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2181 ) 2182 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2183 return f"{self.sql(expression, 'this')}[{expressions_sql}]" 2184 2185 def all_sql(self, expression: exp.All) -> str: 2186 return f"ALL {self.wrap(expression)}" 2187 2188 def any_sql(self, expression: exp.Any) -> str: 2189 this = self.sql(expression, "this") 2190 if isinstance(expression.this, exp.Subqueryable): 2191 this = self.wrap(this) 2192 return f"ANY {this}" 2193 2194 def exists_sql(self, expression: exp.Exists) -> str: 2195 return f"EXISTS{self.wrap(expression)}" 2196 2197 def case_sql(self, expression: exp.Case) -> str: 2198 this = self.sql(expression, "this") 2199 statements = [f"CASE {this}" if this else "CASE"] 2200 2201 for e in expression.args["ifs"]: 2202 statements.append(f"WHEN {self.sql(e, 'this')}") 2203 statements.append(f"THEN {self.sql(e, 'true')}") 2204 2205 default = self.sql(expression, "default") 2206 2207 if default: 2208 statements.append(f"ELSE {default}") 2209 2210 statements.append("END") 2211 2212 if self.pretty and self.text_width(statements) > self.max_text_width: 2213 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2214 2215 return " ".join(statements) 2216 2217 def constraint_sql(self, expression: exp.Constraint) -> str: 2218 this = self.sql(expression, "this") 2219 expressions = self.expressions(expression, flat=True) 2220 return f"CONSTRAINT {this} {expressions}" 2221 2222 def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str: 2223 order = expression.args.get("order") 2224 order = f" OVER ({self.order_sql(order, flat=True)})" if order else "" 2225 return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}" 2226 2227 def extract_sql(self, expression: exp.Extract) -> str: 2228 this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name 2229 expression_sql = self.sql(expression, "expression") 2230 return f"EXTRACT({this} FROM {expression_sql})" 2231 2232 def trim_sql(self, expression: exp.Trim) -> str: 2233 trim_type = self.sql(expression, "position") 2234 2235 if trim_type == "LEADING": 2236 return self.func("LTRIM", expression.this) 2237 elif trim_type == "TRAILING": 2238 return self.func("RTRIM", expression.this) 2239 else: 2240 return self.func("TRIM", expression.this, expression.expression) 2241 2242 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2243 args = expression.expressions 2244 if isinstance(expression, exp.ConcatWs): 2245 args = args[1:] # Skip the delimiter 2246 2247 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2248 args = [exp.cast(e, "text") for e in args] 2249 2250 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2251 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2252 2253 return args 2254 2255 def concat_sql(self, expression: exp.Concat) -> str: 2256 expressions = self.convert_concat_args(expression) 2257 2258 # Some dialects don't allow a single-argument CONCAT call 2259 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2260 return self.sql(expressions[0]) 2261 2262 return self.func("CONCAT", *expressions) 2263 2264 def concatws_sql(self, expression: exp.ConcatWs) -> str: 2265 return self.func( 2266 "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression) 2267 ) 2268 2269 def check_sql(self, expression: exp.Check) -> str: 2270 this = self.sql(expression, key="this") 2271 return f"CHECK ({this})" 2272 2273 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2274 expressions = self.expressions(expression, flat=True) 2275 reference = self.sql(expression, "reference") 2276 reference = f" {reference}" if reference else "" 2277 delete = self.sql(expression, "delete") 2278 delete = f" ON DELETE {delete}" if delete else "" 2279 update = self.sql(expression, "update") 2280 update = f" ON UPDATE {update}" if update else "" 2281 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}" 2282 2283 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2284 expressions = self.expressions(expression, flat=True) 2285 options = self.expressions(expression, key="options", flat=True, sep=" ") 2286 options = f" {options}" if options else "" 2287 return f"PRIMARY KEY ({expressions}){options}" 2288 2289 def if_sql(self, expression: exp.If) -> str: 2290 return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false"))) 2291 2292 def matchagainst_sql(self, expression: exp.MatchAgainst) -> str: 2293 modifier = expression.args.get("modifier") 2294 modifier = f" {modifier}" if modifier else "" 2295 return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})" 2296 2297 def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str: 2298 return f"{self.sql(expression, 'this')}: {self.sql(expression, 'expression')}" 2299 2300 def formatjson_sql(self, expression: exp.FormatJson) -> str: 2301 return f"{self.sql(expression, 'this')} FORMAT JSON" 2302 2303 def jsonobject_sql(self, expression: exp.JSONObject) -> str: 2304 null_handling = expression.args.get("null_handling") 2305 null_handling = f" {null_handling}" if null_handling else "" 2306 unique_keys = expression.args.get("unique_keys") 2307 if unique_keys is not None: 2308 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2309 else: 2310 unique_keys = "" 2311 return_type = self.sql(expression, "return_type") 2312 return_type = f" RETURNING {return_type}" if return_type else "" 2313 encoding = self.sql(expression, "encoding") 2314 encoding = f" ENCODING {encoding}" if encoding else "" 2315 return self.func( 2316 "JSON_OBJECT", 2317 *expression.expressions, 2318 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2319 ) 2320 2321 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2322 null_handling = expression.args.get("null_handling") 2323 null_handling = f" {null_handling}" if null_handling else "" 2324 return_type = self.sql(expression, "return_type") 2325 return_type = f" RETURNING {return_type}" if return_type else "" 2326 strict = " STRICT" if expression.args.get("strict") else "" 2327 return self.func( 2328 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2329 ) 2330 2331 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2332 this = self.sql(expression, "this") 2333 order = self.sql(expression, "order") 2334 null_handling = expression.args.get("null_handling") 2335 null_handling = f" {null_handling}" if null_handling else "" 2336 return_type = self.sql(expression, "return_type") 2337 return_type = f" RETURNING {return_type}" if return_type else "" 2338 strict = " STRICT" if expression.args.get("strict") else "" 2339 return self.func( 2340 "JSON_ARRAYAGG", 2341 this, 2342 suffix=f"{order}{null_handling}{return_type}{strict})", 2343 ) 2344 2345 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2346 path = self.sql(expression, "path") 2347 path = f" PATH {path}" if path else "" 2348 nested_schema = self.sql(expression, "nested_schema") 2349 2350 if nested_schema: 2351 return f"NESTED{path} {nested_schema}" 2352 2353 this = self.sql(expression, "this") 2354 kind = self.sql(expression, "kind") 2355 kind = f" {kind}" if kind else "" 2356 return f"{this}{kind}{path}" 2357 2358 def jsonschema_sql(self, expression: exp.JSONSchema) -> str: 2359 return self.func("COLUMNS", *expression.expressions) 2360 2361 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2362 this = self.sql(expression, "this") 2363 path = self.sql(expression, "path") 2364 path = f", {path}" if path else "" 2365 error_handling = expression.args.get("error_handling") 2366 error_handling = f" {error_handling}" if error_handling else "" 2367 empty_handling = expression.args.get("empty_handling") 2368 empty_handling = f" {empty_handling}" if empty_handling else "" 2369 schema = self.sql(expression, "schema") 2370 return self.func( 2371 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2372 ) 2373 2374 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2375 this = self.sql(expression, "this") 2376 kind = self.sql(expression, "kind") 2377 path = self.sql(expression, "path") 2378 path = f" {path}" if path else "" 2379 as_json = " AS JSON" if expression.args.get("as_json") else "" 2380 return f"{this} {kind}{path}{as_json}" 2381 2382 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2383 this = self.sql(expression, "this") 2384 path = self.sql(expression, "path") 2385 path = f", {path}" if path else "" 2386 expressions = self.expressions(expression) 2387 with_ = ( 2388 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2389 if expressions 2390 else "" 2391 ) 2392 return f"OPENJSON({this}{path}){with_}" 2393 2394 def in_sql(self, expression: exp.In) -> str: 2395 query = expression.args.get("query") 2396 unnest = expression.args.get("unnest") 2397 field = expression.args.get("field") 2398 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2399 2400 if query: 2401 in_sql = self.wrap(query) 2402 elif unnest: 2403 in_sql = self.in_unnest_op(unnest) 2404 elif field: 2405 in_sql = self.sql(field) 2406 else: 2407 in_sql = f"({self.expressions(expression, flat=True)})" 2408 2409 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}" 2410 2411 def in_unnest_op(self, unnest: exp.Unnest) -> str: 2412 return f"(SELECT {self.sql(unnest)})" 2413 2414 def interval_sql(self, expression: exp.Interval) -> str: 2415 unit = self.sql(expression, "unit") 2416 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2417 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2418 unit = f" {unit}" if unit else "" 2419 2420 if self.SINGLE_STRING_INTERVAL: 2421 this = expression.this.name if expression.this else "" 2422 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2423 2424 this = self.sql(expression, "this") 2425 if this: 2426 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2427 this = f" {this}" if unwrapped else f" ({this})" 2428 2429 return f"INTERVAL{this}{unit}" 2430 2431 def return_sql(self, expression: exp.Return) -> str: 2432 return f"RETURN {self.sql(expression, 'this')}" 2433 2434 def reference_sql(self, expression: exp.Reference) -> str: 2435 this = self.sql(expression, "this") 2436 expressions = self.expressions(expression, flat=True) 2437 expressions = f"({expressions})" if expressions else "" 2438 options = self.expressions(expression, key="options", flat=True, sep=" ") 2439 options = f" {options}" if options else "" 2440 return f"REFERENCES {this}{expressions}{options}" 2441 2442 def anonymous_sql(self, expression: exp.Anonymous) -> str: 2443 return self.func(expression.name, *expression.expressions) 2444 2445 def paren_sql(self, expression: exp.Paren) -> str: 2446 if isinstance(expression.unnest(), exp.Select): 2447 sql = self.wrap(expression) 2448 else: 2449 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2450 sql = f"({sql}{self.seg(')', sep='')}" 2451 2452 return self.prepend_ctes(expression, sql) 2453 2454 def neg_sql(self, expression: exp.Neg) -> str: 2455 # This makes sure we don't convert "- - 5" to "--5", which is a comment 2456 this_sql = self.sql(expression, "this") 2457 sep = " " if this_sql[0] == "-" else "" 2458 return f"-{sep}{this_sql}" 2459 2460 def not_sql(self, expression: exp.Not) -> str: 2461 return f"NOT {self.sql(expression, 'this')}" 2462 2463 def alias_sql(self, expression: exp.Alias) -> str: 2464 alias = self.sql(expression, "alias") 2465 alias = f" AS {alias}" if alias else "" 2466 return f"{self.sql(expression, 'this')}{alias}" 2467 2468 def aliases_sql(self, expression: exp.Aliases) -> str: 2469 return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})" 2470 2471 def atindex_sql(self, expression: exp.AtTimeZone) -> str: 2472 this = self.sql(expression, "this") 2473 index = self.sql(expression, "expression") 2474 return f"{this} AT {index}" 2475 2476 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 2477 this = self.sql(expression, "this") 2478 zone = self.sql(expression, "zone") 2479 return f"{this} AT TIME ZONE {zone}" 2480 2481 def add_sql(self, expression: exp.Add) -> str: 2482 return self.binary(expression, "+") 2483 2484 def and_sql(self, expression: exp.And) -> str: 2485 return self.connector_sql(expression, "AND") 2486 2487 def xor_sql(self, expression: exp.Xor) -> str: 2488 return self.connector_sql(expression, "XOR") 2489 2490 def connector_sql(self, expression: exp.Connector, op: str) -> str: 2491 if not self.pretty: 2492 return self.binary(expression, op) 2493 2494 sqls = tuple( 2495 self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e) 2496 for i, e in enumerate(expression.flatten(unnest=False)) 2497 ) 2498 2499 sep = "\n" if self.text_width(sqls) > self.max_text_width else " " 2500 return f"{sep}{op} ".join(sqls) 2501 2502 def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str: 2503 return self.binary(expression, "&") 2504 2505 def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str: 2506 return self.binary(expression, "<<") 2507 2508 def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str: 2509 return f"~{self.sql(expression, 'this')}" 2510 2511 def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str: 2512 return self.binary(expression, "|") 2513 2514 def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str: 2515 return self.binary(expression, ">>") 2516 2517 def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str: 2518 return self.binary(expression, "^") 2519 2520 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2521 format_sql = self.sql(expression, "format") 2522 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2523 to_sql = self.sql(expression, "to") 2524 to_sql = f" {to_sql}" if to_sql else "" 2525 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})" 2526 2527 def currentdate_sql(self, expression: exp.CurrentDate) -> str: 2528 zone = self.sql(expression, "this") 2529 return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE" 2530 2531 def collate_sql(self, expression: exp.Collate) -> str: 2532 if self.COLLATE_IS_FUNC: 2533 return self.function_fallback_sql(expression) 2534 return self.binary(expression, "COLLATE") 2535 2536 def command_sql(self, expression: exp.Command) -> str: 2537 return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}" 2538 2539 def comment_sql(self, expression: exp.Comment) -> str: 2540 this = self.sql(expression, "this") 2541 kind = expression.args["kind"] 2542 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2543 expression_sql = self.sql(expression, "expression") 2544 return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}" 2545 2546 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2547 this = self.sql(expression, "this") 2548 delete = " DELETE" if expression.args.get("delete") else "" 2549 recompress = self.sql(expression, "recompress") 2550 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2551 to_disk = self.sql(expression, "to_disk") 2552 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2553 to_volume = self.sql(expression, "to_volume") 2554 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2555 return f"{this}{delete}{recompress}{to_disk}{to_volume}" 2556 2557 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2558 where = self.sql(expression, "where") 2559 group = self.sql(expression, "group") 2560 aggregates = self.expressions(expression, key="aggregates") 2561 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2562 2563 if not (where or group or aggregates) and len(expression.expressions) == 1: 2564 return f"TTL {self.expressions(expression, flat=True)}" 2565 2566 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}" 2567 2568 def transaction_sql(self, expression: exp.Transaction) -> str: 2569 return "BEGIN" 2570 2571 def commit_sql(self, expression: exp.Commit) -> str: 2572 chain = expression.args.get("chain") 2573 if chain is not None: 2574 chain = " AND CHAIN" if chain else " AND NO CHAIN" 2575 2576 return f"COMMIT{chain or ''}" 2577 2578 def rollback_sql(self, expression: exp.Rollback) -> str: 2579 savepoint = expression.args.get("savepoint") 2580 savepoint = f" TO {savepoint}" if savepoint else "" 2581 return f"ROLLBACK{savepoint}" 2582 2583 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2584 this = self.sql(expression, "this") 2585 2586 dtype = self.sql(expression, "dtype") 2587 if dtype: 2588 collate = self.sql(expression, "collate") 2589 collate = f" COLLATE {collate}" if collate else "" 2590 using = self.sql(expression, "using") 2591 using = f" USING {using}" if using else "" 2592 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2593 2594 default = self.sql(expression, "default") 2595 if default: 2596 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2597 2598 if not expression.args.get("drop"): 2599 self.unsupported("Unsupported ALTER COLUMN syntax") 2600 2601 return f"ALTER COLUMN {this} DROP DEFAULT" 2602 2603 def renametable_sql(self, expression: exp.RenameTable) -> str: 2604 if not self.RENAME_TABLE_WITH_DB: 2605 # Remove db from tables 2606 expression = expression.transform( 2607 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 2608 ) 2609 this = self.sql(expression, "this") 2610 return f"RENAME TO {this}" 2611 2612 def altertable_sql(self, expression: exp.AlterTable) -> str: 2613 actions = expression.args["actions"] 2614 2615 if isinstance(actions[0], exp.ColumnDef): 2616 actions = self.add_column_sql(expression) 2617 elif isinstance(actions[0], exp.Schema): 2618 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 2619 elif isinstance(actions[0], exp.Delete): 2620 actions = self.expressions(expression, key="actions", flat=True) 2621 else: 2622 actions = self.expressions(expression, key="actions", flat=True) 2623 2624 exists = " IF EXISTS" if expression.args.get("exists") else "" 2625 only = " ONLY" if expression.args.get("only") else "" 2626 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}" 2627 2628 def add_column_sql(self, expression: exp.AlterTable) -> str: 2629 if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD: 2630 return self.expressions( 2631 expression, 2632 key="actions", 2633 prefix="ADD COLUMN ", 2634 ) 2635 return f"ADD {self.expressions(expression, key='actions', flat=True)}" 2636 2637 def droppartition_sql(self, expression: exp.DropPartition) -> str: 2638 expressions = self.expressions(expression) 2639 exists = " IF EXISTS " if expression.args.get("exists") else " " 2640 return f"DROP{exists}{expressions}" 2641 2642 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 2643 this = self.sql(expression, "this") 2644 expression_ = self.sql(expression, "expression") 2645 add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD" 2646 2647 enforced = expression.args.get("enforced") 2648 if enforced is not None: 2649 return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}" 2650 2651 return f"{add_constraint} {expression_}" 2652 2653 def distinct_sql(self, expression: exp.Distinct) -> str: 2654 this = self.expressions(expression, flat=True) 2655 this = f" {this}" if this else "" 2656 2657 on = self.sql(expression, "on") 2658 on = f" ON {on}" if on else "" 2659 return f"DISTINCT{this}{on}" 2660 2661 def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: 2662 return f"{self.sql(expression, 'this')} IGNORE NULLS" 2663 2664 def respectnulls_sql(self, expression: exp.RespectNulls) -> str: 2665 return f"{self.sql(expression, 'this')} RESPECT NULLS" 2666 2667 def intdiv_sql(self, expression: exp.IntDiv) -> str: 2668 return self.sql( 2669 exp.Cast( 2670 this=exp.Div(this=expression.this, expression=expression.expression), 2671 to=exp.DataType(this=exp.DataType.Type.INT), 2672 ) 2673 ) 2674 2675 def dpipe_sql(self, expression: exp.DPipe) -> str: 2676 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2677 return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten())) 2678 return self.binary(expression, "||") 2679 2680 def div_sql(self, expression: exp.Div) -> str: 2681 l, r = expression.left, expression.right 2682 2683 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 2684 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 2685 2686 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 2687 if not l.is_type(*exp.DataType.FLOAT_TYPES) and not r.is_type( 2688 *exp.DataType.FLOAT_TYPES 2689 ): 2690 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 2691 2692 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 2693 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 2694 return self.sql( 2695 exp.cast( 2696 l / r, 2697 to=exp.DataType.Type.BIGINT, 2698 ) 2699 ) 2700 2701 return self.binary(expression, "/") 2702 2703 def overlaps_sql(self, expression: exp.Overlaps) -> str: 2704 return self.binary(expression, "OVERLAPS") 2705 2706 def distance_sql(self, expression: exp.Distance) -> str: 2707 return self.binary(expression, "<->") 2708 2709 def dot_sql(self, expression: exp.Dot) -> str: 2710 return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}" 2711 2712 def eq_sql(self, expression: exp.EQ) -> str: 2713 return self.binary(expression, "=") 2714 2715 def propertyeq_sql(self, expression: exp.PropertyEQ) -> str: 2716 return self.binary(expression, ":=") 2717 2718 def escape_sql(self, expression: exp.Escape) -> str: 2719 return self.binary(expression, "ESCAPE") 2720 2721 def glob_sql(self, expression: exp.Glob) -> str: 2722 return self.binary(expression, "GLOB") 2723 2724 def gt_sql(self, expression: exp.GT) -> str: 2725 return self.binary(expression, ">") 2726 2727 def gte_sql(self, expression: exp.GTE) -> str: 2728 return self.binary(expression, ">=") 2729 2730 def ilike_sql(self, expression: exp.ILike) -> str: 2731 return self.binary(expression, "ILIKE") 2732 2733 def ilikeany_sql(self, expression: exp.ILikeAny) -> str: 2734 return self.binary(expression, "ILIKE ANY") 2735 2736 def is_sql(self, expression: exp.Is) -> str: 2737 if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean): 2738 return self.sql( 2739 expression.this if expression.expression.this else exp.not_(expression.this) 2740 ) 2741 return self.binary(expression, "IS") 2742 2743 def like_sql(self, expression: exp.Like) -> str: 2744 return self.binary(expression, "LIKE") 2745 2746 def likeany_sql(self, expression: exp.LikeAny) -> str: 2747 return self.binary(expression, "LIKE ANY") 2748 2749 def similarto_sql(self, expression: exp.SimilarTo) -> str: 2750 return self.binary(expression, "SIMILAR TO") 2751 2752 def lt_sql(self, expression: exp.LT) -> str: 2753 return self.binary(expression, "<") 2754 2755 def lte_sql(self, expression: exp.LTE) -> str: 2756 return self.binary(expression, "<=") 2757 2758 def mod_sql(self, expression: exp.Mod) -> str: 2759 return self.binary(expression, "%") 2760 2761 def mul_sql(self, expression: exp.Mul) -> str: 2762 return self.binary(expression, "*") 2763 2764 def neq_sql(self, expression: exp.NEQ) -> str: 2765 return self.binary(expression, "<>") 2766 2767 def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str: 2768 return self.binary(expression, "IS NOT DISTINCT FROM") 2769 2770 def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str: 2771 return self.binary(expression, "IS DISTINCT FROM") 2772 2773 def or_sql(self, expression: exp.Or) -> str: 2774 return self.connector_sql(expression, "OR") 2775 2776 def slice_sql(self, expression: exp.Slice) -> str: 2777 return self.binary(expression, ":") 2778 2779 def sub_sql(self, expression: exp.Sub) -> str: 2780 return self.binary(expression, "-") 2781 2782 def trycast_sql(self, expression: exp.TryCast) -> str: 2783 return self.cast_sql(expression, safe_prefix="TRY_") 2784 2785 def log_sql(self, expression: exp.Log) -> str: 2786 this = expression.this 2787 expr = expression.expression 2788 2789 if not self.dialect.LOG_BASE_FIRST: 2790 this, expr = expr, this 2791 2792 return self.func("LOG", this, expr) 2793 2794 def use_sql(self, expression: exp.Use) -> str: 2795 kind = self.sql(expression, "kind") 2796 kind = f" {kind}" if kind else "" 2797 this = self.sql(expression, "this") 2798 this = f" {this}" if this else "" 2799 return f"USE{kind}{this}" 2800 2801 def binary(self, expression: exp.Binary, op: str) -> str: 2802 op = self.maybe_comment(op, comments=expression.comments) 2803 return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}" 2804 2805 def function_fallback_sql(self, expression: exp.Func) -> str: 2806 args = [] 2807 2808 for key in expression.arg_types: 2809 arg_value = expression.args.get(key) 2810 2811 if isinstance(arg_value, list): 2812 for value in arg_value: 2813 args.append(value) 2814 elif arg_value is not None: 2815 args.append(arg_value) 2816 2817 if self.normalize_functions: 2818 name = expression.sql_name() 2819 else: 2820 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 2821 2822 return self.func(name, *args) 2823 2824 def func( 2825 self, 2826 name: str, 2827 *args: t.Optional[exp.Expression | str], 2828 prefix: str = "(", 2829 suffix: str = ")", 2830 ) -> str: 2831 return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}" 2832 2833 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 2834 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 2835 if self.pretty and self.text_width(arg_sqls) > self.max_text_width: 2836 return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 2837 return ", ".join(arg_sqls) 2838 2839 def text_width(self, args: t.Iterable) -> int: 2840 return sum(len(arg) for arg in args) 2841 2842 def format_time(self, expression: exp.Expression) -> t.Optional[str]: 2843 return format_time( 2844 self.sql(expression, "format"), 2845 self.dialect.INVERSE_TIME_MAPPING, 2846 self.dialect.INVERSE_TIME_TRIE, 2847 ) 2848 2849 def expressions( 2850 self, 2851 expression: t.Optional[exp.Expression] = None, 2852 key: t.Optional[str] = None, 2853 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 2854 flat: bool = False, 2855 indent: bool = True, 2856 skip_first: bool = False, 2857 sep: str = ", ", 2858 prefix: str = "", 2859 ) -> str: 2860 expressions = expression.args.get(key or "expressions") if expression else sqls 2861 2862 if not expressions: 2863 return "" 2864 2865 if flat: 2866 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 2867 2868 num_sqls = len(expressions) 2869 2870 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 2871 pad = " " * self.pad 2872 stripped_sep = sep.strip() 2873 2874 result_sqls = [] 2875 for i, e in enumerate(expressions): 2876 sql = self.sql(e, comment=False) 2877 if not sql: 2878 continue 2879 2880 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 2881 2882 if self.pretty: 2883 if self.leading_comma: 2884 result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}") 2885 else: 2886 result_sqls.append( 2887 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 2888 ) 2889 else: 2890 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 2891 2892 result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls) 2893 return self.indent(result_sql, skip_first=skip_first) if indent else result_sql 2894 2895 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 2896 flat = flat or isinstance(expression.parent, exp.Properties) 2897 expressions_sql = self.expressions(expression, flat=flat) 2898 if flat: 2899 return f"{op} {expressions_sql}" 2900 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}" 2901 2902 def naked_property(self, expression: exp.Property) -> str: 2903 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 2904 if not property_name: 2905 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 2906 return f"{property_name} {self.sql(expression, 'this')}" 2907 2908 def set_operation(self, expression: exp.Union, op: str) -> str: 2909 this = self.maybe_comment(self.sql(expression, "this"), comments=expression.comments) 2910 op = self.seg(op) 2911 return self.query_modifiers( 2912 expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}" 2913 ) 2914 2915 def tag_sql(self, expression: exp.Tag) -> str: 2916 return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}" 2917 2918 def token_sql(self, token_type: TokenType) -> str: 2919 return self.TOKEN_MAPPING.get(token_type, token_type.name) 2920 2921 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 2922 this = self.sql(expression, "this") 2923 expressions = self.no_identify(self.expressions, expression) 2924 expressions = ( 2925 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 2926 ) 2927 return f"{this}{expressions}" 2928 2929 def joinhint_sql(self, expression: exp.JoinHint) -> str: 2930 this = self.sql(expression, "this") 2931 expressions = self.expressions(expression, flat=True) 2932 return f"{this}({expressions})" 2933 2934 def kwarg_sql(self, expression: exp.Kwarg) -> str: 2935 return self.binary(expression, "=>") 2936 2937 def when_sql(self, expression: exp.When) -> str: 2938 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 2939 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 2940 condition = self.sql(expression, "condition") 2941 condition = f" AND {condition}" if condition else "" 2942 2943 then_expression = expression.args.get("then") 2944 if isinstance(then_expression, exp.Insert): 2945 then = f"INSERT {self.sql(then_expression, 'this')}" 2946 if "expression" in then_expression.args: 2947 then += f" VALUES {self.sql(then_expression, 'expression')}" 2948 elif isinstance(then_expression, exp.Update): 2949 if isinstance(then_expression.args.get("expressions"), exp.Star): 2950 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 2951 else: 2952 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 2953 else: 2954 then = self.sql(then_expression) 2955 return f"WHEN {matched}{source}{condition} THEN {then}" 2956 2957 def merge_sql(self, expression: exp.Merge) -> str: 2958 table = expression.this 2959 table_alias = "" 2960 2961 hints = table.args.get("hints") 2962 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 2963 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 2964 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 2965 2966 this = self.sql(table) 2967 using = f"USING {self.sql(expression, 'using')}" 2968 on = f"ON {self.sql(expression, 'on')}" 2969 expressions = self.expressions(expression, sep=" ") 2970 2971 return self.prepend_ctes( 2972 expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}" 2973 ) 2974 2975 def tochar_sql(self, expression: exp.ToChar) -> str: 2976 if expression.args.get("format"): 2977 self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function") 2978 2979 return self.sql(exp.cast(expression.this, "text")) 2980 2981 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 2982 this = self.sql(expression, "this") 2983 kind = self.sql(expression, "kind") 2984 settings_sql = self.expressions(expression, key="settings", sep=" ") 2985 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 2986 return f"{this}({kind}{args})" 2987 2988 def dictrange_sql(self, expression: exp.DictRange) -> str: 2989 this = self.sql(expression, "this") 2990 max = self.sql(expression, "max") 2991 min = self.sql(expression, "min") 2992 return f"{this}(MIN {min} MAX {max})" 2993 2994 def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str: 2995 return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}" 2996 2997 def oncluster_sql(self, expression: exp.OnCluster) -> str: 2998 return "" 2999 3000 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3001 expressions = self.expressions(expression, key="expressions", flat=True) 3002 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3003 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3004 buckets = self.sql(expression, "buckets") 3005 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS" 3006 3007 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3008 this = self.sql(expression, "this") 3009 having = self.sql(expression, "having") 3010 3011 if having: 3012 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3013 3014 return self.func("ANY_VALUE", this) 3015 3016 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3017 transform = self.func("TRANSFORM", *expression.expressions) 3018 row_format_before = self.sql(expression, "row_format_before") 3019 row_format_before = f" {row_format_before}" if row_format_before else "" 3020 record_writer = self.sql(expression, "record_writer") 3021 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3022 using = f" USING {self.sql(expression, 'command_script')}" 3023 schema = self.sql(expression, "schema") 3024 schema = f" AS {schema}" if schema else "" 3025 row_format_after = self.sql(expression, "row_format_after") 3026 row_format_after = f" {row_format_after}" if row_format_after else "" 3027 record_reader = self.sql(expression, "record_reader") 3028 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3029 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}" 3030 3031 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3032 key_block_size = self.sql(expression, "key_block_size") 3033 if key_block_size: 3034 return f"KEY_BLOCK_SIZE = {key_block_size}" 3035 3036 using = self.sql(expression, "using") 3037 if using: 3038 return f"USING {using}" 3039 3040 parser = self.sql(expression, "parser") 3041 if parser: 3042 return f"WITH PARSER {parser}" 3043 3044 comment = self.sql(expression, "comment") 3045 if comment: 3046 return f"COMMENT {comment}" 3047 3048 visible = expression.args.get("visible") 3049 if visible is not None: 3050 return "VISIBLE" if visible else "INVISIBLE" 3051 3052 engine_attr = self.sql(expression, "engine_attr") 3053 if engine_attr: 3054 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3055 3056 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3057 if secondary_engine_attr: 3058 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3059 3060 self.unsupported("Unsupported index constraint option.") 3061 return "" 3062 3063 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3064 kind = self.sql(expression, "kind") 3065 kind = f"{kind} INDEX" if kind else "INDEX" 3066 this = self.sql(expression, "this") 3067 this = f" {this}" if this else "" 3068 index_type = self.sql(expression, "index_type") 3069 index_type = f" USING {index_type}" if index_type else "" 3070 schema = self.sql(expression, "schema") 3071 schema = f" {schema}" if schema else "" 3072 options = self.expressions(expression, key="options", sep=" ") 3073 options = f" {options}" if options else "" 3074 return f"{kind}{this}{index_type}{schema}{options}" 3075 3076 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3077 if self.NVL2_SUPPORTED: 3078 return self.function_fallback_sql(expression) 3079 3080 case = exp.Case().when( 3081 expression.this.is_(exp.null()).not_(copy=False), 3082 expression.args["true"], 3083 copy=False, 3084 ) 3085 else_cond = expression.args.get("false") 3086 if else_cond: 3087 case.else_(else_cond, copy=False) 3088 3089 return self.sql(case) 3090 3091 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3092 this = self.sql(expression, "this") 3093 expr = self.sql(expression, "expression") 3094 iterator = self.sql(expression, "iterator") 3095 condition = self.sql(expression, "condition") 3096 condition = f" IF {condition}" if condition else "" 3097 return f"{this} FOR {expr} IN {iterator}{condition}" 3098 3099 def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str: 3100 return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})" 3101 3102 def opclass_sql(self, expression: exp.Opclass) -> str: 3103 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 3104 3105 def predict_sql(self, expression: exp.Predict) -> str: 3106 model = self.sql(expression, "this") 3107 model = f"MODEL {model}" 3108 table = self.sql(expression, "expression") 3109 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3110 parameters = self.sql(expression, "params_struct") 3111 return self.func("PREDICT", model, table, parameters or None) 3112 3113 def forin_sql(self, expression: exp.ForIn) -> str: 3114 this = self.sql(expression, "this") 3115 expression_sql = self.sql(expression, "expression") 3116 return f"FOR {this} DO {expression_sql}" 3117 3118 def refresh_sql(self, expression: exp.Refresh) -> str: 3119 this = self.sql(expression, "this") 3120 table = "" if isinstance(expression.this, exp.Literal) else "TABLE " 3121 return f"REFRESH {table}{this}" 3122 3123 def operator_sql(self, expression: exp.Operator) -> str: 3124 return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})") 3125 3126 def toarray_sql(self, expression: exp.ToArray) -> str: 3127 arg = expression.this 3128 if not arg.type: 3129 from sqlglot.optimizer.annotate_types import annotate_types 3130 3131 arg = annotate_types(arg) 3132 3133 if arg.is_type(exp.DataType.Type.ARRAY): 3134 return self.sql(arg) 3135 3136 cond_for_null = arg.is_(exp.null()) 3137 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.Array(expressions=[arg]))) 3138 3139 def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str: 3140 this = expression.this 3141 if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME): 3142 return self.sql(this) 3143 3144 return self.sql(exp.cast(this, "time")) 3145 3146 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3147 this = expression.this 3148 time_format = self.format_time(expression) 3149 3150 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3151 return self.sql( 3152 exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date") 3153 ) 3154 3155 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3156 return self.sql(this) 3157 3158 return self.sql(exp.cast(this, "date")) 3159 3160 def unixdate_sql(self, expression: exp.UnixDate) -> str: 3161 return self.sql( 3162 exp.func( 3163 "DATEDIFF", 3164 expression.this, 3165 exp.cast(exp.Literal.string("1970-01-01"), "date"), 3166 "day", 3167 ) 3168 ) 3169 3170 def lastday_sql(self, expression: exp.LastDay) -> str: 3171 if self.LAST_DAY_SUPPORTS_DATE_PART: 3172 return self.function_fallback_sql(expression) 3173 3174 unit = expression.text("unit") 3175 if unit and unit != "MONTH": 3176 self.unsupported("Date parts are not supported in LAST_DAY.") 3177 3178 return self.func("LAST_DAY", expression.this) 3179 3180 def _simplify_unless_literal(self, expression: E) -> E: 3181 if not isinstance(expression, exp.Literal): 3182 from sqlglot.optimizer.simplify import simplify 3183 3184 expression = simplify(expression, dialect=self.dialect) 3185 3186 return expression 3187 3188 def _ensure_string_if_null(self, values: t.List[exp.Expression]) -> t.List[exp.Expression]: 3189 return [ 3190 exp.func("COALESCE", exp.cast(value, "text"), exp.Literal.string("")) 3191 for value in values 3192 if value 3193 ]
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 = expression.this 1691 this_sql = self.sql(this) 1692 1693 if on_sql: 1694 on_sql = self.indent(on_sql, skip_first=True) 1695 space = self.seg(" " * self.pad) if self.pretty else " " 1696 if using: 1697 on_sql = f"{space}USING ({on_sql})" 1698 else: 1699 on_sql = f"{space}ON {on_sql}" 1700 elif not op_sql: 1701 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 1702 return f" {this_sql}" 1703 1704 return f", {this_sql}" 1705 1706 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1707 return f"{self.seg(op_sql)} {this_sql}{on_sql}"
1714 def lateral_op(self, expression: exp.Lateral) -> str: 1715 cross_apply = expression.args.get("cross_apply") 1716 1717 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 1718 if cross_apply is True: 1719 op = "INNER JOIN " 1720 elif cross_apply is False: 1721 op = "LEFT JOIN " 1722 else: 1723 op = "" 1724 1725 return f"{op}LATERAL"
1727 def lateral_sql(self, expression: exp.Lateral) -> str: 1728 this = self.sql(expression, "this") 1729 1730 if expression.args.get("view"): 1731 alias = expression.args["alias"] 1732 columns = self.expressions(alias, key="columns", flat=True) 1733 table = f" {alias.name}" if alias.name else "" 1734 columns = f" AS {columns}" if columns else "" 1735 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1736 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1737 1738 alias = self.sql(expression, "alias") 1739 alias = f" AS {alias}" if alias else "" 1740 return f"{self.lateral_op(expression)} {this}{alias}"
1742 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1743 this = self.sql(expression, "this") 1744 1745 args = [ 1746 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1747 for e in (expression.args.get(k) for k in ("offset", "expression")) 1748 if e 1749 ] 1750 1751 args_sql = ", ".join(self.sql(e) for e in args) 1752 args_sql = f"({args_sql})" if any(top and not e.is_number for e in args) else args_sql 1753 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}"
1755 def offset_sql(self, expression: exp.Offset) -> str: 1756 this = self.sql(expression, "this") 1757 expression = expression.expression 1758 expression = ( 1759 self._simplify_unless_literal(expression) if self.LIMIT_ONLY_LITERALS else expression 1760 ) 1761 return f"{this}{self.seg('OFFSET')} {self.sql(expression)}"
1763 def setitem_sql(self, expression: exp.SetItem) -> str: 1764 kind = self.sql(expression, "kind") 1765 kind = f"{kind} " if kind else "" 1766 this = self.sql(expression, "this") 1767 expressions = self.expressions(expression) 1768 collate = self.sql(expression, "collate") 1769 collate = f" COLLATE {collate}" if collate else "" 1770 global_ = "GLOBAL " if expression.args.get("global") else "" 1771 return f"{global_}{kind}{this}{expressions}{collate}"
1773 def set_sql(self, expression: exp.Set) -> str: 1774 expressions = ( 1775 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 1776 ) 1777 tag = " TAG" if expression.args.get("tag") else "" 1778 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
1783 def lock_sql(self, expression: exp.Lock) -> str: 1784 if not self.LOCKING_READS_SUPPORTED: 1785 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 1786 return "" 1787 1788 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 1789 expressions = self.expressions(expression, flat=True) 1790 expressions = f" OF {expressions}" if expressions else "" 1791 wait = expression.args.get("wait") 1792 1793 if wait is not None: 1794 if isinstance(wait, exp.Literal): 1795 wait = f" WAIT {self.sql(wait)}" 1796 else: 1797 wait = " NOWAIT" if wait else " SKIP LOCKED" 1798 1799 return f"{lock_type}{expressions}{wait or ''}"
def
escape_str(self, text: str) -> str:
1807 def escape_str(self, text: str) -> str: 1808 text = text.replace(self.dialect.QUOTE_END, self._escaped_quote_end) 1809 if self.dialect.INVERSE_ESCAPE_SEQUENCES: 1810 text = "".join(self.dialect.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text) 1811 elif self.pretty: 1812 text = text.replace("\n", self.SENTINEL_LINE_BREAK) 1813 return text
1815 def loaddata_sql(self, expression: exp.LoadData) -> str: 1816 local = " LOCAL" if expression.args.get("local") else "" 1817 inpath = f" INPATH {self.sql(expression, 'inpath')}" 1818 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 1819 this = f" INTO TABLE {self.sql(expression, 'this')}" 1820 partition = self.sql(expression, "partition") 1821 partition = f" {partition}" if partition else "" 1822 input_format = self.sql(expression, "input_format") 1823 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 1824 serde = self.sql(expression, "serde") 1825 serde = f" SERDE {serde}" if serde else "" 1826 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
1834 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 1835 this = self.sql(expression, "this") 1836 this = f"{this} " if this else this 1837 order = self.op_expressions(f"{this}ORDER BY", expression, flat=this or flat) # type: ignore 1838 interpolated_values = [ 1839 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 1840 for named_expression in expression.args.get("interpolate") or [] 1841 ] 1842 interpolate = ( 1843 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 1844 ) 1845 return f"{order}{interpolate}"
1847 def withfill_sql(self, expression: exp.WithFill) -> str: 1848 from_sql = self.sql(expression, "from") 1849 from_sql = f" FROM {from_sql}" if from_sql else "" 1850 to_sql = self.sql(expression, "to") 1851 to_sql = f" TO {to_sql}" if to_sql else "" 1852 step_sql = self.sql(expression, "step") 1853 step_sql = f" STEP {step_sql}" if step_sql else "" 1854 return f"WITH FILL{from_sql}{to_sql}{step_sql}"
1865 def ordered_sql(self, expression: exp.Ordered) -> str: 1866 desc = expression.args.get("desc") 1867 asc = not desc 1868 1869 nulls_first = expression.args.get("nulls_first") 1870 nulls_last = not nulls_first 1871 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 1872 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 1873 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 1874 1875 this = self.sql(expression, "this") 1876 1877 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 1878 nulls_sort_change = "" 1879 if nulls_first and ( 1880 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 1881 ): 1882 nulls_sort_change = " NULLS FIRST" 1883 elif ( 1884 nulls_last 1885 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 1886 and not nulls_are_last 1887 ): 1888 nulls_sort_change = " NULLS LAST" 1889 1890 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 1891 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 1892 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 1893 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 1894 nulls_sort_change = "" 1895 1896 with_fill = self.sql(expression, "with_fill") 1897 with_fill = f" {with_fill}" if with_fill else "" 1898 1899 return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
1901 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 1902 partition = self.partition_by_sql(expression) 1903 order = self.sql(expression, "order") 1904 measures = self.expressions(expression, key="measures") 1905 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 1906 rows = self.sql(expression, "rows") 1907 rows = self.seg(rows) if rows else "" 1908 after = self.sql(expression, "after") 1909 after = self.seg(after) if after else "" 1910 pattern = self.sql(expression, "pattern") 1911 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 1912 definition_sqls = [ 1913 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 1914 for definition in expression.args.get("define", []) 1915 ] 1916 definitions = self.expressions(sqls=definition_sqls) 1917 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 1918 body = "".join( 1919 ( 1920 partition, 1921 order, 1922 measures, 1923 rows, 1924 after, 1925 pattern, 1926 define, 1927 ) 1928 ) 1929 alias = self.sql(expression, "alias") 1930 alias = f" {alias}" if alias else "" 1931 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
1933 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 1934 limit: t.Optional[exp.Fetch | exp.Limit] = expression.args.get("limit") 1935 1936 # If the limit is generated as TOP, we need to ensure it's not generated twice 1937 with_offset_limit_modifiers = not isinstance(limit, exp.Limit) or not self.LIMIT_IS_TOP 1938 1939 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 1940 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 1941 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 1942 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 1943 1944 fetch = isinstance(limit, exp.Fetch) 1945 1946 offset_limit_modifiers = ( 1947 self.offset_limit_modifiers(expression, fetch, limit) 1948 if with_offset_limit_modifiers 1949 else [] 1950 ) 1951 1952 return csv( 1953 *sqls, 1954 *[self.sql(join) for join in expression.args.get("joins") or []], 1955 self.sql(expression, "connect"), 1956 self.sql(expression, "match"), 1957 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 1958 self.sql(expression, "where"), 1959 self.sql(expression, "group"), 1960 self.sql(expression, "having"), 1961 *self.after_having_modifiers(expression), 1962 self.sql(expression, "order"), 1963 *offset_limit_modifiers, 1964 *self.after_limit_modifiers(expression), 1965 sep="", 1966 )
def
offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]:
1968 def offset_limit_modifiers( 1969 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 1970 ) -> t.List[str]: 1971 return [ 1972 self.sql(expression, "offset") if fetch else self.sql(limit), 1973 self.sql(limit) if fetch else self.sql(expression, "offset"), 1974 ]
1976 def after_having_modifiers(self, expression: exp.Expression) -> t.List[str]: 1977 return [ 1978 self.sql(expression, "qualify"), 1979 self.seg("WINDOW ") + self.expressions(expression, key="windows", flat=True) 1980 if expression.args.get("windows") 1981 else "", 1982 self.sql(expression, "distribute"), 1983 self.sql(expression, "sort"), 1984 self.sql(expression, "cluster"), 1985 ]
1992 def select_sql(self, expression: exp.Select) -> str: 1993 hint = self.sql(expression, "hint") 1994 distinct = self.sql(expression, "distinct") 1995 distinct = f" {distinct}" if distinct else "" 1996 kind = self.sql(expression, "kind") 1997 limit = expression.args.get("limit") 1998 top = ( 1999 self.limit_sql(limit, top=True) 2000 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP 2001 else "" 2002 ) 2003 2004 expressions = self.expressions(expression) 2005 2006 if kind: 2007 if kind in self.SELECT_KINDS: 2008 kind = f" AS {kind}" 2009 else: 2010 if kind == "STRUCT": 2011 expressions = self.expressions( 2012 sqls=[ 2013 self.sql( 2014 exp.Struct( 2015 expressions=[ 2016 exp.column(e.output_name).eq( 2017 e.this if isinstance(e, exp.Alias) else e 2018 ) 2019 for e in expression.expressions 2020 ] 2021 ) 2022 ) 2023 ] 2024 ) 2025 kind = "" 2026 2027 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2028 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2029 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2030 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2031 sql = self.query_modifiers( 2032 expression, 2033 f"SELECT{top_distinct}{kind}{expressions}", 2034 self.sql(expression, "into", comment=False), 2035 self.sql(expression, "from", comment=False), 2036 ) 2037 return self.prepend_ctes(expression, sql)
2049 def star_sql(self, expression: exp.Star) -> str: 2050 except_ = self.expressions(expression, key="except", flat=True) 2051 except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else "" 2052 replace = self.expressions(expression, key="replace", flat=True) 2053 replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else "" 2054 return f"*{except_}{replace}"
2070 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2071 alias = self.sql(expression, "alias") 2072 alias = f"{sep}{alias}" if alias else "" 2073 2074 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2075 pivots = f" {pivots}" if pivots else "" 2076 2077 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2078 return self.prepend_ctes(expression, sql)
2096 def unnest_sql(self, expression: exp.Unnest) -> str: 2097 args = self.expressions(expression, flat=True) 2098 2099 alias = expression.args.get("alias") 2100 offset = expression.args.get("offset") 2101 2102 if self.UNNEST_WITH_ORDINALITY: 2103 if alias and isinstance(offset, exp.Expression): 2104 alias.append("columns", offset) 2105 2106 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2107 columns = alias.columns 2108 alias = self.sql(columns[0]) if columns else "" 2109 else: 2110 alias = self.sql(alias) 2111 2112 alias = f" AS {alias}" if alias else alias 2113 if self.UNNEST_WITH_ORDINALITY: 2114 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2115 else: 2116 if isinstance(offset, exp.Expression): 2117 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2118 elif offset: 2119 suffix = f"{alias} WITH OFFSET" 2120 else: 2121 suffix = alias 2122 2123 return f"UNNEST({args}){suffix}"
2129 def window_sql(self, expression: exp.Window) -> str: 2130 this = self.sql(expression, "this") 2131 partition = self.partition_by_sql(expression) 2132 order = expression.args.get("order") 2133 order = self.order_sql(order, flat=True) if order else "" 2134 spec = self.sql(expression, "spec") 2135 alias = self.sql(expression, "alias") 2136 over = self.sql(expression, "over") or "OVER" 2137 2138 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2139 2140 first = expression.args.get("first") 2141 if first is None: 2142 first = "" 2143 else: 2144 first = "FIRST" if first else "LAST" 2145 2146 if not partition and not order and not spec and alias: 2147 return f"{this} {alias}" 2148 2149 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2150 return f"{this} ({args})"
def
partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
2156 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2157 kind = self.sql(expression, "kind") 2158 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2159 end = ( 2160 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2161 or "CURRENT ROW" 2162 ) 2163 return f"{kind} BETWEEN {start} AND {end}"
2176 def bracket_sql(self, expression: exp.Bracket) -> str: 2177 expressions = apply_index_offset( 2178 expression.this, 2179 expression.expressions, 2180 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2181 ) 2182 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2183 return f"{self.sql(expression, 'this')}[{expressions_sql}]"
2197 def case_sql(self, expression: exp.Case) -> str: 2198 this = self.sql(expression, "this") 2199 statements = [f"CASE {this}" if this else "CASE"] 2200 2201 for e in expression.args["ifs"]: 2202 statements.append(f"WHEN {self.sql(e, 'this')}") 2203 statements.append(f"THEN {self.sql(e, 'true')}") 2204 2205 default = self.sql(expression, "default") 2206 2207 if default: 2208 statements.append(f"ELSE {default}") 2209 2210 statements.append("END") 2211 2212 if self.pretty and self.text_width(statements) > self.max_text_width: 2213 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2214 2215 return " ".join(statements)
2232 def trim_sql(self, expression: exp.Trim) -> str: 2233 trim_type = self.sql(expression, "position") 2234 2235 if trim_type == "LEADING": 2236 return self.func("LTRIM", expression.this) 2237 elif trim_type == "TRAILING": 2238 return self.func("RTRIM", expression.this) 2239 else: 2240 return self.func("TRIM", expression.this, expression.expression)
def
convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]:
2242 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2243 args = expression.expressions 2244 if isinstance(expression, exp.ConcatWs): 2245 args = args[1:] # Skip the delimiter 2246 2247 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2248 args = [exp.cast(e, "text") for e in args] 2249 2250 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2251 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2252 2253 return args
2255 def concat_sql(self, expression: exp.Concat) -> str: 2256 expressions = self.convert_concat_args(expression) 2257 2258 # Some dialects don't allow a single-argument CONCAT call 2259 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2260 return self.sql(expressions[0]) 2261 2262 return self.func("CONCAT", *expressions)
2273 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2274 expressions = self.expressions(expression, flat=True) 2275 reference = self.sql(expression, "reference") 2276 reference = f" {reference}" if reference else "" 2277 delete = self.sql(expression, "delete") 2278 delete = f" ON DELETE {delete}" if delete else "" 2279 update = self.sql(expression, "update") 2280 update = f" ON UPDATE {update}" if update else "" 2281 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
2283 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2284 expressions = self.expressions(expression, flat=True) 2285 options = self.expressions(expression, key="options", flat=True, sep=" ") 2286 options = f" {options}" if options else "" 2287 return f"PRIMARY KEY ({expressions}){options}"
2303 def jsonobject_sql(self, expression: exp.JSONObject) -> str: 2304 null_handling = expression.args.get("null_handling") 2305 null_handling = f" {null_handling}" if null_handling else "" 2306 unique_keys = expression.args.get("unique_keys") 2307 if unique_keys is not None: 2308 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2309 else: 2310 unique_keys = "" 2311 return_type = self.sql(expression, "return_type") 2312 return_type = f" RETURNING {return_type}" if return_type else "" 2313 encoding = self.sql(expression, "encoding") 2314 encoding = f" ENCODING {encoding}" if encoding else "" 2315 return self.func( 2316 "JSON_OBJECT", 2317 *expression.expressions, 2318 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2319 )
2321 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2322 null_handling = expression.args.get("null_handling") 2323 null_handling = f" {null_handling}" if null_handling else "" 2324 return_type = self.sql(expression, "return_type") 2325 return_type = f" RETURNING {return_type}" if return_type else "" 2326 strict = " STRICT" if expression.args.get("strict") else "" 2327 return self.func( 2328 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2329 )
2331 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2332 this = self.sql(expression, "this") 2333 order = self.sql(expression, "order") 2334 null_handling = expression.args.get("null_handling") 2335 null_handling = f" {null_handling}" if null_handling else "" 2336 return_type = self.sql(expression, "return_type") 2337 return_type = f" RETURNING {return_type}" if return_type else "" 2338 strict = " STRICT" if expression.args.get("strict") else "" 2339 return self.func( 2340 "JSON_ARRAYAGG", 2341 this, 2342 suffix=f"{order}{null_handling}{return_type}{strict})", 2343 )
2345 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2346 path = self.sql(expression, "path") 2347 path = f" PATH {path}" if path else "" 2348 nested_schema = self.sql(expression, "nested_schema") 2349 2350 if nested_schema: 2351 return f"NESTED{path} {nested_schema}" 2352 2353 this = self.sql(expression, "this") 2354 kind = self.sql(expression, "kind") 2355 kind = f" {kind}" if kind else "" 2356 return f"{this}{kind}{path}"
2361 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2362 this = self.sql(expression, "this") 2363 path = self.sql(expression, "path") 2364 path = f", {path}" if path else "" 2365 error_handling = expression.args.get("error_handling") 2366 error_handling = f" {error_handling}" if error_handling else "" 2367 empty_handling = expression.args.get("empty_handling") 2368 empty_handling = f" {empty_handling}" if empty_handling else "" 2369 schema = self.sql(expression, "schema") 2370 return self.func( 2371 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2372 )
2374 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2375 this = self.sql(expression, "this") 2376 kind = self.sql(expression, "kind") 2377 path = self.sql(expression, "path") 2378 path = f" {path}" if path else "" 2379 as_json = " AS JSON" if expression.args.get("as_json") else "" 2380 return f"{this} {kind}{path}{as_json}"
2382 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2383 this = self.sql(expression, "this") 2384 path = self.sql(expression, "path") 2385 path = f", {path}" if path else "" 2386 expressions = self.expressions(expression) 2387 with_ = ( 2388 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2389 if expressions 2390 else "" 2391 ) 2392 return f"OPENJSON({this}{path}){with_}"
2394 def in_sql(self, expression: exp.In) -> str: 2395 query = expression.args.get("query") 2396 unnest = expression.args.get("unnest") 2397 field = expression.args.get("field") 2398 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2399 2400 if query: 2401 in_sql = self.wrap(query) 2402 elif unnest: 2403 in_sql = self.in_unnest_op(unnest) 2404 elif field: 2405 in_sql = self.sql(field) 2406 else: 2407 in_sql = f"({self.expressions(expression, flat=True)})" 2408 2409 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
2414 def interval_sql(self, expression: exp.Interval) -> str: 2415 unit = self.sql(expression, "unit") 2416 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2417 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2418 unit = f" {unit}" if unit else "" 2419 2420 if self.SINGLE_STRING_INTERVAL: 2421 this = expression.this.name if expression.this else "" 2422 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2423 2424 this = self.sql(expression, "this") 2425 if this: 2426 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2427 this = f" {this}" if unwrapped else f" ({this})" 2428 2429 return f"INTERVAL{this}{unit}"
2434 def reference_sql(self, expression: exp.Reference) -> str: 2435 this = self.sql(expression, "this") 2436 expressions = self.expressions(expression, flat=True) 2437 expressions = f"({expressions})" if expressions else "" 2438 options = self.expressions(expression, key="options", flat=True, sep=" ") 2439 options = f" {options}" if options else "" 2440 return f"REFERENCES {this}{expressions}{options}"
2445 def paren_sql(self, expression: exp.Paren) -> str: 2446 if isinstance(expression.unnest(), exp.Select): 2447 sql = self.wrap(expression) 2448 else: 2449 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2450 sql = f"({sql}{self.seg(')', sep='')}" 2451 2452 return self.prepend_ctes(expression, sql)
2490 def connector_sql(self, expression: exp.Connector, op: str) -> str: 2491 if not self.pretty: 2492 return self.binary(expression, op) 2493 2494 sqls = tuple( 2495 self.maybe_comment(self.sql(e), e, e.parent.comments or []) if i != 1 else self.sql(e) 2496 for i, e in enumerate(expression.flatten(unnest=False)) 2497 ) 2498 2499 sep = "\n" if self.text_width(sqls) > self.max_text_width else " " 2500 return f"{sep}{op} ".join(sqls)
def
cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str:
2520 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2521 format_sql = self.sql(expression, "format") 2522 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2523 to_sql = self.sql(expression, "to") 2524 to_sql = f" {to_sql}" if to_sql else "" 2525 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql})"
2539 def comment_sql(self, expression: exp.Comment) -> str: 2540 this = self.sql(expression, "this") 2541 kind = expression.args["kind"] 2542 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2543 expression_sql = self.sql(expression, "expression") 2544 return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
2546 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2547 this = self.sql(expression, "this") 2548 delete = " DELETE" if expression.args.get("delete") else "" 2549 recompress = self.sql(expression, "recompress") 2550 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2551 to_disk = self.sql(expression, "to_disk") 2552 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2553 to_volume = self.sql(expression, "to_volume") 2554 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2555 return f"{this}{delete}{recompress}{to_disk}{to_volume}"
2557 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2558 where = self.sql(expression, "where") 2559 group = self.sql(expression, "group") 2560 aggregates = self.expressions(expression, key="aggregates") 2561 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2562 2563 if not (where or group or aggregates) and len(expression.expressions) == 1: 2564 return f"TTL {self.expressions(expression, flat=True)}" 2565 2566 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
2583 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2584 this = self.sql(expression, "this") 2585 2586 dtype = self.sql(expression, "dtype") 2587 if dtype: 2588 collate = self.sql(expression, "collate") 2589 collate = f" COLLATE {collate}" if collate else "" 2590 using = self.sql(expression, "using") 2591 using = f" USING {using}" if using else "" 2592 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2593 2594 default = self.sql(expression, "default") 2595 if default: 2596 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2597 2598 if not expression.args.get("drop"): 2599 self.unsupported("Unsupported ALTER COLUMN syntax") 2600 2601 return f"ALTER COLUMN {this} DROP DEFAULT"
2603 def renametable_sql(self, expression: exp.RenameTable) -> str: 2604 if not self.RENAME_TABLE_WITH_DB: 2605 # Remove db from tables 2606 expression = expression.transform( 2607 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 2608 ) 2609 this = self.sql(expression, "this") 2610 return f"RENAME TO {this}"
2612 def altertable_sql(self, expression: exp.AlterTable) -> str: 2613 actions = expression.args["actions"] 2614 2615 if isinstance(actions[0], exp.ColumnDef): 2616 actions = self.add_column_sql(expression) 2617 elif isinstance(actions[0], exp.Schema): 2618 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 2619 elif isinstance(actions[0], exp.Delete): 2620 actions = self.expressions(expression, key="actions", flat=True) 2621 else: 2622 actions = self.expressions(expression, key="actions", flat=True) 2623 2624 exists = " IF EXISTS" if expression.args.get("exists") else "" 2625 only = " ONLY" if expression.args.get("only") else "" 2626 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}"
2642 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 2643 this = self.sql(expression, "this") 2644 expression_ = self.sql(expression, "expression") 2645 add_constraint = f"ADD CONSTRAINT {this}" if this else "ADD" 2646 2647 enforced = expression.args.get("enforced") 2648 if enforced is not None: 2649 return f"{add_constraint} CHECK ({expression_}){' ENFORCED' if enforced else ''}" 2650 2651 return f"{add_constraint} {expression_}"
2680 def div_sql(self, expression: exp.Div) -> str: 2681 l, r = expression.left, expression.right 2682 2683 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 2684 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 2685 2686 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 2687 if not l.is_type(*exp.DataType.FLOAT_TYPES) and not r.is_type( 2688 *exp.DataType.FLOAT_TYPES 2689 ): 2690 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 2691 2692 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 2693 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 2694 return self.sql( 2695 exp.cast( 2696 l / r, 2697 to=exp.DataType.Type.BIGINT, 2698 ) 2699 ) 2700 2701 return self.binary(expression, "/")
2805 def function_fallback_sql(self, expression: exp.Func) -> str: 2806 args = [] 2807 2808 for key in expression.arg_types: 2809 arg_value = expression.args.get(key) 2810 2811 if isinstance(arg_value, list): 2812 for value in arg_value: 2813 args.append(value) 2814 elif arg_value is not None: 2815 args.append(arg_value) 2816 2817 if self.normalize_functions: 2818 name = expression.sql_name() 2819 else: 2820 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 2821 2822 return self.func(name, *args)
def
func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')') -> str:
2833 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 2834 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 2835 if self.pretty and self.text_width(arg_sqls) > self.max_text_width: 2836 return self.indent("\n" + f",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 2837 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:
2849 def expressions( 2850 self, 2851 expression: t.Optional[exp.Expression] = None, 2852 key: t.Optional[str] = None, 2853 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 2854 flat: bool = False, 2855 indent: bool = True, 2856 skip_first: bool = False, 2857 sep: str = ", ", 2858 prefix: str = "", 2859 ) -> str: 2860 expressions = expression.args.get(key or "expressions") if expression else sqls 2861 2862 if not expressions: 2863 return "" 2864 2865 if flat: 2866 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 2867 2868 num_sqls = len(expressions) 2869 2870 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 2871 pad = " " * self.pad 2872 stripped_sep = sep.strip() 2873 2874 result_sqls = [] 2875 for i, e in enumerate(expressions): 2876 sql = self.sql(e, comment=False) 2877 if not sql: 2878 continue 2879 2880 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 2881 2882 if self.pretty: 2883 if self.leading_comma: 2884 result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}") 2885 else: 2886 result_sqls.append( 2887 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 2888 ) 2889 else: 2890 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 2891 2892 result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls) 2893 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:
2895 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 2896 flat = flat or isinstance(expression.parent, exp.Properties) 2897 expressions_sql = self.expressions(expression, flat=flat) 2898 if flat: 2899 return f"{op} {expressions_sql}" 2900 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
2902 def naked_property(self, expression: exp.Property) -> str: 2903 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 2904 if not property_name: 2905 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 2906 return f"{property_name} {self.sql(expression, 'this')}"
2908 def set_operation(self, expression: exp.Union, op: str) -> str: 2909 this = self.maybe_comment(self.sql(expression, "this"), comments=expression.comments) 2910 op = self.seg(op) 2911 return self.query_modifiers( 2912 expression, f"{this}{op}{self.sep()}{self.sql(expression, 'expression')}" 2913 )
2921 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 2922 this = self.sql(expression, "this") 2923 expressions = self.no_identify(self.expressions, expression) 2924 expressions = ( 2925 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 2926 ) 2927 return f"{this}{expressions}"
2937 def when_sql(self, expression: exp.When) -> str: 2938 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 2939 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 2940 condition = self.sql(expression, "condition") 2941 condition = f" AND {condition}" if condition else "" 2942 2943 then_expression = expression.args.get("then") 2944 if isinstance(then_expression, exp.Insert): 2945 then = f"INSERT {self.sql(then_expression, 'this')}" 2946 if "expression" in then_expression.args: 2947 then += f" VALUES {self.sql(then_expression, 'expression')}" 2948 elif isinstance(then_expression, exp.Update): 2949 if isinstance(then_expression.args.get("expressions"), exp.Star): 2950 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 2951 else: 2952 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 2953 else: 2954 then = self.sql(then_expression) 2955 return f"WHEN {matched}{source}{condition} THEN {then}"
2957 def merge_sql(self, expression: exp.Merge) -> str: 2958 table = expression.this 2959 table_alias = "" 2960 2961 hints = table.args.get("hints") 2962 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 2963 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 2964 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 2965 2966 this = self.sql(table) 2967 using = f"USING {self.sql(expression, 'using')}" 2968 on = f"ON {self.sql(expression, 'on')}" 2969 expressions = self.expressions(expression, sep=" ") 2970 2971 return self.prepend_ctes( 2972 expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}" 2973 )
2981 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 2982 this = self.sql(expression, "this") 2983 kind = self.sql(expression, "kind") 2984 settings_sql = self.expressions(expression, key="settings", sep=" ") 2985 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 2986 return f"{this}({kind}{args})"
3000 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3001 expressions = self.expressions(expression, key="expressions", flat=True) 3002 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3003 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3004 buckets = self.sql(expression, "buckets") 3005 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
3007 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3008 this = self.sql(expression, "this") 3009 having = self.sql(expression, "having") 3010 3011 if having: 3012 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3013 3014 return self.func("ANY_VALUE", this)
3016 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3017 transform = self.func("TRANSFORM", *expression.expressions) 3018 row_format_before = self.sql(expression, "row_format_before") 3019 row_format_before = f" {row_format_before}" if row_format_before else "" 3020 record_writer = self.sql(expression, "record_writer") 3021 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3022 using = f" USING {self.sql(expression, 'command_script')}" 3023 schema = self.sql(expression, "schema") 3024 schema = f" AS {schema}" if schema else "" 3025 row_format_after = self.sql(expression, "row_format_after") 3026 row_format_after = f" {row_format_after}" if row_format_after else "" 3027 record_reader = self.sql(expression, "record_reader") 3028 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3029 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
3031 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3032 key_block_size = self.sql(expression, "key_block_size") 3033 if key_block_size: 3034 return f"KEY_BLOCK_SIZE = {key_block_size}" 3035 3036 using = self.sql(expression, "using") 3037 if using: 3038 return f"USING {using}" 3039 3040 parser = self.sql(expression, "parser") 3041 if parser: 3042 return f"WITH PARSER {parser}" 3043 3044 comment = self.sql(expression, "comment") 3045 if comment: 3046 return f"COMMENT {comment}" 3047 3048 visible = expression.args.get("visible") 3049 if visible is not None: 3050 return "VISIBLE" if visible else "INVISIBLE" 3051 3052 engine_attr = self.sql(expression, "engine_attr") 3053 if engine_attr: 3054 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3055 3056 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3057 if secondary_engine_attr: 3058 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3059 3060 self.unsupported("Unsupported index constraint option.") 3061 return ""
3063 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3064 kind = self.sql(expression, "kind") 3065 kind = f"{kind} INDEX" if kind else "INDEX" 3066 this = self.sql(expression, "this") 3067 this = f" {this}" if this else "" 3068 index_type = self.sql(expression, "index_type") 3069 index_type = f" USING {index_type}" if index_type else "" 3070 schema = self.sql(expression, "schema") 3071 schema = f" {schema}" if schema else "" 3072 options = self.expressions(expression, key="options", sep=" ") 3073 options = f" {options}" if options else "" 3074 return f"{kind}{this}{index_type}{schema}{options}"
3076 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3077 if self.NVL2_SUPPORTED: 3078 return self.function_fallback_sql(expression) 3079 3080 case = exp.Case().when( 3081 expression.this.is_(exp.null()).not_(copy=False), 3082 expression.args["true"], 3083 copy=False, 3084 ) 3085 else_cond = expression.args.get("false") 3086 if else_cond: 3087 case.else_(else_cond, copy=False) 3088 3089 return self.sql(case)
3091 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3092 this = self.sql(expression, "this") 3093 expr = self.sql(expression, "expression") 3094 iterator = self.sql(expression, "iterator") 3095 condition = self.sql(expression, "condition") 3096 condition = f" IF {condition}" if condition else "" 3097 return f"{this} FOR {expr} IN {iterator}{condition}"
3105 def predict_sql(self, expression: exp.Predict) -> str: 3106 model = self.sql(expression, "this") 3107 model = f"MODEL {model}" 3108 table = self.sql(expression, "expression") 3109 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3110 parameters = self.sql(expression, "params_struct") 3111 return self.func("PREDICT", model, table, parameters or None)
3126 def toarray_sql(self, expression: exp.ToArray) -> str: 3127 arg = expression.this 3128 if not arg.type: 3129 from sqlglot.optimizer.annotate_types import annotate_types 3130 3131 arg = annotate_types(arg) 3132 3133 if arg.is_type(exp.DataType.Type.ARRAY): 3134 return self.sql(arg) 3135 3136 cond_for_null = arg.is_(exp.null()) 3137 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.Array(expressions=[arg])))
3146 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3147 this = expression.this 3148 time_format = self.format_time(expression) 3149 3150 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3151 return self.sql( 3152 exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date") 3153 ) 3154 3155 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3156 return self.sql(this) 3157 3158 return self.sql(exp.cast(this, "date"))
3170 def lastday_sql(self, expression: exp.LastDay) -> str: 3171 if self.LAST_DAY_SUPPORTS_DATE_PART: 3172 return self.function_fallback_sql(expression) 3173 3174 unit = expression.text("unit") 3175 if unit and unit != "MONTH": 3176 self.unsupported("Date parts are not supported in LAST_DAY.") 3177 3178 return self.func("LAST_DAY", expression.this)