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