sqlglot.generator
1from __future__ import annotations 2 3import logging 4import re 5import typing as t 6from collections import defaultdict 7from functools import reduce 8 9from sqlglot import exp 10from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages 11from sqlglot.helper import apply_index_offset, csv, seq_get 12from sqlglot.jsonpath import ALL_JSON_PATH_PARTS, JSON_PATH_PART_TRANSFORMS 13from sqlglot.time import format_time 14from sqlglot.tokens import TokenType 15 16if t.TYPE_CHECKING: 17 from sqlglot._typing import E 18 from sqlglot.dialects.dialect import DialectType 19 20logger = logging.getLogger("sqlglot") 21 22ESCAPED_UNICODE_RE = re.compile(r"\\(\d+)") 23 24 25class _Generator(type): 26 def __new__(cls, clsname, bases, attrs): 27 klass = super().__new__(cls, clsname, bases, attrs) 28 29 # Remove transforms that correspond to unsupported JSONPathPart expressions 30 for part in ALL_JSON_PATH_PARTS - klass.SUPPORTED_JSON_PATH_PARTS: 31 klass.TRANSFORMS.pop(part, None) 32 33 return klass 34 35 36class Generator(metaclass=_Generator): 37 """ 38 Generator converts a given syntax tree to the corresponding SQL string. 39 40 Args: 41 pretty: Whether to format the produced SQL string. 42 Default: False. 43 identify: Determines when an identifier should be quoted. Possible values are: 44 False (default): Never quote, except in cases where it's mandatory by the dialect. 45 True or 'always': Always quote. 46 'safe': Only quote identifiers that are case insensitive. 47 normalize: Whether to normalize identifiers to lowercase. 48 Default: False. 49 pad: The pad size in a formatted string. For example, this affects the indentation of 50 a projection in a query, relative to its nesting level. 51 Default: 2. 52 indent: The indentation size in a formatted string. For example, this affects the 53 indentation of subqueries and filters under a `WHERE` clause. 54 Default: 2. 55 normalize_functions: How to normalize function names. Possible values are: 56 "upper" or True (default): Convert names to uppercase. 57 "lower": Convert names to lowercase. 58 False: Disables function name normalization. 59 unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. 60 Default ErrorLevel.WARN. 61 max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. 62 This is only relevant if unsupported_level is ErrorLevel.RAISE. 63 Default: 3 64 leading_comma: Whether the comma is leading or trailing in select expressions. 65 This is only relevant when generating in pretty mode. 66 Default: False 67 max_text_width: The max number of characters in a segment before creating new lines in pretty mode. 68 The default is on the smaller end because the length only represents a segment and not the true 69 line length. 70 Default: 80 71 comments: Whether to preserve comments in the output SQL code. 72 Default: True 73 """ 74 75 TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = { 76 **JSON_PATH_PART_TRANSFORMS, 77 exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}", 78 exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}", 79 exp.CaseSpecificColumnConstraint: lambda _, 80 e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC", 81 exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}", 82 exp.CharacterSetProperty: lambda self, 83 e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}", 84 exp.ClusteredColumnConstraint: lambda self, 85 e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})", 86 exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}", 87 exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}", 88 exp.CopyGrantsProperty: lambda *_: "COPY GRANTS", 89 exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}", 90 exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}", 91 exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}", 92 exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}", 93 exp.ExecuteAsProperty: lambda self, e: self.naked_property(e), 94 exp.ExternalProperty: lambda *_: "EXTERNAL", 95 exp.GlobalProperty: lambda *_: "GLOBAL", 96 exp.HeapProperty: lambda *_: "HEAP", 97 exp.IcebergProperty: lambda *_: "ICEBERG", 98 exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})", 99 exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}", 100 exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}", 101 exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}", 102 exp.JSONExtract: lambda self, e: self.func( 103 "JSON_EXTRACT", e.this, e.expression, *e.expressions 104 ), 105 exp.JSONExtractScalar: lambda self, e: self.func( 106 "JSON_EXTRACT_SCALAR", e.this, e.expression, *e.expressions 107 ), 108 exp.LanguageProperty: lambda self, e: self.naked_property(e), 109 exp.LocationProperty: lambda self, e: self.naked_property(e), 110 exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG", 111 exp.MaterializedProperty: lambda *_: "MATERIALIZED", 112 exp.NonClusteredColumnConstraint: lambda self, 113 e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})", 114 exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX", 115 exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION", 116 exp.OnCommitProperty: lambda _, 117 e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS", 118 exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}", 119 exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}", 120 exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}", 121 exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}", 122 exp.RemoteWithConnectionModelProperty: lambda self, 123 e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}", 124 exp.ReturnsProperty: lambda self, e: self.naked_property(e), 125 exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}", 126 exp.SetConfigProperty: lambda self, e: self.sql(e, "this"), 127 exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET", 128 exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}", 129 exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}", 130 exp.SqlReadWriteProperty: lambda _, e: e.name, 131 exp.SqlSecurityProperty: lambda _, 132 e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}", 133 exp.StabilityProperty: lambda _, e: e.name, 134 exp.TemporaryProperty: lambda *_: "TEMPORARY", 135 exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}", 136 exp.Timestamp: lambda self, e: self.func("TIMESTAMP", e.this, e.expression), 137 exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}", 138 exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}", 139 exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions), 140 exp.TransientProperty: lambda *_: "TRANSIENT", 141 exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE", 142 exp.UnloggedProperty: lambda *_: "UNLOGGED", 143 exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]), 144 exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}", 145 exp.VolatileProperty: lambda *_: "VOLATILE", 146 exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}", 147 exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}", 148 } 149 150 # Whether null ordering is supported in order by 151 # True: Full Support, None: No support, False: No support in window specifications 152 NULL_ORDERING_SUPPORTED: t.Optional[bool] = True 153 154 # Whether ignore nulls is inside the agg or outside. 155 # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER 156 IGNORE_NULLS_IN_FUNC = False 157 158 # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported 159 LOCKING_READS_SUPPORTED = False 160 161 # Always do union distinct or union all 162 EXPLICIT_UNION = False 163 164 # Wrap derived values in parens, usually standard but spark doesn't support it 165 WRAP_DERIVED_VALUES = True 166 167 # Whether create function uses an AS before the RETURN 168 CREATE_FUNCTION_RETURN_AS = True 169 170 # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed 171 MATCHED_BY_SOURCE = True 172 173 # Whether the INTERVAL expression works only with values like '1 day' 174 SINGLE_STRING_INTERVAL = False 175 176 # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs 177 INTERVAL_ALLOWS_PLURAL_FORM = True 178 179 # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH") 180 LIMIT_FETCH = "ALL" 181 182 # Whether limit and fetch allows expresions or just limits 183 LIMIT_ONLY_LITERALS = False 184 185 # Whether a table is allowed to be renamed with a db 186 RENAME_TABLE_WITH_DB = True 187 188 # The separator for grouping sets and rollups 189 GROUPINGS_SEP = "," 190 191 # The string used for creating an index on a table 192 INDEX_ON = "ON" 193 194 # Whether join hints should be generated 195 JOIN_HINTS = True 196 197 # Whether table hints should be generated 198 TABLE_HINTS = True 199 200 # Whether query hints should be generated 201 QUERY_HINTS = True 202 203 # What kind of separator to use for query hints 204 QUERY_HINT_SEP = ", " 205 206 # Whether comparing against booleans (e.g. x IS TRUE) is supported 207 IS_BOOL_ALLOWED = True 208 209 # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement 210 DUPLICATE_KEY_UPDATE_WITH_SET = True 211 212 # Whether to generate the limit as TOP <value> instead of LIMIT <value> 213 LIMIT_IS_TOP = False 214 215 # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ... 216 RETURNING_END = True 217 218 # Whether to generate the (+) suffix for columns used in old-style join conditions 219 COLUMN_JOIN_MARKS_SUPPORTED = False 220 221 # Whether to generate an unquoted value for EXTRACT's date part argument 222 EXTRACT_ALLOWS_QUOTES = True 223 224 # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax 225 TZ_TO_WITH_TIME_ZONE = False 226 227 # Whether the NVL2 function is supported 228 NVL2_SUPPORTED = True 229 230 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax 231 SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE") 232 233 # Whether VALUES statements can be used as derived tables. 234 # MySQL 5 and Redshift do not allow this, so when False, it will convert 235 # SELECT * VALUES into SELECT UNION 236 VALUES_AS_TABLE = True 237 238 # Whether the word COLUMN is included when adding a column with ALTER TABLE 239 ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True 240 241 # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery) 242 UNNEST_WITH_ORDINALITY = True 243 244 # Whether FILTER (WHERE cond) can be used for conditional aggregation 245 AGGREGATE_FILTER_SUPPORTED = True 246 247 # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds 248 SEMI_ANTI_JOIN_WITH_SIDE = True 249 250 # Whether to include the type of a computed column in the CREATE DDL 251 COMPUTED_COLUMN_WITH_TYPE = True 252 253 # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY 254 SUPPORTS_TABLE_COPY = True 255 256 # Whether parentheses are required around the table sample's expression 257 TABLESAMPLE_REQUIRES_PARENS = True 258 259 # Whether a table sample clause's size needs to be followed by the ROWS keyword 260 TABLESAMPLE_SIZE_IS_ROWS = True 261 262 # The keyword(s) to use when generating a sample clause 263 TABLESAMPLE_KEYWORDS = "TABLESAMPLE" 264 265 # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI 266 TABLESAMPLE_WITH_METHOD = True 267 268 # The keyword to use when specifying the seed of a sample clause 269 TABLESAMPLE_SEED_KEYWORD = "SEED" 270 271 # Whether COLLATE is a function instead of a binary operator 272 COLLATE_IS_FUNC = False 273 274 # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle) 275 DATA_TYPE_SPECIFIERS_ALLOWED = False 276 277 # Whether conditions require booleans WHERE x = 0 vs WHERE x 278 ENSURE_BOOLS = False 279 280 # Whether the "RECURSIVE" keyword is required when defining recursive CTEs 281 CTE_RECURSIVE_KEYWORD_REQUIRED = True 282 283 # Whether CONCAT requires >1 arguments 284 SUPPORTS_SINGLE_ARG_CONCAT = True 285 286 # Whether LAST_DAY function supports a date part argument 287 LAST_DAY_SUPPORTS_DATE_PART = True 288 289 # Whether named columns are allowed in table aliases 290 SUPPORTS_TABLE_ALIAS_COLUMNS = True 291 292 # Whether UNPIVOT aliases are Identifiers (False means they're Literals) 293 UNPIVOT_ALIASES_ARE_IDENTIFIERS = True 294 295 # What delimiter to use for separating JSON key/value pairs 296 JSON_KEY_VALUE_PAIR_SEP = ":" 297 298 # INSERT OVERWRITE TABLE x override 299 INSERT_OVERWRITE = " OVERWRITE TABLE" 300 301 # Whether the SELECT .. INTO syntax is used instead of CTAS 302 SUPPORTS_SELECT_INTO = False 303 304 # Whether UNLOGGED tables can be created 305 SUPPORTS_UNLOGGED_TABLES = False 306 307 # Whether the CREATE TABLE LIKE statement is supported 308 SUPPORTS_CREATE_TABLE_LIKE = True 309 310 # Whether the LikeProperty needs to be specified inside of the schema clause 311 LIKE_PROPERTY_INSIDE_SCHEMA = False 312 313 # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be 314 # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args 315 MULTI_ARG_DISTINCT = True 316 317 # Whether the JSON extraction operators expect a value of type JSON 318 JSON_TYPE_REQUIRED_FOR_EXTRACTION = False 319 320 # Whether bracketed keys like ["foo"] are supported in JSON paths 321 JSON_PATH_BRACKETED_KEY_SUPPORTED = True 322 323 # Whether to escape keys using single quotes in JSON paths 324 JSON_PATH_SINGLE_QUOTE_ESCAPE = False 325 326 # The JSONPathPart expressions supported by this dialect 327 SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy() 328 329 # Whether any(f(x) for x in array) can be implemented by this dialect 330 CAN_IMPLEMENT_ARRAY_ANY = False 331 332 # Whether the function TO_NUMBER is supported 333 SUPPORTS_TO_NUMBER = True 334 335 TYPE_MAPPING = { 336 exp.DataType.Type.NCHAR: "CHAR", 337 exp.DataType.Type.NVARCHAR: "VARCHAR", 338 exp.DataType.Type.MEDIUMTEXT: "TEXT", 339 exp.DataType.Type.LONGTEXT: "TEXT", 340 exp.DataType.Type.TINYTEXT: "TEXT", 341 exp.DataType.Type.MEDIUMBLOB: "BLOB", 342 exp.DataType.Type.LONGBLOB: "BLOB", 343 exp.DataType.Type.TINYBLOB: "BLOB", 344 exp.DataType.Type.INET: "INET", 345 } 346 347 STAR_MAPPING = { 348 "except": "EXCEPT", 349 "replace": "REPLACE", 350 } 351 352 TIME_PART_SINGULARS = { 353 "MICROSECONDS": "MICROSECOND", 354 "SECONDS": "SECOND", 355 "MINUTES": "MINUTE", 356 "HOURS": "HOUR", 357 "DAYS": "DAY", 358 "WEEKS": "WEEK", 359 "MONTHS": "MONTH", 360 "QUARTERS": "QUARTER", 361 "YEARS": "YEAR", 362 } 363 364 AFTER_HAVING_MODIFIER_TRANSFORMS = { 365 "cluster": lambda self, e: self.sql(e, "cluster"), 366 "distribute": lambda self, e: self.sql(e, "distribute"), 367 "qualify": lambda self, e: self.sql(e, "qualify"), 368 "sort": lambda self, e: self.sql(e, "sort"), 369 "windows": lambda self, e: ( 370 self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True) 371 if e.args.get("windows") 372 else "" 373 ), 374 } 375 376 TOKEN_MAPPING: t.Dict[TokenType, str] = {} 377 378 STRUCT_DELIMITER = ("<", ">") 379 380 PARAMETER_TOKEN = "@" 381 NAMED_PLACEHOLDER_TOKEN = ":" 382 383 PROPERTIES_LOCATION = { 384 exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE, 385 exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA, 386 exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA, 387 exp.BackupProperty: exp.Properties.Location.POST_SCHEMA, 388 exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME, 389 exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA, 390 exp.ChecksumProperty: exp.Properties.Location.POST_NAME, 391 exp.CollateProperty: exp.Properties.Location.POST_SCHEMA, 392 exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA, 393 exp.Cluster: exp.Properties.Location.POST_SCHEMA, 394 exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA, 395 exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME, 396 exp.DefinerProperty: exp.Properties.Location.POST_CREATE, 397 exp.DictRange: exp.Properties.Location.POST_SCHEMA, 398 exp.DictProperty: exp.Properties.Location.POST_SCHEMA, 399 exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA, 400 exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA, 401 exp.EngineProperty: exp.Properties.Location.POST_SCHEMA, 402 exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA, 403 exp.ExternalProperty: exp.Properties.Location.POST_CREATE, 404 exp.FallbackProperty: exp.Properties.Location.POST_NAME, 405 exp.FileFormatProperty: exp.Properties.Location.POST_WITH, 406 exp.FreespaceProperty: exp.Properties.Location.POST_NAME, 407 exp.GlobalProperty: exp.Properties.Location.POST_CREATE, 408 exp.HeapProperty: exp.Properties.Location.POST_WITH, 409 exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA, 410 exp.IcebergProperty: exp.Properties.Location.POST_CREATE, 411 exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA, 412 exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME, 413 exp.JournalProperty: exp.Properties.Location.POST_NAME, 414 exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA, 415 exp.LikeProperty: exp.Properties.Location.POST_SCHEMA, 416 exp.LocationProperty: exp.Properties.Location.POST_SCHEMA, 417 exp.LockProperty: exp.Properties.Location.POST_SCHEMA, 418 exp.LockingProperty: exp.Properties.Location.POST_ALIAS, 419 exp.LogProperty: exp.Properties.Location.POST_NAME, 420 exp.MaterializedProperty: exp.Properties.Location.POST_CREATE, 421 exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME, 422 exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION, 423 exp.OnProperty: exp.Properties.Location.POST_SCHEMA, 424 exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION, 425 exp.Order: exp.Properties.Location.POST_SCHEMA, 426 exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA, 427 exp.PartitionedByProperty: exp.Properties.Location.POST_WITH, 428 exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA, 429 exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA, 430 exp.Property: exp.Properties.Location.POST_WITH, 431 exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA, 432 exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA, 433 exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA, 434 exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA, 435 exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA, 436 exp.SampleProperty: exp.Properties.Location.POST_SCHEMA, 437 exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA, 438 exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA, 439 exp.Set: exp.Properties.Location.POST_SCHEMA, 440 exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA, 441 exp.SetProperty: exp.Properties.Location.POST_CREATE, 442 exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA, 443 exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION, 444 exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION, 445 exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA, 446 exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA, 447 exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE, 448 exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA, 449 exp.TemporaryProperty: exp.Properties.Location.POST_CREATE, 450 exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA, 451 exp.TransientProperty: exp.Properties.Location.POST_CREATE, 452 exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA, 453 exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA, 454 exp.UnloggedProperty: exp.Properties.Location.POST_CREATE, 455 exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA, 456 exp.VolatileProperty: exp.Properties.Location.POST_CREATE, 457 exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION, 458 exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME, 459 exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA, 460 } 461 462 # Keywords that can't be used as unquoted identifier names 463 RESERVED_KEYWORDS: t.Set[str] = set() 464 465 # Expressions whose comments are separated from them for better formatting 466 WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 467 exp.Create, 468 exp.Delete, 469 exp.Drop, 470 exp.From, 471 exp.Insert, 472 exp.Join, 473 exp.Select, 474 exp.Union, 475 exp.Update, 476 exp.Where, 477 exp.With, 478 ) 479 480 # Expressions that should not have their comments generated in maybe_comment 481 EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 482 exp.Binary, 483 exp.Union, 484 ) 485 486 # Expressions that can remain unwrapped when appearing in the context of an INTERVAL 487 UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = ( 488 exp.Column, 489 exp.Literal, 490 exp.Neg, 491 exp.Paren, 492 ) 493 494 PARAMETERIZABLE_TEXT_TYPES = { 495 exp.DataType.Type.NVARCHAR, 496 exp.DataType.Type.VARCHAR, 497 exp.DataType.Type.CHAR, 498 exp.DataType.Type.NCHAR, 499 } 500 501 # Expressions that need to have all CTEs under them bubbled up to them 502 EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set() 503 504 SENTINEL_LINE_BREAK = "__SQLGLOT__LB__" 505 506 __slots__ = ( 507 "pretty", 508 "identify", 509 "normalize", 510 "pad", 511 "_indent", 512 "normalize_functions", 513 "unsupported_level", 514 "max_unsupported", 515 "leading_comma", 516 "max_text_width", 517 "comments", 518 "dialect", 519 "unsupported_messages", 520 "_escaped_quote_end", 521 "_escaped_identifier_end", 522 ) 523 524 def __init__( 525 self, 526 pretty: t.Optional[bool] = None, 527 identify: str | bool = False, 528 normalize: bool = False, 529 pad: int = 2, 530 indent: int = 2, 531 normalize_functions: t.Optional[str | bool] = None, 532 unsupported_level: ErrorLevel = ErrorLevel.WARN, 533 max_unsupported: int = 3, 534 leading_comma: bool = False, 535 max_text_width: int = 80, 536 comments: bool = True, 537 dialect: DialectType = None, 538 ): 539 import sqlglot 540 from sqlglot.dialects import Dialect 541 542 self.pretty = pretty if pretty is not None else sqlglot.pretty 543 self.identify = identify 544 self.normalize = normalize 545 self.pad = pad 546 self._indent = indent 547 self.unsupported_level = unsupported_level 548 self.max_unsupported = max_unsupported 549 self.leading_comma = leading_comma 550 self.max_text_width = max_text_width 551 self.comments = comments 552 self.dialect = Dialect.get_or_raise(dialect) 553 554 # This is both a Dialect property and a Generator argument, so we prioritize the latter 555 self.normalize_functions = ( 556 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 557 ) 558 559 self.unsupported_messages: t.List[str] = [] 560 self._escaped_quote_end: str = ( 561 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 562 ) 563 self._escaped_identifier_end: str = ( 564 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 565 ) 566 567 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 568 """ 569 Generates the SQL string corresponding to the given syntax tree. 570 571 Args: 572 expression: The syntax tree. 573 copy: Whether to copy the expression. The generator performs mutations so 574 it is safer to copy. 575 576 Returns: 577 The SQL string corresponding to `expression`. 578 """ 579 if copy: 580 expression = expression.copy() 581 582 expression = self.preprocess(expression) 583 584 self.unsupported_messages = [] 585 sql = self.sql(expression).strip() 586 587 if self.pretty: 588 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 589 590 if self.unsupported_level == ErrorLevel.IGNORE: 591 return sql 592 593 if self.unsupported_level == ErrorLevel.WARN: 594 for msg in self.unsupported_messages: 595 logger.warning(msg) 596 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 597 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 598 599 return sql 600 601 def preprocess(self, expression: exp.Expression) -> exp.Expression: 602 """Apply generic preprocessing transformations to a given expression.""" 603 if ( 604 not expression.parent 605 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 606 and any(node.parent is not expression for node in expression.find_all(exp.With)) 607 ): 608 from sqlglot.transforms import move_ctes_to_top_level 609 610 expression = move_ctes_to_top_level(expression) 611 612 if self.ENSURE_BOOLS: 613 from sqlglot.transforms import ensure_bools 614 615 expression = ensure_bools(expression) 616 617 return expression 618 619 def unsupported(self, message: str) -> None: 620 if self.unsupported_level == ErrorLevel.IMMEDIATE: 621 raise UnsupportedError(message) 622 self.unsupported_messages.append(message) 623 624 def sep(self, sep: str = " ") -> str: 625 return f"{sep.strip()}\n" if self.pretty else sep 626 627 def seg(self, sql: str, sep: str = " ") -> str: 628 return f"{self.sep(sep)}{sql}" 629 630 def pad_comment(self, comment: str) -> str: 631 comment = " " + comment if comment[0].strip() else comment 632 comment = comment + " " if comment[-1].strip() else comment 633 return comment 634 635 def maybe_comment( 636 self, 637 sql: str, 638 expression: t.Optional[exp.Expression] = None, 639 comments: t.Optional[t.List[str]] = None, 640 ) -> str: 641 comments = ( 642 ((expression and expression.comments) if comments is None else comments) # type: ignore 643 if self.comments 644 else None 645 ) 646 647 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 648 return sql 649 650 comments_sql = " ".join( 651 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 652 ) 653 654 if not comments_sql: 655 return sql 656 657 if isinstance(expression, self.WITH_SEPARATED_COMMENTS): 658 return ( 659 f"{self.sep()}{comments_sql}{sql}" 660 if not sql or sql[0].isspace() 661 else f"{comments_sql}{self.sep()}{sql}" 662 ) 663 664 return f"{sql} {comments_sql}" 665 666 def wrap(self, expression: exp.Expression | str) -> str: 667 this_sql = self.indent( 668 ( 669 self.sql(expression) 670 if isinstance(expression, exp.UNWRAPPED_QUERIES) 671 else self.sql(expression, "this") 672 ), 673 level=1, 674 pad=0, 675 ) 676 return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}" 677 678 def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str: 679 original = self.identify 680 self.identify = False 681 result = func(*args, **kwargs) 682 self.identify = original 683 return result 684 685 def normalize_func(self, name: str) -> str: 686 if self.normalize_functions == "upper" or self.normalize_functions is True: 687 return name.upper() 688 if self.normalize_functions == "lower": 689 return name.lower() 690 return name 691 692 def indent( 693 self, 694 sql: str, 695 level: int = 0, 696 pad: t.Optional[int] = None, 697 skip_first: bool = False, 698 skip_last: bool = False, 699 ) -> str: 700 if not self.pretty: 701 return sql 702 703 pad = self.pad if pad is None else pad 704 lines = sql.split("\n") 705 706 return "\n".join( 707 ( 708 line 709 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 710 else f"{' ' * (level * self._indent + pad)}{line}" 711 ) 712 for i, line in enumerate(lines) 713 ) 714 715 def sql( 716 self, 717 expression: t.Optional[str | exp.Expression], 718 key: t.Optional[str] = None, 719 comment: bool = True, 720 ) -> str: 721 if not expression: 722 return "" 723 724 if isinstance(expression, str): 725 return expression 726 727 if key: 728 value = expression.args.get(key) 729 if value: 730 return self.sql(value) 731 return "" 732 733 transform = self.TRANSFORMS.get(expression.__class__) 734 735 if callable(transform): 736 sql = transform(self, expression) 737 elif isinstance(expression, exp.Expression): 738 exp_handler_name = f"{expression.key}_sql" 739 740 if hasattr(self, exp_handler_name): 741 sql = getattr(self, exp_handler_name)(expression) 742 elif isinstance(expression, exp.Func): 743 sql = self.function_fallback_sql(expression) 744 elif isinstance(expression, exp.Property): 745 sql = self.property_sql(expression) 746 else: 747 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 748 else: 749 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 750 751 return self.maybe_comment(sql, expression) if self.comments and comment else sql 752 753 def uncache_sql(self, expression: exp.Uncache) -> str: 754 table = self.sql(expression, "this") 755 exists_sql = " IF EXISTS" if expression.args.get("exists") else "" 756 return f"UNCACHE TABLE{exists_sql} {table}" 757 758 def cache_sql(self, expression: exp.Cache) -> str: 759 lazy = " LAZY" if expression.args.get("lazy") else "" 760 table = self.sql(expression, "this") 761 options = expression.args.get("options") 762 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 763 sql = self.sql(expression, "expression") 764 sql = f" AS{self.sep()}{sql}" if sql else "" 765 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 766 return self.prepend_ctes(expression, sql) 767 768 def characterset_sql(self, expression: exp.CharacterSet) -> str: 769 if isinstance(expression.parent, exp.Cast): 770 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 771 default = "DEFAULT " if expression.args.get("default") else "" 772 return f"{default}CHARACTER SET={self.sql(expression, 'this')}" 773 774 def column_sql(self, expression: exp.Column) -> str: 775 join_mark = " (+)" if expression.args.get("join_mark") else "" 776 777 if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED: 778 join_mark = "" 779 self.unsupported("Outer join syntax using the (+) operator is not supported.") 780 781 column = ".".join( 782 self.sql(part) 783 for part in ( 784 expression.args.get("catalog"), 785 expression.args.get("db"), 786 expression.args.get("table"), 787 expression.args.get("this"), 788 ) 789 if part 790 ) 791 792 return f"{column}{join_mark}" 793 794 def columnposition_sql(self, expression: exp.ColumnPosition) -> str: 795 this = self.sql(expression, "this") 796 this = f" {this}" if this else "" 797 position = self.sql(expression, "position") 798 return f"{position}{this}" 799 800 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 801 column = self.sql(expression, "this") 802 kind = self.sql(expression, "kind") 803 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 804 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 805 kind = f"{sep}{kind}" if kind else "" 806 constraints = f" {constraints}" if constraints else "" 807 position = self.sql(expression, "position") 808 position = f" {position}" if position else "" 809 810 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 811 kind = "" 812 813 return f"{exists}{column}{kind}{constraints}{position}" 814 815 def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str: 816 this = self.sql(expression, "this") 817 kind_sql = self.sql(expression, "kind").strip() 818 return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql 819 820 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 821 this = self.sql(expression, "this") 822 if expression.args.get("not_null"): 823 persisted = " PERSISTED NOT NULL" 824 elif expression.args.get("persisted"): 825 persisted = " PERSISTED" 826 else: 827 persisted = "" 828 return f"AS {this}{persisted}" 829 830 def autoincrementcolumnconstraint_sql(self, _) -> str: 831 return self.token_sql(TokenType.AUTO_INCREMENT) 832 833 def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str: 834 if isinstance(expression.this, list): 835 this = self.wrap(self.expressions(expression, key="this", flat=True)) 836 else: 837 this = self.sql(expression, "this") 838 839 return f"COMPRESS {this}" 840 841 def generatedasidentitycolumnconstraint_sql( 842 self, expression: exp.GeneratedAsIdentityColumnConstraint 843 ) -> str: 844 this = "" 845 if expression.this is not None: 846 on_null = " ON NULL" if expression.args.get("on_null") else "" 847 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 848 849 start = expression.args.get("start") 850 start = f"START WITH {start}" if start else "" 851 increment = expression.args.get("increment") 852 increment = f" INCREMENT BY {increment}" if increment else "" 853 minvalue = expression.args.get("minvalue") 854 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 855 maxvalue = expression.args.get("maxvalue") 856 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 857 cycle = expression.args.get("cycle") 858 cycle_sql = "" 859 860 if cycle is not None: 861 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 862 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 863 864 sequence_opts = "" 865 if start or increment or cycle_sql: 866 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 867 sequence_opts = f" ({sequence_opts.strip()})" 868 869 expr = self.sql(expression, "expression") 870 expr = f"({expr})" if expr else "IDENTITY" 871 872 return f"GENERATED{this} AS {expr}{sequence_opts}" 873 874 def generatedasrowcolumnconstraint_sql( 875 self, expression: exp.GeneratedAsRowColumnConstraint 876 ) -> str: 877 start = "START" if expression.args.get("start") else "END" 878 hidden = " HIDDEN" if expression.args.get("hidden") else "" 879 return f"GENERATED ALWAYS AS ROW {start}{hidden}" 880 881 def periodforsystemtimeconstraint_sql( 882 self, expression: exp.PeriodForSystemTimeConstraint 883 ) -> str: 884 return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})" 885 886 def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str: 887 return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL" 888 889 def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str: 890 return f"AS {self.sql(expression, 'this')}" 891 892 def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str: 893 desc = expression.args.get("desc") 894 if desc is not None: 895 return f"PRIMARY KEY{' DESC' if desc else ' ASC'}" 896 return "PRIMARY KEY" 897 898 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 899 this = self.sql(expression, "this") 900 this = f" {this}" if this else "" 901 index_type = expression.args.get("index_type") 902 index_type = f" USING {index_type}" if index_type else "" 903 on_conflict = self.sql(expression, "on_conflict") 904 on_conflict = f" {on_conflict}" if on_conflict else "" 905 return f"UNIQUE{this}{index_type}{on_conflict}" 906 907 def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str: 908 return self.sql(expression, "this") 909 910 def create_sql(self, expression: exp.Create) -> str: 911 kind = self.sql(expression, "kind") 912 properties = expression.args.get("properties") 913 properties_locs = self.locate_properties(properties) if properties else defaultdict() 914 915 this = self.createable_sql(expression, properties_locs) 916 917 properties_sql = "" 918 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 919 exp.Properties.Location.POST_WITH 920 ): 921 properties_sql = self.sql( 922 exp.Properties( 923 expressions=[ 924 *properties_locs[exp.Properties.Location.POST_SCHEMA], 925 *properties_locs[exp.Properties.Location.POST_WITH], 926 ] 927 ) 928 ) 929 930 begin = " BEGIN" if expression.args.get("begin") else "" 931 end = " END" if expression.args.get("end") else "" 932 933 expression_sql = self.sql(expression, "expression") 934 if expression_sql: 935 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 936 937 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 938 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 939 postalias_props_sql = self.properties( 940 exp.Properties( 941 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 942 ), 943 wrapped=False, 944 ) 945 expression_sql = f" AS {postalias_props_sql}{expression_sql}" 946 else: 947 expression_sql = f" AS{expression_sql}" 948 949 postindex_props_sql = "" 950 if properties_locs.get(exp.Properties.Location.POST_INDEX): 951 postindex_props_sql = self.properties( 952 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 953 wrapped=False, 954 prefix=" ", 955 ) 956 957 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 958 indexes = f" {indexes}" if indexes else "" 959 index_sql = indexes + postindex_props_sql 960 961 replace = " OR REPLACE" if expression.args.get("replace") else "" 962 unique = " UNIQUE" if expression.args.get("unique") else "" 963 964 postcreate_props_sql = "" 965 if properties_locs.get(exp.Properties.Location.POST_CREATE): 966 postcreate_props_sql = self.properties( 967 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 968 sep=" ", 969 prefix=" ", 970 wrapped=False, 971 ) 972 973 modifiers = "".join((replace, unique, postcreate_props_sql)) 974 975 postexpression_props_sql = "" 976 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 977 postexpression_props_sql = self.properties( 978 exp.Properties( 979 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 980 ), 981 sep=" ", 982 prefix=" ", 983 wrapped=False, 984 ) 985 986 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 987 no_schema_binding = ( 988 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 989 ) 990 991 clone = self.sql(expression, "clone") 992 clone = f" {clone}" if clone else "" 993 994 expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 995 return self.prepend_ctes(expression, expression_sql) 996 997 def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str: 998 start = self.sql(expression, "start") 999 start = f"START WITH {start}" if start else "" 1000 increment = self.sql(expression, "increment") 1001 increment = f" INCREMENT BY {increment}" if increment else "" 1002 minvalue = self.sql(expression, "minvalue") 1003 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 1004 maxvalue = self.sql(expression, "maxvalue") 1005 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 1006 owned = self.sql(expression, "owned") 1007 owned = f" OWNED BY {owned}" if owned else "" 1008 1009 cache = expression.args.get("cache") 1010 if cache is None: 1011 cache_str = "" 1012 elif cache is True: 1013 cache_str = " CACHE" 1014 else: 1015 cache_str = f" CACHE {cache}" 1016 1017 options = self.expressions(expression, key="options", flat=True, sep=" ") 1018 options = f" {options}" if options else "" 1019 1020 return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip() 1021 1022 def clone_sql(self, expression: exp.Clone) -> str: 1023 this = self.sql(expression, "this") 1024 shallow = "SHALLOW " if expression.args.get("shallow") else "" 1025 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 1026 return f"{shallow}{keyword} {this}" 1027 1028 def describe_sql(self, expression: exp.Describe) -> str: 1029 style = expression.args.get("style") 1030 style = f" {style}" if style else "" 1031 return f"DESCRIBE{style} {self.sql(expression, 'this')}" 1032 1033 def heredoc_sql(self, expression: exp.Heredoc) -> str: 1034 tag = self.sql(expression, "tag") 1035 return f"${tag}${self.sql(expression, 'this')}${tag}$" 1036 1037 def prepend_ctes(self, expression: exp.Expression, sql: str) -> str: 1038 with_ = self.sql(expression, "with") 1039 if with_: 1040 sql = f"{with_}{self.sep()}{sql}" 1041 return sql 1042 1043 def with_sql(self, expression: exp.With) -> str: 1044 sql = self.expressions(expression, flat=True) 1045 recursive = ( 1046 "RECURSIVE " 1047 if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive") 1048 else "" 1049 ) 1050 1051 return f"WITH {recursive}{sql}" 1052 1053 def cte_sql(self, expression: exp.CTE) -> str: 1054 alias = self.sql(expression, "alias") 1055 1056 materialized = expression.args.get("materialized") 1057 if materialized is False: 1058 materialized = "NOT MATERIALIZED " 1059 elif materialized: 1060 materialized = "MATERIALIZED " 1061 1062 return f"{alias} AS {materialized or ''}{self.wrap(expression)}" 1063 1064 def tablealias_sql(self, expression: exp.TableAlias) -> str: 1065 alias = self.sql(expression, "this") 1066 columns = self.expressions(expression, key="columns", flat=True) 1067 columns = f"({columns})" if columns else "" 1068 1069 if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS: 1070 columns = "" 1071 self.unsupported("Named columns are not supported in table alias.") 1072 1073 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 1074 alias = "_t" 1075 1076 return f"{alias}{columns}" 1077 1078 def bitstring_sql(self, expression: exp.BitString) -> str: 1079 this = self.sql(expression, "this") 1080 if self.dialect.BIT_START: 1081 return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}" 1082 return f"{int(this, 2)}" 1083 1084 def hexstring_sql(self, expression: exp.HexString) -> str: 1085 this = self.sql(expression, "this") 1086 if self.dialect.HEX_START: 1087 return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}" 1088 return f"{int(this, 16)}" 1089 1090 def bytestring_sql(self, expression: exp.ByteString) -> str: 1091 this = self.sql(expression, "this") 1092 if self.dialect.BYTE_START: 1093 return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}" 1094 return this 1095 1096 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 1097 this = self.sql(expression, "this") 1098 escape = expression.args.get("escape") 1099 1100 if self.dialect.UNICODE_START: 1101 escape = f" UESCAPE {self.sql(escape)}" if escape else "" 1102 return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}" 1103 1104 if escape: 1105 pattern = re.compile(rf"{escape.name}(\d+)") 1106 else: 1107 pattern = ESCAPED_UNICODE_RE 1108 1109 this = pattern.sub(r"\\u\1", this) 1110 return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}" 1111 1112 def rawstring_sql(self, expression: exp.RawString) -> str: 1113 string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False) 1114 return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}" 1115 1116 def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str: 1117 this = self.sql(expression, "this") 1118 specifier = self.sql(expression, "expression") 1119 specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else "" 1120 return f"{this}{specifier}" 1121 1122 def datatype_sql(self, expression: exp.DataType) -> str: 1123 type_value = expression.this 1124 1125 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 1126 type_sql = self.sql(expression, "kind") 1127 else: 1128 type_sql = ( 1129 self.TYPE_MAPPING.get(type_value, type_value.value) 1130 if isinstance(type_value, exp.DataType.Type) 1131 else type_value 1132 ) 1133 1134 nested = "" 1135 interior = self.expressions(expression, flat=True) 1136 values = "" 1137 1138 if interior: 1139 if expression.args.get("nested"): 1140 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 1141 if expression.args.get("values") is not None: 1142 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 1143 values = self.expressions(expression, key="values", flat=True) 1144 values = f"{delimiters[0]}{values}{delimiters[1]}" 1145 elif type_value == exp.DataType.Type.INTERVAL: 1146 nested = f" {interior}" 1147 else: 1148 nested = f"({interior})" 1149 1150 type_sql = f"{type_sql}{nested}{values}" 1151 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 1152 exp.DataType.Type.TIMETZ, 1153 exp.DataType.Type.TIMESTAMPTZ, 1154 ): 1155 type_sql = f"{type_sql} WITH TIME ZONE" 1156 1157 return type_sql 1158 1159 def directory_sql(self, expression: exp.Directory) -> str: 1160 local = "LOCAL " if expression.args.get("local") else "" 1161 row_format = self.sql(expression, "row_format") 1162 row_format = f" {row_format}" if row_format else "" 1163 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}" 1164 1165 def delete_sql(self, expression: exp.Delete) -> str: 1166 this = self.sql(expression, "this") 1167 this = f" FROM {this}" if this else "" 1168 using = self.sql(expression, "using") 1169 using = f" USING {using}" if using else "" 1170 where = self.sql(expression, "where") 1171 returning = self.sql(expression, "returning") 1172 limit = self.sql(expression, "limit") 1173 tables = self.expressions(expression, key="tables") 1174 tables = f" {tables}" if tables else "" 1175 if self.RETURNING_END: 1176 expression_sql = f"{this}{using}{where}{returning}{limit}" 1177 else: 1178 expression_sql = f"{returning}{this}{using}{where}{limit}" 1179 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}") 1180 1181 def drop_sql(self, expression: exp.Drop) -> str: 1182 this = self.sql(expression, "this") 1183 expressions = self.expressions(expression, flat=True) 1184 expressions = f" ({expressions})" if expressions else "" 1185 kind = expression.args["kind"] 1186 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1187 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1188 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1189 cascade = " CASCADE" if expression.args.get("cascade") else "" 1190 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1191 purge = " PURGE" if expression.args.get("purge") else "" 1192 return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{expressions}{cascade}{constraints}{purge}" 1193 1194 def except_sql(self, expression: exp.Except) -> str: 1195 return self.set_operations(expression) 1196 1197 def except_op(self, expression: exp.Except) -> str: 1198 return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}" 1199 1200 def fetch_sql(self, expression: exp.Fetch) -> str: 1201 direction = expression.args.get("direction") 1202 direction = f" {direction}" if direction else "" 1203 count = expression.args.get("count") 1204 count = f" {count}" if count else "" 1205 if expression.args.get("percent"): 1206 count = f"{count} PERCENT" 1207 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1208 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}" 1209 1210 def filter_sql(self, expression: exp.Filter) -> str: 1211 if self.AGGREGATE_FILTER_SUPPORTED: 1212 this = self.sql(expression, "this") 1213 where = self.sql(expression, "expression").strip() 1214 return f"{this} FILTER({where})" 1215 1216 agg = expression.this 1217 agg_arg = agg.this 1218 cond = expression.expression.this 1219 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1220 return self.sql(agg) 1221 1222 def hint_sql(self, expression: exp.Hint) -> str: 1223 if not self.QUERY_HINTS: 1224 self.unsupported("Hints are not supported") 1225 return "" 1226 1227 return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */" 1228 1229 def indexparameters_sql(self, expression: exp.IndexParameters) -> str: 1230 using = self.sql(expression, "using") 1231 using = f" USING {using}" if using else "" 1232 columns = self.expressions(expression, key="columns", flat=True) 1233 columns = f"({columns})" if columns else "" 1234 partition_by = self.expressions(expression, key="partition_by", flat=True) 1235 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1236 where = self.sql(expression, "where") 1237 include = self.expressions(expression, key="include", flat=True) 1238 if include: 1239 include = f" INCLUDE ({include})" 1240 with_storage = self.expressions(expression, key="with_storage", flat=True) 1241 with_storage = f" WITH ({with_storage})" if with_storage else "" 1242 tablespace = self.sql(expression, "tablespace") 1243 tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else "" 1244 1245 return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}" 1246 1247 def index_sql(self, expression: exp.Index) -> str: 1248 unique = "UNIQUE " if expression.args.get("unique") else "" 1249 primary = "PRIMARY " if expression.args.get("primary") else "" 1250 amp = "AMP " if expression.args.get("amp") else "" 1251 name = self.sql(expression, "this") 1252 name = f"{name} " if name else "" 1253 table = self.sql(expression, "table") 1254 table = f"{self.INDEX_ON} {table}" if table else "" 1255 1256 index = "INDEX " if not table else "" 1257 1258 params = self.sql(expression, "params") 1259 return f"{unique}{primary}{amp}{index}{name}{table}{params}" 1260 1261 def identifier_sql(self, expression: exp.Identifier) -> str: 1262 text = expression.name 1263 lower = text.lower() 1264 text = lower if self.normalize and not expression.quoted else text 1265 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1266 if ( 1267 expression.quoted 1268 or self.dialect.can_identify(text, self.identify) 1269 or lower in self.RESERVED_KEYWORDS 1270 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1271 ): 1272 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1273 return text 1274 1275 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1276 input_format = self.sql(expression, "input_format") 1277 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1278 output_format = self.sql(expression, "output_format") 1279 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1280 return self.sep().join((input_format, output_format)) 1281 1282 def national_sql(self, expression: exp.National, prefix: str = "N") -> str: 1283 string = self.sql(exp.Literal.string(expression.name)) 1284 return f"{prefix}{string}" 1285 1286 def partition_sql(self, expression: exp.Partition) -> str: 1287 return f"PARTITION({self.expressions(expression, flat=True)})" 1288 1289 def properties_sql(self, expression: exp.Properties) -> str: 1290 root_properties = [] 1291 with_properties = [] 1292 1293 for p in expression.expressions: 1294 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1295 if p_loc == exp.Properties.Location.POST_WITH: 1296 with_properties.append(p) 1297 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1298 root_properties.append(p) 1299 1300 return self.root_properties( 1301 exp.Properties(expressions=root_properties) 1302 ) + self.with_properties(exp.Properties(expressions=with_properties)) 1303 1304 def root_properties(self, properties: exp.Properties) -> str: 1305 if properties.expressions: 1306 return self.sep() + self.expressions(properties, indent=False, sep=" ") 1307 return "" 1308 1309 def properties( 1310 self, 1311 properties: exp.Properties, 1312 prefix: str = "", 1313 sep: str = ", ", 1314 suffix: str = "", 1315 wrapped: bool = True, 1316 ) -> str: 1317 if properties.expressions: 1318 expressions = self.expressions(properties, sep=sep, indent=False) 1319 if expressions: 1320 expressions = self.wrap(expressions) if wrapped else expressions 1321 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1322 return "" 1323 1324 def with_properties(self, properties: exp.Properties) -> str: 1325 return self.properties(properties, prefix=self.seg("WITH")) 1326 1327 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1328 properties_locs = defaultdict(list) 1329 for p in properties.expressions: 1330 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1331 if p_loc != exp.Properties.Location.UNSUPPORTED: 1332 properties_locs[p_loc].append(p) 1333 else: 1334 self.unsupported(f"Unsupported property {p.key}") 1335 1336 return properties_locs 1337 1338 def property_name(self, expression: exp.Property, string_key: bool = False) -> str: 1339 if isinstance(expression.this, exp.Dot): 1340 return self.sql(expression, "this") 1341 return f"'{expression.name}'" if string_key else expression.name 1342 1343 def property_sql(self, expression: exp.Property) -> str: 1344 property_cls = expression.__class__ 1345 if property_cls == exp.Property: 1346 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1347 1348 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1349 if not property_name: 1350 self.unsupported(f"Unsupported property {expression.key}") 1351 1352 return f"{property_name}={self.sql(expression, 'this')}" 1353 1354 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1355 if self.SUPPORTS_CREATE_TABLE_LIKE: 1356 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1357 options = f" {options}" if options else "" 1358 1359 like = f"LIKE {self.sql(expression, 'this')}{options}" 1360 if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema): 1361 like = f"({like})" 1362 1363 return like 1364 1365 if expression.expressions: 1366 self.unsupported("Transpilation of LIKE property options is unsupported") 1367 1368 select = exp.select("*").from_(expression.this).limit(0) 1369 return f"AS {self.sql(select)}" 1370 1371 def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str: 1372 no = "NO " if expression.args.get("no") else "" 1373 protection = " PROTECTION" if expression.args.get("protection") else "" 1374 return f"{no}FALLBACK{protection}" 1375 1376 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1377 no = "NO " if expression.args.get("no") else "" 1378 local = expression.args.get("local") 1379 local = f"{local} " if local else "" 1380 dual = "DUAL " if expression.args.get("dual") else "" 1381 before = "BEFORE " if expression.args.get("before") else "" 1382 after = "AFTER " if expression.args.get("after") else "" 1383 return f"{no}{local}{dual}{before}{after}JOURNAL" 1384 1385 def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str: 1386 freespace = self.sql(expression, "this") 1387 percent = " PERCENT" if expression.args.get("percent") else "" 1388 return f"FREESPACE={freespace}{percent}" 1389 1390 def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str: 1391 if expression.args.get("default"): 1392 property = "DEFAULT" 1393 elif expression.args.get("on"): 1394 property = "ON" 1395 else: 1396 property = "OFF" 1397 return f"CHECKSUM={property}" 1398 1399 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1400 if expression.args.get("no"): 1401 return "NO MERGEBLOCKRATIO" 1402 if expression.args.get("default"): 1403 return "DEFAULT MERGEBLOCKRATIO" 1404 1405 percent = " PERCENT" if expression.args.get("percent") else "" 1406 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}" 1407 1408 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1409 default = expression.args.get("default") 1410 minimum = expression.args.get("minimum") 1411 maximum = expression.args.get("maximum") 1412 if default or minimum or maximum: 1413 if default: 1414 prop = "DEFAULT" 1415 elif minimum: 1416 prop = "MINIMUM" 1417 else: 1418 prop = "MAXIMUM" 1419 return f"{prop} DATABLOCKSIZE" 1420 units = expression.args.get("units") 1421 units = f" {units}" if units else "" 1422 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}" 1423 1424 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1425 autotemp = expression.args.get("autotemp") 1426 always = expression.args.get("always") 1427 default = expression.args.get("default") 1428 manual = expression.args.get("manual") 1429 never = expression.args.get("never") 1430 1431 if autotemp is not None: 1432 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1433 elif always: 1434 prop = "ALWAYS" 1435 elif default: 1436 prop = "DEFAULT" 1437 elif manual: 1438 prop = "MANUAL" 1439 elif never: 1440 prop = "NEVER" 1441 return f"BLOCKCOMPRESSION={prop}" 1442 1443 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1444 no = expression.args.get("no") 1445 no = " NO" if no else "" 1446 concurrent = expression.args.get("concurrent") 1447 concurrent = " CONCURRENT" if concurrent else "" 1448 target = self.sql(expression, "target") 1449 target = f" {target}" if target else "" 1450 return f"WITH{no}{concurrent} ISOLATED LOADING{target}" 1451 1452 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1453 if isinstance(expression.this, list): 1454 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1455 if expression.this: 1456 modulus = self.sql(expression, "this") 1457 remainder = self.sql(expression, "expression") 1458 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1459 1460 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1461 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1462 return f"FROM ({from_expressions}) TO ({to_expressions})" 1463 1464 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1465 this = self.sql(expression, "this") 1466 1467 for_values_or_default = expression.expression 1468 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1469 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1470 else: 1471 for_values_or_default = " DEFAULT" 1472 1473 return f"PARTITION OF {this}{for_values_or_default}" 1474 1475 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1476 kind = expression.args.get("kind") 1477 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1478 for_or_in = expression.args.get("for_or_in") 1479 for_or_in = f" {for_or_in}" if for_or_in else "" 1480 lock_type = expression.args.get("lock_type") 1481 override = " OVERRIDE" if expression.args.get("override") else "" 1482 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}" 1483 1484 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1485 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1486 statistics = expression.args.get("statistics") 1487 statistics_sql = "" 1488 if statistics is not None: 1489 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1490 return f"{data_sql}{statistics_sql}" 1491 1492 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1493 sql = "WITH(SYSTEM_VERSIONING=ON" 1494 1495 if expression.this: 1496 history_table = self.sql(expression, "this") 1497 sql = f"{sql}(HISTORY_TABLE={history_table}" 1498 1499 if expression.expression: 1500 data_consistency_check = self.sql(expression, "expression") 1501 sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}" 1502 1503 sql = f"{sql})" 1504 1505 return f"{sql})" 1506 1507 def insert_sql(self, expression: exp.Insert) -> str: 1508 hint = self.sql(expression, "hint") 1509 overwrite = expression.args.get("overwrite") 1510 1511 if isinstance(expression.this, exp.Directory): 1512 this = " OVERWRITE" if overwrite else " INTO" 1513 else: 1514 this = self.INSERT_OVERWRITE if overwrite else " INTO" 1515 1516 alternative = expression.args.get("alternative") 1517 alternative = f" OR {alternative}" if alternative else "" 1518 ignore = " IGNORE" if expression.args.get("ignore") else "" 1519 is_function = expression.args.get("is_function") 1520 if is_function: 1521 this = f"{this} FUNCTION" 1522 this = f"{this} {self.sql(expression, 'this')}" 1523 1524 exists = " IF EXISTS" if expression.args.get("exists") else "" 1525 partition_sql = ( 1526 f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else "" 1527 ) 1528 where = self.sql(expression, "where") 1529 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1530 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1531 on_conflict = self.sql(expression, "conflict") 1532 on_conflict = f" {on_conflict}" if on_conflict else "" 1533 by_name = " BY NAME" if expression.args.get("by_name") else "" 1534 returning = self.sql(expression, "returning") 1535 1536 if self.RETURNING_END: 1537 expression_sql = f"{expression_sql}{on_conflict}{returning}" 1538 else: 1539 expression_sql = f"{returning}{expression_sql}{on_conflict}" 1540 1541 sql = f"INSERT{hint}{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}" 1542 return self.prepend_ctes(expression, sql) 1543 1544 def intersect_sql(self, expression: exp.Intersect) -> str: 1545 return self.set_operations(expression) 1546 1547 def intersect_op(self, expression: exp.Intersect) -> str: 1548 return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}" 1549 1550 def introducer_sql(self, expression: exp.Introducer) -> str: 1551 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 1552 1553 def kill_sql(self, expression: exp.Kill) -> str: 1554 kind = self.sql(expression, "kind") 1555 kind = f" {kind}" if kind else "" 1556 this = self.sql(expression, "this") 1557 this = f" {this}" if this else "" 1558 return f"KILL{kind}{this}" 1559 1560 def pseudotype_sql(self, expression: exp.PseudoType) -> str: 1561 return expression.name 1562 1563 def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str: 1564 return expression.name 1565 1566 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1567 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1568 1569 constraint = self.sql(expression, "constraint") 1570 constraint = f" ON CONSTRAINT {constraint}" if constraint else "" 1571 1572 conflict_keys = self.expressions(expression, key="conflict_keys", flat=True) 1573 conflict_keys = f"({conflict_keys}) " if conflict_keys else " " 1574 action = self.sql(expression, "action") 1575 1576 expressions = self.expressions(expression, flat=True) 1577 if expressions: 1578 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1579 expressions = f" {set_keyword}{expressions}" 1580 1581 return f"{conflict}{constraint}{conflict_keys}{action}{expressions}" 1582 1583 def returning_sql(self, expression: exp.Returning) -> str: 1584 return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}" 1585 1586 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1587 fields = self.sql(expression, "fields") 1588 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1589 escaped = self.sql(expression, "escaped") 1590 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1591 items = self.sql(expression, "collection_items") 1592 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1593 keys = self.sql(expression, "map_keys") 1594 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1595 lines = self.sql(expression, "lines") 1596 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1597 null = self.sql(expression, "null") 1598 null = f" NULL DEFINED AS {null}" if null else "" 1599 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}" 1600 1601 def withtablehint_sql(self, expression: exp.WithTableHint) -> str: 1602 return f"WITH ({self.expressions(expression, flat=True)})" 1603 1604 def indextablehint_sql(self, expression: exp.IndexTableHint) -> str: 1605 this = f"{self.sql(expression, 'this')} INDEX" 1606 target = self.sql(expression, "target") 1607 target = f" FOR {target}" if target else "" 1608 return f"{this}{target} ({self.expressions(expression, flat=True)})" 1609 1610 def historicaldata_sql(self, expression: exp.HistoricalData) -> str: 1611 this = self.sql(expression, "this") 1612 kind = self.sql(expression, "kind") 1613 expr = self.sql(expression, "expression") 1614 return f"{this} ({kind} => {expr})" 1615 1616 def table_parts(self, expression: exp.Table) -> str: 1617 return ".".join( 1618 self.sql(part) 1619 for part in ( 1620 expression.args.get("catalog"), 1621 expression.args.get("db"), 1622 expression.args.get("this"), 1623 ) 1624 if part is not None 1625 ) 1626 1627 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1628 table = self.table_parts(expression) 1629 only = "ONLY " if expression.args.get("only") else "" 1630 version = self.sql(expression, "version") 1631 version = f" {version}" if version else "" 1632 alias = self.sql(expression, "alias") 1633 alias = f"{sep}{alias}" if alias else "" 1634 hints = self.expressions(expression, key="hints", sep=" ") 1635 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1636 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 1637 pivots = f" {pivots}" if pivots else "" 1638 joins = self.indent( 1639 self.expressions(expression, key="joins", sep="", flat=True), skip_first=True 1640 ) 1641 laterals = self.expressions(expression, key="laterals", sep="") 1642 1643 file_format = self.sql(expression, "format") 1644 if file_format: 1645 pattern = self.sql(expression, "pattern") 1646 pattern = f", PATTERN => {pattern}" if pattern else "" 1647 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1648 1649 ordinality = expression.args.get("ordinality") or "" 1650 if ordinality: 1651 ordinality = f" WITH ORDINALITY{alias}" 1652 alias = "" 1653 1654 when = self.sql(expression, "when") 1655 if when: 1656 table = f"{table} {when}" 1657 1658 return f"{only}{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}" 1659 1660 def tablesample_sql( 1661 self, 1662 expression: exp.TableSample, 1663 sep: str = " AS ", 1664 tablesample_keyword: t.Optional[str] = None, 1665 ) -> str: 1666 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1667 table = expression.this.copy() 1668 table.set("alias", None) 1669 this = self.sql(table) 1670 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1671 else: 1672 this = self.sql(expression, "this") 1673 alias = "" 1674 1675 method = self.sql(expression, "method") 1676 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1677 numerator = self.sql(expression, "bucket_numerator") 1678 denominator = self.sql(expression, "bucket_denominator") 1679 field = self.sql(expression, "bucket_field") 1680 field = f" ON {field}" if field else "" 1681 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1682 seed = self.sql(expression, "seed") 1683 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1684 1685 size = self.sql(expression, "size") 1686 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1687 size = f"{size} ROWS" 1688 1689 percent = self.sql(expression, "percent") 1690 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1691 percent = f"{percent} PERCENT" 1692 1693 expr = f"{bucket}{percent}{size}" 1694 if self.TABLESAMPLE_REQUIRES_PARENS: 1695 expr = f"({expr})" 1696 1697 return ( 1698 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1699 ) 1700 1701 def pivot_sql(self, expression: exp.Pivot) -> str: 1702 expressions = self.expressions(expression, flat=True) 1703 1704 if expression.this: 1705 this = self.sql(expression, "this") 1706 if not expressions: 1707 return f"UNPIVOT {this}" 1708 1709 on = f"{self.seg('ON')} {expressions}" 1710 using = self.expressions(expression, key="using", flat=True) 1711 using = f"{self.seg('USING')} {using}" if using else "" 1712 group = self.sql(expression, "group") 1713 return f"PIVOT {this}{on}{using}{group}" 1714 1715 alias = self.sql(expression, "alias") 1716 alias = f" AS {alias}" if alias else "" 1717 direction = "UNPIVOT" if expression.unpivot else "PIVOT" 1718 field = self.sql(expression, "field") 1719 include_nulls = expression.args.get("include_nulls") 1720 if include_nulls is not None: 1721 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1722 else: 1723 nulls = "" 1724 return f"{direction}{nulls}({expressions} FOR {field}){alias}" 1725 1726 def version_sql(self, expression: exp.Version) -> str: 1727 this = f"FOR {expression.name}" 1728 kind = expression.text("kind") 1729 expr = self.sql(expression, "expression") 1730 return f"{this} {kind} {expr}" 1731 1732 def tuple_sql(self, expression: exp.Tuple) -> str: 1733 return f"({self.expressions(expression, flat=True)})" 1734 1735 def update_sql(self, expression: exp.Update) -> str: 1736 this = self.sql(expression, "this") 1737 set_sql = self.expressions(expression, flat=True) 1738 from_sql = self.sql(expression, "from") 1739 where_sql = self.sql(expression, "where") 1740 returning = self.sql(expression, "returning") 1741 order = self.sql(expression, "order") 1742 limit = self.sql(expression, "limit") 1743 if self.RETURNING_END: 1744 expression_sql = f"{from_sql}{where_sql}{returning}" 1745 else: 1746 expression_sql = f"{returning}{from_sql}{where_sql}" 1747 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1748 return self.prepend_ctes(expression, sql) 1749 1750 def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str: 1751 values_as_table = values_as_table and self.VALUES_AS_TABLE 1752 1753 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1754 if values_as_table or not expression.find_ancestor(exp.From, exp.Join): 1755 args = self.expressions(expression) 1756 alias = self.sql(expression, "alias") 1757 values = f"VALUES{self.seg('')}{args}" 1758 values = ( 1759 f"({values})" 1760 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1761 else values 1762 ) 1763 return f"{values} AS {alias}" if alias else values 1764 1765 # Converts `VALUES...` expression into a series of select unions. 1766 alias_node = expression.args.get("alias") 1767 column_names = alias_node and alias_node.columns 1768 1769 selects: t.List[exp.Query] = [] 1770 1771 for i, tup in enumerate(expression.expressions): 1772 row = tup.expressions 1773 1774 if i == 0 and column_names: 1775 row = [ 1776 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1777 ] 1778 1779 selects.append(exp.Select(expressions=row)) 1780 1781 if self.pretty: 1782 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1783 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1784 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1785 query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1786 return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False)) 1787 1788 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1789 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1790 return f"({unions}){alias}" 1791 1792 def var_sql(self, expression: exp.Var) -> str: 1793 return self.sql(expression, "this") 1794 1795 def into_sql(self, expression: exp.Into) -> str: 1796 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1797 unlogged = " UNLOGGED" if expression.args.get("unlogged") else "" 1798 return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}" 1799 1800 def from_sql(self, expression: exp.From) -> str: 1801 return f"{self.seg('FROM')} {self.sql(expression, 'this')}" 1802 1803 def group_sql(self, expression: exp.Group) -> str: 1804 group_by = self.op_expressions("GROUP BY", expression) 1805 1806 if expression.args.get("all"): 1807 return f"{group_by} ALL" 1808 1809 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1810 grouping_sets = ( 1811 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1812 ) 1813 1814 cube = expression.args.get("cube", []) 1815 if seq_get(cube, 0) is True: 1816 return f"{group_by}{self.seg('WITH CUBE')}" 1817 else: 1818 cube_sql = self.expressions(expression, key="cube", indent=False) 1819 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1820 1821 rollup = expression.args.get("rollup", []) 1822 if seq_get(rollup, 0) is True: 1823 return f"{group_by}{self.seg('WITH ROLLUP')}" 1824 else: 1825 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1826 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1827 1828 groupings = csv( 1829 grouping_sets, 1830 cube_sql, 1831 rollup_sql, 1832 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1833 sep=self.GROUPINGS_SEP, 1834 ) 1835 1836 if expression.args.get("expressions") and groupings: 1837 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1838 1839 return f"{group_by}{groupings}" 1840 1841 def having_sql(self, expression: exp.Having) -> str: 1842 this = self.indent(self.sql(expression, "this")) 1843 return f"{self.seg('HAVING')}{self.sep()}{this}" 1844 1845 def connect_sql(self, expression: exp.Connect) -> str: 1846 start = self.sql(expression, "start") 1847 start = self.seg(f"START WITH {start}") if start else "" 1848 nocycle = " NOCYCLE" if expression.args.get("nocycle") else "" 1849 connect = self.sql(expression, "connect") 1850 connect = self.seg(f"CONNECT BY{nocycle} {connect}") 1851 return start + connect 1852 1853 def prior_sql(self, expression: exp.Prior) -> str: 1854 return f"PRIOR {self.sql(expression, 'this')}" 1855 1856 def join_sql(self, expression: exp.Join) -> str: 1857 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1858 side = None 1859 else: 1860 side = expression.side 1861 1862 op_sql = " ".join( 1863 op 1864 for op in ( 1865 expression.method, 1866 "GLOBAL" if expression.args.get("global") else None, 1867 side, 1868 expression.kind, 1869 expression.hint if self.JOIN_HINTS else None, 1870 ) 1871 if op 1872 ) 1873 match_cond = self.sql(expression, "match_condition") 1874 match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else "" 1875 on_sql = self.sql(expression, "on") 1876 using = expression.args.get("using") 1877 1878 if not on_sql and using: 1879 on_sql = csv(*(self.sql(column) for column in using)) 1880 1881 this = expression.this 1882 this_sql = self.sql(this) 1883 1884 if on_sql: 1885 on_sql = self.indent(on_sql, skip_first=True) 1886 space = self.seg(" " * self.pad) if self.pretty else " " 1887 if using: 1888 on_sql = f"{space}USING ({on_sql})" 1889 else: 1890 on_sql = f"{space}ON {on_sql}" 1891 elif not op_sql: 1892 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 1893 return f" {this_sql}" 1894 1895 return f", {this_sql}" 1896 1897 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1898 return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}" 1899 1900 def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str: 1901 args = self.expressions(expression, flat=True) 1902 args = f"({args})" if len(args.split(",")) > 1 else args 1903 return f"{args} {arrow_sep} {self.sql(expression, 'this')}" 1904 1905 def lateral_op(self, expression: exp.Lateral) -> str: 1906 cross_apply = expression.args.get("cross_apply") 1907 1908 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 1909 if cross_apply is True: 1910 op = "INNER JOIN " 1911 elif cross_apply is False: 1912 op = "LEFT JOIN " 1913 else: 1914 op = "" 1915 1916 return f"{op}LATERAL" 1917 1918 def lateral_sql(self, expression: exp.Lateral) -> str: 1919 this = self.sql(expression, "this") 1920 1921 if expression.args.get("view"): 1922 alias = expression.args["alias"] 1923 columns = self.expressions(alias, key="columns", flat=True) 1924 table = f" {alias.name}" if alias.name else "" 1925 columns = f" AS {columns}" if columns else "" 1926 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1927 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1928 1929 alias = self.sql(expression, "alias") 1930 alias = f" AS {alias}" if alias else "" 1931 return f"{self.lateral_op(expression)} {this}{alias}" 1932 1933 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1934 this = self.sql(expression, "this") 1935 1936 args = [ 1937 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1938 for e in (expression.args.get(k) for k in ("offset", "expression")) 1939 if e 1940 ] 1941 1942 args_sql = ", ".join(self.sql(e) for e in args) 1943 args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql 1944 expressions = self.expressions(expression, flat=True) 1945 expressions = f" BY {expressions}" if expressions else "" 1946 1947 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}" 1948 1949 def offset_sql(self, expression: exp.Offset) -> str: 1950 this = self.sql(expression, "this") 1951 value = expression.expression 1952 value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value 1953 expressions = self.expressions(expression, flat=True) 1954 expressions = f" BY {expressions}" if expressions else "" 1955 return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}" 1956 1957 def setitem_sql(self, expression: exp.SetItem) -> str: 1958 kind = self.sql(expression, "kind") 1959 kind = f"{kind} " if kind else "" 1960 this = self.sql(expression, "this") 1961 expressions = self.expressions(expression) 1962 collate = self.sql(expression, "collate") 1963 collate = f" COLLATE {collate}" if collate else "" 1964 global_ = "GLOBAL " if expression.args.get("global") else "" 1965 return f"{global_}{kind}{this}{expressions}{collate}" 1966 1967 def set_sql(self, expression: exp.Set) -> str: 1968 expressions = ( 1969 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 1970 ) 1971 tag = " TAG" if expression.args.get("tag") else "" 1972 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}" 1973 1974 def pragma_sql(self, expression: exp.Pragma) -> str: 1975 return f"PRAGMA {self.sql(expression, 'this')}" 1976 1977 def lock_sql(self, expression: exp.Lock) -> str: 1978 if not self.LOCKING_READS_SUPPORTED: 1979 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 1980 return "" 1981 1982 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 1983 expressions = self.expressions(expression, flat=True) 1984 expressions = f" OF {expressions}" if expressions else "" 1985 wait = expression.args.get("wait") 1986 1987 if wait is not None: 1988 if isinstance(wait, exp.Literal): 1989 wait = f" WAIT {self.sql(wait)}" 1990 else: 1991 wait = " NOWAIT" if wait else " SKIP LOCKED" 1992 1993 return f"{lock_type}{expressions}{wait or ''}" 1994 1995 def literal_sql(self, expression: exp.Literal) -> str: 1996 text = expression.this or "" 1997 if expression.is_string: 1998 text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}" 1999 return text 2000 2001 def escape_str(self, text: str, escape_backslash: bool = True) -> str: 2002 if self.dialect.ESCAPED_SEQUENCES: 2003 to_escaped = self.dialect.ESCAPED_SEQUENCES 2004 text = "".join( 2005 to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text 2006 ) 2007 2008 if self.pretty: 2009 text = text.replace("\n", self.SENTINEL_LINE_BREAK) 2010 2011 return text.replace(self.dialect.QUOTE_END, self._escaped_quote_end) 2012 2013 def loaddata_sql(self, expression: exp.LoadData) -> str: 2014 local = " LOCAL" if expression.args.get("local") else "" 2015 inpath = f" INPATH {self.sql(expression, 'inpath')}" 2016 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 2017 this = f" INTO TABLE {self.sql(expression, 'this')}" 2018 partition = self.sql(expression, "partition") 2019 partition = f" {partition}" if partition else "" 2020 input_format = self.sql(expression, "input_format") 2021 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 2022 serde = self.sql(expression, "serde") 2023 serde = f" SERDE {serde}" if serde else "" 2024 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}" 2025 2026 def null_sql(self, *_) -> str: 2027 return "NULL" 2028 2029 def boolean_sql(self, expression: exp.Boolean) -> str: 2030 return "TRUE" if expression.this else "FALSE" 2031 2032 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 2033 this = self.sql(expression, "this") 2034 this = f"{this} " if this else this 2035 siblings = "SIBLINGS " if expression.args.get("siblings") else "" 2036 order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore 2037 interpolated_values = [ 2038 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 2039 for named_expression in expression.args.get("interpolate") or [] 2040 ] 2041 interpolate = ( 2042 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 2043 ) 2044 return f"{order}{interpolate}" 2045 2046 def withfill_sql(self, expression: exp.WithFill) -> str: 2047 from_sql = self.sql(expression, "from") 2048 from_sql = f" FROM {from_sql}" if from_sql else "" 2049 to_sql = self.sql(expression, "to") 2050 to_sql = f" TO {to_sql}" if to_sql else "" 2051 step_sql = self.sql(expression, "step") 2052 step_sql = f" STEP {step_sql}" if step_sql else "" 2053 return f"WITH FILL{from_sql}{to_sql}{step_sql}" 2054 2055 def cluster_sql(self, expression: exp.Cluster) -> str: 2056 return self.op_expressions("CLUSTER BY", expression) 2057 2058 def distribute_sql(self, expression: exp.Distribute) -> str: 2059 return self.op_expressions("DISTRIBUTE BY", expression) 2060 2061 def sort_sql(self, expression: exp.Sort) -> str: 2062 return self.op_expressions("SORT BY", expression) 2063 2064 def ordered_sql(self, expression: exp.Ordered) -> str: 2065 desc = expression.args.get("desc") 2066 asc = not desc 2067 2068 nulls_first = expression.args.get("nulls_first") 2069 nulls_last = not nulls_first 2070 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 2071 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 2072 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 2073 2074 this = self.sql(expression, "this") 2075 2076 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 2077 nulls_sort_change = "" 2078 if nulls_first and ( 2079 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 2080 ): 2081 nulls_sort_change = " NULLS FIRST" 2082 elif ( 2083 nulls_last 2084 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 2085 and not nulls_are_last 2086 ): 2087 nulls_sort_change = " NULLS LAST" 2088 2089 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 2090 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 2091 window = expression.find_ancestor(exp.Window, exp.Select) 2092 if isinstance(window, exp.Window) and window.args.get("spec"): 2093 self.unsupported( 2094 f"'{nulls_sort_change.strip()}' translation not supported in window functions" 2095 ) 2096 nulls_sort_change = "" 2097 elif self.NULL_ORDERING_SUPPORTED is None: 2098 if expression.this.is_int: 2099 self.unsupported( 2100 f"'{nulls_sort_change.strip()}' translation not supported with positional ordering" 2101 ) 2102 elif not isinstance(expression.this, exp.Rand): 2103 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 2104 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 2105 nulls_sort_change = "" 2106 2107 with_fill = self.sql(expression, "with_fill") 2108 with_fill = f" {with_fill}" if with_fill else "" 2109 2110 return f"{this}{sort_order}{nulls_sort_change}{with_fill}" 2111 2112 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 2113 partition = self.partition_by_sql(expression) 2114 order = self.sql(expression, "order") 2115 measures = self.expressions(expression, key="measures") 2116 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 2117 rows = self.sql(expression, "rows") 2118 rows = self.seg(rows) if rows else "" 2119 after = self.sql(expression, "after") 2120 after = self.seg(after) if after else "" 2121 pattern = self.sql(expression, "pattern") 2122 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 2123 definition_sqls = [ 2124 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 2125 for definition in expression.args.get("define", []) 2126 ] 2127 definitions = self.expressions(sqls=definition_sqls) 2128 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 2129 body = "".join( 2130 ( 2131 partition, 2132 order, 2133 measures, 2134 rows, 2135 after, 2136 pattern, 2137 define, 2138 ) 2139 ) 2140 alias = self.sql(expression, "alias") 2141 alias = f" {alias}" if alias else "" 2142 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}" 2143 2144 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 2145 limit = expression.args.get("limit") 2146 2147 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 2148 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 2149 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 2150 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 2151 2152 options = self.expressions(expression, key="options") 2153 if options: 2154 options = f" OPTION{self.wrap(options)}" 2155 2156 return csv( 2157 *sqls, 2158 *[self.sql(join) for join in expression.args.get("joins") or []], 2159 self.sql(expression, "connect"), 2160 self.sql(expression, "match"), 2161 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 2162 self.sql(expression, "prewhere"), 2163 self.sql(expression, "where"), 2164 self.sql(expression, "group"), 2165 self.sql(expression, "having"), 2166 *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()], 2167 self.sql(expression, "order"), 2168 *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit), 2169 *self.after_limit_modifiers(expression), 2170 options, 2171 sep="", 2172 ) 2173 2174 def queryoption_sql(self, expression: exp.QueryOption) -> str: 2175 return "" 2176 2177 def offset_limit_modifiers( 2178 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 2179 ) -> t.List[str]: 2180 return [ 2181 self.sql(expression, "offset") if fetch else self.sql(limit), 2182 self.sql(limit) if fetch else self.sql(expression, "offset"), 2183 ] 2184 2185 def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]: 2186 locks = self.expressions(expression, key="locks", sep=" ") 2187 locks = f" {locks}" if locks else "" 2188 return [locks, self.sql(expression, "sample")] 2189 2190 def select_sql(self, expression: exp.Select) -> str: 2191 into = expression.args.get("into") 2192 if not self.SUPPORTS_SELECT_INTO and into: 2193 into.pop() 2194 2195 hint = self.sql(expression, "hint") 2196 distinct = self.sql(expression, "distinct") 2197 distinct = f" {distinct}" if distinct else "" 2198 kind = self.sql(expression, "kind") 2199 2200 limit = expression.args.get("limit") 2201 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP: 2202 top = self.limit_sql(limit, top=True) 2203 limit.pop() 2204 else: 2205 top = "" 2206 2207 expressions = self.expressions(expression) 2208 2209 if kind: 2210 if kind in self.SELECT_KINDS: 2211 kind = f" AS {kind}" 2212 else: 2213 if kind == "STRUCT": 2214 expressions = self.expressions( 2215 sqls=[ 2216 self.sql( 2217 exp.Struct( 2218 expressions=[ 2219 exp.PropertyEQ(this=e.args.get("alias"), expression=e.this) 2220 if isinstance(e, exp.Alias) 2221 else e 2222 for e in expression.expressions 2223 ] 2224 ) 2225 ) 2226 ] 2227 ) 2228 kind = "" 2229 2230 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2231 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2232 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2233 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2234 sql = self.query_modifiers( 2235 expression, 2236 f"SELECT{top_distinct}{kind}{expressions}", 2237 self.sql(expression, "into", comment=False), 2238 self.sql(expression, "from", comment=False), 2239 ) 2240 2241 sql = self.prepend_ctes(expression, sql) 2242 2243 if not self.SUPPORTS_SELECT_INTO and into: 2244 if into.args.get("temporary"): 2245 table_kind = " TEMPORARY" 2246 elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"): 2247 table_kind = " UNLOGGED" 2248 else: 2249 table_kind = "" 2250 sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}" 2251 2252 return sql 2253 2254 def schema_sql(self, expression: exp.Schema) -> str: 2255 this = self.sql(expression, "this") 2256 sql = self.schema_columns_sql(expression) 2257 return f"{this} {sql}" if this and sql else this or sql 2258 2259 def schema_columns_sql(self, expression: exp.Schema) -> str: 2260 if expression.expressions: 2261 return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}" 2262 return "" 2263 2264 def star_sql(self, expression: exp.Star) -> str: 2265 except_ = self.expressions(expression, key="except", flat=True) 2266 except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else "" 2267 replace = self.expressions(expression, key="replace", flat=True) 2268 replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else "" 2269 return f"*{except_}{replace}" 2270 2271 def parameter_sql(self, expression: exp.Parameter) -> str: 2272 this = self.sql(expression, "this") 2273 return f"{self.PARAMETER_TOKEN}{this}" 2274 2275 def sessionparameter_sql(self, expression: exp.SessionParameter) -> str: 2276 this = self.sql(expression, "this") 2277 kind = expression.text("kind") 2278 if kind: 2279 kind = f"{kind}." 2280 return f"@@{kind}{this}" 2281 2282 def placeholder_sql(self, expression: exp.Placeholder) -> str: 2283 return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?" 2284 2285 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2286 alias = self.sql(expression, "alias") 2287 alias = f"{sep}{alias}" if alias else "" 2288 2289 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2290 pivots = f" {pivots}" if pivots else "" 2291 2292 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2293 return self.prepend_ctes(expression, sql) 2294 2295 def qualify_sql(self, expression: exp.Qualify) -> str: 2296 this = self.indent(self.sql(expression, "this")) 2297 return f"{self.seg('QUALIFY')}{self.sep()}{this}" 2298 2299 def set_operations(self, expression: exp.Union) -> str: 2300 sqls: t.List[str] = [] 2301 stack: t.List[t.Union[str, exp.Expression]] = [expression] 2302 2303 while stack: 2304 node = stack.pop() 2305 2306 if isinstance(node, exp.Union): 2307 stack.append(node.expression) 2308 stack.append( 2309 self.maybe_comment( 2310 getattr(self, f"{node.key}_op")(node), 2311 expression=node.this, 2312 comments=node.comments, 2313 ) 2314 ) 2315 stack.append(node.this) 2316 else: 2317 sqls.append(self.sql(node)) 2318 2319 this = self.sep().join(sqls) 2320 this = self.query_modifiers(expression, this) 2321 return self.prepend_ctes(expression, this) 2322 2323 def union_sql(self, expression: exp.Union) -> str: 2324 return self.set_operations(expression) 2325 2326 def union_op(self, expression: exp.Union) -> str: 2327 kind = " DISTINCT" if self.EXPLICIT_UNION else "" 2328 kind = kind if expression.args.get("distinct") else " ALL" 2329 by_name = " BY NAME" if expression.args.get("by_name") else "" 2330 return f"UNION{kind}{by_name}" 2331 2332 def unnest_sql(self, expression: exp.Unnest) -> str: 2333 args = self.expressions(expression, flat=True) 2334 2335 alias = expression.args.get("alias") 2336 offset = expression.args.get("offset") 2337 2338 if self.UNNEST_WITH_ORDINALITY: 2339 if alias and isinstance(offset, exp.Expression): 2340 alias.append("columns", offset) 2341 2342 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2343 columns = alias.columns 2344 alias = self.sql(columns[0]) if columns else "" 2345 else: 2346 alias = self.sql(alias) 2347 2348 alias = f" AS {alias}" if alias else alias 2349 if self.UNNEST_WITH_ORDINALITY: 2350 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2351 else: 2352 if isinstance(offset, exp.Expression): 2353 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2354 elif offset: 2355 suffix = f"{alias} WITH OFFSET" 2356 else: 2357 suffix = alias 2358 2359 return f"UNNEST({args}){suffix}" 2360 2361 def prewhere_sql(self, expression: exp.PreWhere) -> str: 2362 return "" 2363 2364 def where_sql(self, expression: exp.Where) -> str: 2365 this = self.indent(self.sql(expression, "this")) 2366 return f"{self.seg('WHERE')}{self.sep()}{this}" 2367 2368 def window_sql(self, expression: exp.Window) -> str: 2369 this = self.sql(expression, "this") 2370 partition = self.partition_by_sql(expression) 2371 order = expression.args.get("order") 2372 order = self.order_sql(order, flat=True) if order else "" 2373 spec = self.sql(expression, "spec") 2374 alias = self.sql(expression, "alias") 2375 over = self.sql(expression, "over") or "OVER" 2376 2377 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2378 2379 first = expression.args.get("first") 2380 if first is None: 2381 first = "" 2382 else: 2383 first = "FIRST" if first else "LAST" 2384 2385 if not partition and not order and not spec and alias: 2386 return f"{this} {alias}" 2387 2388 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2389 return f"{this} ({args})" 2390 2391 def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str: 2392 partition = self.expressions(expression, key="partition_by", flat=True) 2393 return f"PARTITION BY {partition}" if partition else "" 2394 2395 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2396 kind = self.sql(expression, "kind") 2397 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2398 end = ( 2399 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2400 or "CURRENT ROW" 2401 ) 2402 return f"{kind} BETWEEN {start} AND {end}" 2403 2404 def withingroup_sql(self, expression: exp.WithinGroup) -> str: 2405 this = self.sql(expression, "this") 2406 expression_sql = self.sql(expression, "expression")[1:] # order has a leading space 2407 return f"{this} WITHIN GROUP ({expression_sql})" 2408 2409 def between_sql(self, expression: exp.Between) -> str: 2410 this = self.sql(expression, "this") 2411 low = self.sql(expression, "low") 2412 high = self.sql(expression, "high") 2413 return f"{this} BETWEEN {low} AND {high}" 2414 2415 def bracket_sql(self, expression: exp.Bracket) -> str: 2416 expressions = apply_index_offset( 2417 expression.this, 2418 expression.expressions, 2419 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2420 ) 2421 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2422 return f"{self.sql(expression, 'this')}[{expressions_sql}]" 2423 2424 def all_sql(self, expression: exp.All) -> str: 2425 return f"ALL {self.wrap(expression)}" 2426 2427 def any_sql(self, expression: exp.Any) -> str: 2428 this = self.sql(expression, "this") 2429 if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)): 2430 if isinstance(expression.this, exp.UNWRAPPED_QUERIES): 2431 this = self.wrap(this) 2432 return f"ANY{this}" 2433 return f"ANY {this}" 2434 2435 def exists_sql(self, expression: exp.Exists) -> str: 2436 return f"EXISTS{self.wrap(expression)}" 2437 2438 def case_sql(self, expression: exp.Case) -> str: 2439 this = self.sql(expression, "this") 2440 statements = [f"CASE {this}" if this else "CASE"] 2441 2442 for e in expression.args["ifs"]: 2443 statements.append(f"WHEN {self.sql(e, 'this')}") 2444 statements.append(f"THEN {self.sql(e, 'true')}") 2445 2446 default = self.sql(expression, "default") 2447 2448 if default: 2449 statements.append(f"ELSE {default}") 2450 2451 statements.append("END") 2452 2453 if self.pretty and self.text_width(statements) > self.max_text_width: 2454 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2455 2456 return " ".join(statements) 2457 2458 def constraint_sql(self, expression: exp.Constraint) -> str: 2459 this = self.sql(expression, "this") 2460 expressions = self.expressions(expression, flat=True) 2461 return f"CONSTRAINT {this} {expressions}" 2462 2463 def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str: 2464 order = expression.args.get("order") 2465 order = f" OVER ({self.order_sql(order, flat=True)})" if order else "" 2466 return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}" 2467 2468 def extract_sql(self, expression: exp.Extract) -> str: 2469 this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name 2470 expression_sql = self.sql(expression, "expression") 2471 return f"EXTRACT({this} FROM {expression_sql})" 2472 2473 def trim_sql(self, expression: exp.Trim) -> str: 2474 trim_type = self.sql(expression, "position") 2475 2476 if trim_type == "LEADING": 2477 return self.func("LTRIM", expression.this) 2478 elif trim_type == "TRAILING": 2479 return self.func("RTRIM", expression.this) 2480 else: 2481 return self.func("TRIM", expression.this, expression.expression) 2482 2483 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2484 args = expression.expressions 2485 if isinstance(expression, exp.ConcatWs): 2486 args = args[1:] # Skip the delimiter 2487 2488 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2489 args = [exp.cast(e, "text") for e in args] 2490 2491 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2492 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2493 2494 return args 2495 2496 def concat_sql(self, expression: exp.Concat) -> str: 2497 expressions = self.convert_concat_args(expression) 2498 2499 # Some dialects don't allow a single-argument CONCAT call 2500 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2501 return self.sql(expressions[0]) 2502 2503 return self.func("CONCAT", *expressions) 2504 2505 def concatws_sql(self, expression: exp.ConcatWs) -> str: 2506 return self.func( 2507 "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression) 2508 ) 2509 2510 def check_sql(self, expression: exp.Check) -> str: 2511 this = self.sql(expression, key="this") 2512 return f"CHECK ({this})" 2513 2514 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2515 expressions = self.expressions(expression, flat=True) 2516 reference = self.sql(expression, "reference") 2517 reference = f" {reference}" if reference else "" 2518 delete = self.sql(expression, "delete") 2519 delete = f" ON DELETE {delete}" if delete else "" 2520 update = self.sql(expression, "update") 2521 update = f" ON UPDATE {update}" if update else "" 2522 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}" 2523 2524 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2525 expressions = self.expressions(expression, flat=True) 2526 options = self.expressions(expression, key="options", flat=True, sep=" ") 2527 options = f" {options}" if options else "" 2528 return f"PRIMARY KEY ({expressions}){options}" 2529 2530 def if_sql(self, expression: exp.If) -> str: 2531 return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false"))) 2532 2533 def matchagainst_sql(self, expression: exp.MatchAgainst) -> str: 2534 modifier = expression.args.get("modifier") 2535 modifier = f" {modifier}" if modifier else "" 2536 return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})" 2537 2538 def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str: 2539 return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}" 2540 2541 def jsonpath_sql(self, expression: exp.JSONPath) -> str: 2542 path = self.expressions(expression, sep="", flat=True).lstrip(".") 2543 return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}" 2544 2545 def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str: 2546 if isinstance(expression, exp.JSONPathPart): 2547 transform = self.TRANSFORMS.get(expression.__class__) 2548 if not callable(transform): 2549 self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}") 2550 return "" 2551 2552 return transform(self, expression) 2553 2554 if isinstance(expression, int): 2555 return str(expression) 2556 2557 if self.JSON_PATH_SINGLE_QUOTE_ESCAPE: 2558 escaped = expression.replace("'", "\\'") 2559 escaped = f"\\'{expression}\\'" 2560 else: 2561 escaped = expression.replace('"', '\\"') 2562 escaped = f'"{escaped}"' 2563 2564 return escaped 2565 2566 def formatjson_sql(self, expression: exp.FormatJson) -> str: 2567 return f"{self.sql(expression, 'this')} FORMAT JSON" 2568 2569 def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str: 2570 null_handling = expression.args.get("null_handling") 2571 null_handling = f" {null_handling}" if null_handling else "" 2572 2573 unique_keys = expression.args.get("unique_keys") 2574 if unique_keys is not None: 2575 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2576 else: 2577 unique_keys = "" 2578 2579 return_type = self.sql(expression, "return_type") 2580 return_type = f" RETURNING {return_type}" if return_type else "" 2581 encoding = self.sql(expression, "encoding") 2582 encoding = f" ENCODING {encoding}" if encoding else "" 2583 2584 return self.func( 2585 "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG", 2586 *expression.expressions, 2587 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2588 ) 2589 2590 def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str: 2591 return self.jsonobject_sql(expression) 2592 2593 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2594 null_handling = expression.args.get("null_handling") 2595 null_handling = f" {null_handling}" if null_handling else "" 2596 return_type = self.sql(expression, "return_type") 2597 return_type = f" RETURNING {return_type}" if return_type else "" 2598 strict = " STRICT" if expression.args.get("strict") else "" 2599 return self.func( 2600 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2601 ) 2602 2603 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2604 this = self.sql(expression, "this") 2605 order = self.sql(expression, "order") 2606 null_handling = expression.args.get("null_handling") 2607 null_handling = f" {null_handling}" if null_handling else "" 2608 return_type = self.sql(expression, "return_type") 2609 return_type = f" RETURNING {return_type}" if return_type else "" 2610 strict = " STRICT" if expression.args.get("strict") else "" 2611 return self.func( 2612 "JSON_ARRAYAGG", 2613 this, 2614 suffix=f"{order}{null_handling}{return_type}{strict})", 2615 ) 2616 2617 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2618 path = self.sql(expression, "path") 2619 path = f" PATH {path}" if path else "" 2620 nested_schema = self.sql(expression, "nested_schema") 2621 2622 if nested_schema: 2623 return f"NESTED{path} {nested_schema}" 2624 2625 this = self.sql(expression, "this") 2626 kind = self.sql(expression, "kind") 2627 kind = f" {kind}" if kind else "" 2628 return f"{this}{kind}{path}" 2629 2630 def jsonschema_sql(self, expression: exp.JSONSchema) -> str: 2631 return self.func("COLUMNS", *expression.expressions) 2632 2633 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2634 this = self.sql(expression, "this") 2635 path = self.sql(expression, "path") 2636 path = f", {path}" if path else "" 2637 error_handling = expression.args.get("error_handling") 2638 error_handling = f" {error_handling}" if error_handling else "" 2639 empty_handling = expression.args.get("empty_handling") 2640 empty_handling = f" {empty_handling}" if empty_handling else "" 2641 schema = self.sql(expression, "schema") 2642 return self.func( 2643 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2644 ) 2645 2646 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2647 this = self.sql(expression, "this") 2648 kind = self.sql(expression, "kind") 2649 path = self.sql(expression, "path") 2650 path = f" {path}" if path else "" 2651 as_json = " AS JSON" if expression.args.get("as_json") else "" 2652 return f"{this} {kind}{path}{as_json}" 2653 2654 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2655 this = self.sql(expression, "this") 2656 path = self.sql(expression, "path") 2657 path = f", {path}" if path else "" 2658 expressions = self.expressions(expression) 2659 with_ = ( 2660 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2661 if expressions 2662 else "" 2663 ) 2664 return f"OPENJSON({this}{path}){with_}" 2665 2666 def in_sql(self, expression: exp.In) -> str: 2667 query = expression.args.get("query") 2668 unnest = expression.args.get("unnest") 2669 field = expression.args.get("field") 2670 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2671 2672 if query: 2673 in_sql = self.wrap(self.sql(query)) 2674 elif unnest: 2675 in_sql = self.in_unnest_op(unnest) 2676 elif field: 2677 in_sql = self.sql(field) 2678 else: 2679 in_sql = f"({self.expressions(expression, flat=True)})" 2680 2681 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}" 2682 2683 def in_unnest_op(self, unnest: exp.Unnest) -> str: 2684 return f"(SELECT {self.sql(unnest)})" 2685 2686 def interval_sql(self, expression: exp.Interval) -> str: 2687 unit = self.sql(expression, "unit") 2688 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2689 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2690 unit = f" {unit}" if unit else "" 2691 2692 if self.SINGLE_STRING_INTERVAL: 2693 this = expression.this.name if expression.this else "" 2694 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2695 2696 this = self.sql(expression, "this") 2697 if this: 2698 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2699 this = f" {this}" if unwrapped else f" ({this})" 2700 2701 return f"INTERVAL{this}{unit}" 2702 2703 def return_sql(self, expression: exp.Return) -> str: 2704 return f"RETURN {self.sql(expression, 'this')}" 2705 2706 def reference_sql(self, expression: exp.Reference) -> str: 2707 this = self.sql(expression, "this") 2708 expressions = self.expressions(expression, flat=True) 2709 expressions = f"({expressions})" if expressions else "" 2710 options = self.expressions(expression, key="options", flat=True, sep=" ") 2711 options = f" {options}" if options else "" 2712 return f"REFERENCES {this}{expressions}{options}" 2713 2714 def anonymous_sql(self, expression: exp.Anonymous) -> str: 2715 return self.func(self.sql(expression, "this"), *expression.expressions) 2716 2717 def paren_sql(self, expression: exp.Paren) -> str: 2718 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2719 return f"({sql}{self.seg(')', sep='')}" 2720 2721 def neg_sql(self, expression: exp.Neg) -> str: 2722 # This makes sure we don't convert "- - 5" to "--5", which is a comment 2723 this_sql = self.sql(expression, "this") 2724 sep = " " if this_sql[0] == "-" else "" 2725 return f"-{sep}{this_sql}" 2726 2727 def not_sql(self, expression: exp.Not) -> str: 2728 return f"NOT {self.sql(expression, 'this')}" 2729 2730 def alias_sql(self, expression: exp.Alias) -> str: 2731 alias = self.sql(expression, "alias") 2732 alias = f" AS {alias}" if alias else "" 2733 return f"{self.sql(expression, 'this')}{alias}" 2734 2735 def pivotalias_sql(self, expression: exp.PivotAlias) -> str: 2736 alias = expression.args["alias"] 2737 identifier_alias = isinstance(alias, exp.Identifier) 2738 2739 if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2740 alias.replace(exp.Literal.string(alias.output_name)) 2741 elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2742 alias.replace(exp.to_identifier(alias.output_name)) 2743 2744 return self.alias_sql(expression) 2745 2746 def aliases_sql(self, expression: exp.Aliases) -> str: 2747 return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})" 2748 2749 def atindex_sql(self, expression: exp.AtTimeZone) -> str: 2750 this = self.sql(expression, "this") 2751 index = self.sql(expression, "expression") 2752 return f"{this} AT {index}" 2753 2754 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 2755 this = self.sql(expression, "this") 2756 zone = self.sql(expression, "zone") 2757 return f"{this} AT TIME ZONE {zone}" 2758 2759 def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str: 2760 this = self.sql(expression, "this") 2761 zone = self.sql(expression, "zone") 2762 return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'" 2763 2764 def add_sql(self, expression: exp.Add) -> str: 2765 return self.binary(expression, "+") 2766 2767 def and_sql( 2768 self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None 2769 ) -> str: 2770 return self.connector_sql(expression, "AND", stack) 2771 2772 def or_sql( 2773 self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None 2774 ) -> str: 2775 return self.connector_sql(expression, "OR", stack) 2776 2777 def xor_sql( 2778 self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None 2779 ) -> str: 2780 return self.connector_sql(expression, "XOR", stack) 2781 2782 def connector_sql( 2783 self, 2784 expression: exp.Connector, 2785 op: str, 2786 stack: t.Optional[t.List[str | exp.Expression]] = None, 2787 ) -> str: 2788 if stack is not None: 2789 if expression.expressions: 2790 stack.append(self.expressions(expression, sep=f" {op} ")) 2791 else: 2792 stack.append(expression.right) 2793 if expression.comments: 2794 for comment in expression.comments: 2795 op += f" /*{self.pad_comment(comment)}*/" 2796 stack.extend((op, expression.left)) 2797 return op 2798 2799 stack = [expression] 2800 sqls: t.List[str] = [] 2801 ops = set() 2802 2803 while stack: 2804 node = stack.pop() 2805 if isinstance(node, exp.Connector): 2806 ops.add(getattr(self, f"{node.key}_sql")(node, stack)) 2807 else: 2808 sql = self.sql(node) 2809 if sqls and sqls[-1] in ops: 2810 sqls[-1] += f" {sql}" 2811 else: 2812 sqls.append(sql) 2813 2814 sep = "\n" if self.pretty and self.text_width(sqls) > self.max_text_width else " " 2815 return sep.join(sqls) 2816 2817 def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str: 2818 return self.binary(expression, "&") 2819 2820 def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str: 2821 return self.binary(expression, "<<") 2822 2823 def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str: 2824 return f"~{self.sql(expression, 'this')}" 2825 2826 def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str: 2827 return self.binary(expression, "|") 2828 2829 def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str: 2830 return self.binary(expression, ">>") 2831 2832 def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str: 2833 return self.binary(expression, "^") 2834 2835 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2836 format_sql = self.sql(expression, "format") 2837 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2838 to_sql = self.sql(expression, "to") 2839 to_sql = f" {to_sql}" if to_sql else "" 2840 action = self.sql(expression, "action") 2841 action = f" {action}" if action else "" 2842 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql}{action})" 2843 2844 def currentdate_sql(self, expression: exp.CurrentDate) -> str: 2845 zone = self.sql(expression, "this") 2846 return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE" 2847 2848 def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str: 2849 return self.func("CURRENT_TIMESTAMP", expression.this) 2850 2851 def collate_sql(self, expression: exp.Collate) -> str: 2852 if self.COLLATE_IS_FUNC: 2853 return self.function_fallback_sql(expression) 2854 return self.binary(expression, "COLLATE") 2855 2856 def command_sql(self, expression: exp.Command) -> str: 2857 return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}" 2858 2859 def comment_sql(self, expression: exp.Comment) -> str: 2860 this = self.sql(expression, "this") 2861 kind = expression.args["kind"] 2862 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2863 expression_sql = self.sql(expression, "expression") 2864 return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}" 2865 2866 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2867 this = self.sql(expression, "this") 2868 delete = " DELETE" if expression.args.get("delete") else "" 2869 recompress = self.sql(expression, "recompress") 2870 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2871 to_disk = self.sql(expression, "to_disk") 2872 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2873 to_volume = self.sql(expression, "to_volume") 2874 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2875 return f"{this}{delete}{recompress}{to_disk}{to_volume}" 2876 2877 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2878 where = self.sql(expression, "where") 2879 group = self.sql(expression, "group") 2880 aggregates = self.expressions(expression, key="aggregates") 2881 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2882 2883 if not (where or group or aggregates) and len(expression.expressions) == 1: 2884 return f"TTL {self.expressions(expression, flat=True)}" 2885 2886 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}" 2887 2888 def transaction_sql(self, expression: exp.Transaction) -> str: 2889 return "BEGIN" 2890 2891 def commit_sql(self, expression: exp.Commit) -> str: 2892 chain = expression.args.get("chain") 2893 if chain is not None: 2894 chain = " AND CHAIN" if chain else " AND NO CHAIN" 2895 2896 return f"COMMIT{chain or ''}" 2897 2898 def rollback_sql(self, expression: exp.Rollback) -> str: 2899 savepoint = expression.args.get("savepoint") 2900 savepoint = f" TO {savepoint}" if savepoint else "" 2901 return f"ROLLBACK{savepoint}" 2902 2903 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2904 this = self.sql(expression, "this") 2905 2906 dtype = self.sql(expression, "dtype") 2907 if dtype: 2908 collate = self.sql(expression, "collate") 2909 collate = f" COLLATE {collate}" if collate else "" 2910 using = self.sql(expression, "using") 2911 using = f" USING {using}" if using else "" 2912 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2913 2914 default = self.sql(expression, "default") 2915 if default: 2916 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2917 2918 comment = self.sql(expression, "comment") 2919 if comment: 2920 return f"ALTER COLUMN {this} COMMENT {comment}" 2921 2922 if not expression.args.get("drop"): 2923 self.unsupported("Unsupported ALTER COLUMN syntax") 2924 2925 return f"ALTER COLUMN {this} DROP DEFAULT" 2926 2927 def renametable_sql(self, expression: exp.RenameTable) -> str: 2928 if not self.RENAME_TABLE_WITH_DB: 2929 # Remove db from tables 2930 expression = expression.transform( 2931 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 2932 ).assert_is(exp.RenameTable) 2933 this = self.sql(expression, "this") 2934 return f"RENAME TO {this}" 2935 2936 def renamecolumn_sql(self, expression: exp.RenameColumn) -> str: 2937 exists = " IF EXISTS" if expression.args.get("exists") else "" 2938 old_column = self.sql(expression, "this") 2939 new_column = self.sql(expression, "to") 2940 return f"RENAME COLUMN{exists} {old_column} TO {new_column}" 2941 2942 def altertable_sql(self, expression: exp.AlterTable) -> str: 2943 actions = expression.args["actions"] 2944 2945 if isinstance(actions[0], exp.ColumnDef): 2946 actions = self.add_column_sql(expression) 2947 elif isinstance(actions[0], exp.Schema): 2948 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 2949 elif isinstance(actions[0], exp.Delete): 2950 actions = self.expressions(expression, key="actions", flat=True) 2951 else: 2952 actions = self.expressions(expression, key="actions", flat=True) 2953 2954 exists = " IF EXISTS" if expression.args.get("exists") else "" 2955 only = " ONLY" if expression.args.get("only") else "" 2956 options = self.expressions(expression, key="options") 2957 options = f", {options}" if options else "" 2958 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}" 2959 2960 def add_column_sql(self, expression: exp.AlterTable) -> str: 2961 if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD: 2962 return self.expressions( 2963 expression, 2964 key="actions", 2965 prefix="ADD COLUMN ", 2966 ) 2967 return f"ADD {self.expressions(expression, key='actions', flat=True)}" 2968 2969 def droppartition_sql(self, expression: exp.DropPartition) -> str: 2970 expressions = self.expressions(expression) 2971 exists = " IF EXISTS " if expression.args.get("exists") else " " 2972 return f"DROP{exists}{expressions}" 2973 2974 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 2975 return f"ADD {self.expressions(expression)}" 2976 2977 def distinct_sql(self, expression: exp.Distinct) -> str: 2978 this = self.expressions(expression, flat=True) 2979 2980 if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1: 2981 case = exp.case() 2982 for arg in expression.expressions: 2983 case = case.when(arg.is_(exp.null()), exp.null()) 2984 this = self.sql(case.else_(f"({this})")) 2985 2986 this = f" {this}" if this else "" 2987 2988 on = self.sql(expression, "on") 2989 on = f" ON {on}" if on else "" 2990 return f"DISTINCT{this}{on}" 2991 2992 def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: 2993 return self._embed_ignore_nulls(expression, "IGNORE NULLS") 2994 2995 def respectnulls_sql(self, expression: exp.RespectNulls) -> str: 2996 return self._embed_ignore_nulls(expression, "RESPECT NULLS") 2997 2998 def havingmax_sql(self, expression: exp.HavingMax) -> str: 2999 this_sql = self.sql(expression, "this") 3000 expression_sql = self.sql(expression, "expression") 3001 kind = "MAX" if expression.args.get("max") else "MIN" 3002 return f"{this_sql} HAVING {kind} {expression_sql}" 3003 3004 def intdiv_sql(self, expression: exp.IntDiv) -> str: 3005 return self.sql( 3006 exp.Cast( 3007 this=exp.Div(this=expression.this, expression=expression.expression), 3008 to=exp.DataType(this=exp.DataType.Type.INT), 3009 ) 3010 ) 3011 3012 def dpipe_sql(self, expression: exp.DPipe) -> str: 3013 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 3014 return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten())) 3015 return self.binary(expression, "||") 3016 3017 def div_sql(self, expression: exp.Div) -> str: 3018 l, r = expression.left, expression.right 3019 3020 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 3021 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 3022 3023 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 3024 if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES): 3025 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 3026 3027 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 3028 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 3029 return self.sql( 3030 exp.cast( 3031 l / r, 3032 to=exp.DataType.Type.BIGINT, 3033 ) 3034 ) 3035 3036 return self.binary(expression, "/") 3037 3038 def overlaps_sql(self, expression: exp.Overlaps) -> str: 3039 return self.binary(expression, "OVERLAPS") 3040 3041 def distance_sql(self, expression: exp.Distance) -> str: 3042 return self.binary(expression, "<->") 3043 3044 def dot_sql(self, expression: exp.Dot) -> str: 3045 return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}" 3046 3047 def eq_sql(self, expression: exp.EQ) -> str: 3048 return self.binary(expression, "=") 3049 3050 def propertyeq_sql(self, expression: exp.PropertyEQ) -> str: 3051 return self.binary(expression, ":=") 3052 3053 def escape_sql(self, expression: exp.Escape) -> str: 3054 return self.binary(expression, "ESCAPE") 3055 3056 def glob_sql(self, expression: exp.Glob) -> str: 3057 return self.binary(expression, "GLOB") 3058 3059 def gt_sql(self, expression: exp.GT) -> str: 3060 return self.binary(expression, ">") 3061 3062 def gte_sql(self, expression: exp.GTE) -> str: 3063 return self.binary(expression, ">=") 3064 3065 def ilike_sql(self, expression: exp.ILike) -> str: 3066 return self.binary(expression, "ILIKE") 3067 3068 def ilikeany_sql(self, expression: exp.ILikeAny) -> str: 3069 return self.binary(expression, "ILIKE ANY") 3070 3071 def is_sql(self, expression: exp.Is) -> str: 3072 if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean): 3073 return self.sql( 3074 expression.this if expression.expression.this else exp.not_(expression.this) 3075 ) 3076 return self.binary(expression, "IS") 3077 3078 def like_sql(self, expression: exp.Like) -> str: 3079 return self.binary(expression, "LIKE") 3080 3081 def likeany_sql(self, expression: exp.LikeAny) -> str: 3082 return self.binary(expression, "LIKE ANY") 3083 3084 def similarto_sql(self, expression: exp.SimilarTo) -> str: 3085 return self.binary(expression, "SIMILAR TO") 3086 3087 def lt_sql(self, expression: exp.LT) -> str: 3088 return self.binary(expression, "<") 3089 3090 def lte_sql(self, expression: exp.LTE) -> str: 3091 return self.binary(expression, "<=") 3092 3093 def mod_sql(self, expression: exp.Mod) -> str: 3094 return self.binary(expression, "%") 3095 3096 def mul_sql(self, expression: exp.Mul) -> str: 3097 return self.binary(expression, "*") 3098 3099 def neq_sql(self, expression: exp.NEQ) -> str: 3100 return self.binary(expression, "<>") 3101 3102 def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str: 3103 return self.binary(expression, "IS NOT DISTINCT FROM") 3104 3105 def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str: 3106 return self.binary(expression, "IS DISTINCT FROM") 3107 3108 def slice_sql(self, expression: exp.Slice) -> str: 3109 return self.binary(expression, ":") 3110 3111 def sub_sql(self, expression: exp.Sub) -> str: 3112 return self.binary(expression, "-") 3113 3114 def trycast_sql(self, expression: exp.TryCast) -> str: 3115 return self.cast_sql(expression, safe_prefix="TRY_") 3116 3117 def log_sql(self, expression: exp.Log) -> str: 3118 this = expression.this 3119 expr = expression.expression 3120 3121 if self.dialect.LOG_BASE_FIRST is False: 3122 this, expr = expr, this 3123 elif self.dialect.LOG_BASE_FIRST is None and expr: 3124 if this.name in ("2", "10"): 3125 return self.func(f"LOG{this.name}", expr) 3126 3127 self.unsupported(f"Unsupported logarithm with base {self.sql(this)}") 3128 3129 return self.func("LOG", this, expr) 3130 3131 def use_sql(self, expression: exp.Use) -> str: 3132 kind = self.sql(expression, "kind") 3133 kind = f" {kind}" if kind else "" 3134 this = self.sql(expression, "this") 3135 this = f" {this}" if this else "" 3136 return f"USE{kind}{this}" 3137 3138 def binary(self, expression: exp.Binary, op: str) -> str: 3139 op = self.maybe_comment(op, comments=expression.comments) 3140 return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}" 3141 3142 def function_fallback_sql(self, expression: exp.Func) -> str: 3143 args = [] 3144 3145 for key in expression.arg_types: 3146 arg_value = expression.args.get(key) 3147 3148 if isinstance(arg_value, list): 3149 for value in arg_value: 3150 args.append(value) 3151 elif arg_value is not None: 3152 args.append(arg_value) 3153 3154 if self.normalize_functions: 3155 name = expression.sql_name() 3156 else: 3157 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 3158 3159 return self.func(name, *args) 3160 3161 def func( 3162 self, 3163 name: str, 3164 *args: t.Optional[exp.Expression | str], 3165 prefix: str = "(", 3166 suffix: str = ")", 3167 ) -> str: 3168 return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}" 3169 3170 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 3171 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 3172 if self.pretty and self.text_width(arg_sqls) > self.max_text_width: 3173 return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 3174 return ", ".join(arg_sqls) 3175 3176 def text_width(self, args: t.Iterable) -> int: 3177 return sum(len(arg) for arg in args) 3178 3179 def format_time( 3180 self, 3181 expression: exp.Expression, 3182 inverse_time_mapping: t.Optional[t.Dict[str, str]] = None, 3183 inverse_time_trie: t.Optional[t.Dict] = None, 3184 ) -> t.Optional[str]: 3185 return format_time( 3186 self.sql(expression, "format"), 3187 inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING, 3188 inverse_time_trie or self.dialect.INVERSE_TIME_TRIE, 3189 ) 3190 3191 def expressions( 3192 self, 3193 expression: t.Optional[exp.Expression] = None, 3194 key: t.Optional[str] = None, 3195 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 3196 flat: bool = False, 3197 indent: bool = True, 3198 skip_first: bool = False, 3199 sep: str = ", ", 3200 prefix: str = "", 3201 ) -> str: 3202 expressions = expression.args.get(key or "expressions") if expression else sqls 3203 3204 if not expressions: 3205 return "" 3206 3207 if flat: 3208 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 3209 3210 num_sqls = len(expressions) 3211 3212 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 3213 if self.pretty: 3214 if self.leading_comma: 3215 pad = " " * len(sep) 3216 else: 3217 stripped_sep = sep.strip() 3218 3219 result_sqls = [] 3220 for i, e in enumerate(expressions): 3221 sql = self.sql(e, comment=False) 3222 if not sql: 3223 continue 3224 3225 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 3226 3227 if self.pretty: 3228 if self.leading_comma: 3229 result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}") 3230 else: 3231 result_sqls.append( 3232 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 3233 ) 3234 else: 3235 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 3236 3237 result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls) 3238 return self.indent(result_sql, skip_first=skip_first) if indent else result_sql 3239 3240 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 3241 flat = flat or isinstance(expression.parent, exp.Properties) 3242 expressions_sql = self.expressions(expression, flat=flat) 3243 if flat: 3244 return f"{op} {expressions_sql}" 3245 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}" 3246 3247 def naked_property(self, expression: exp.Property) -> str: 3248 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 3249 if not property_name: 3250 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 3251 return f"{property_name} {self.sql(expression, 'this')}" 3252 3253 def tag_sql(self, expression: exp.Tag) -> str: 3254 return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}" 3255 3256 def token_sql(self, token_type: TokenType) -> str: 3257 return self.TOKEN_MAPPING.get(token_type, token_type.name) 3258 3259 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 3260 this = self.sql(expression, "this") 3261 expressions = self.no_identify(self.expressions, expression) 3262 expressions = ( 3263 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 3264 ) 3265 return f"{this}{expressions}" 3266 3267 def joinhint_sql(self, expression: exp.JoinHint) -> str: 3268 this = self.sql(expression, "this") 3269 expressions = self.expressions(expression, flat=True) 3270 return f"{this}({expressions})" 3271 3272 def kwarg_sql(self, expression: exp.Kwarg) -> str: 3273 return self.binary(expression, "=>") 3274 3275 def when_sql(self, expression: exp.When) -> str: 3276 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 3277 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 3278 condition = self.sql(expression, "condition") 3279 condition = f" AND {condition}" if condition else "" 3280 3281 then_expression = expression.args.get("then") 3282 if isinstance(then_expression, exp.Insert): 3283 then = f"INSERT {self.sql(then_expression, 'this')}" 3284 if "expression" in then_expression.args: 3285 then += f" VALUES {self.sql(then_expression, 'expression')}" 3286 elif isinstance(then_expression, exp.Update): 3287 if isinstance(then_expression.args.get("expressions"), exp.Star): 3288 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 3289 else: 3290 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 3291 else: 3292 then = self.sql(then_expression) 3293 return f"WHEN {matched}{source}{condition} THEN {then}" 3294 3295 def merge_sql(self, expression: exp.Merge) -> str: 3296 table = expression.this 3297 table_alias = "" 3298 3299 hints = table.args.get("hints") 3300 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 3301 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 3302 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 3303 3304 this = self.sql(table) 3305 using = f"USING {self.sql(expression, 'using')}" 3306 on = f"ON {self.sql(expression, 'on')}" 3307 expressions = self.expressions(expression, sep=" ") 3308 3309 return self.prepend_ctes( 3310 expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}" 3311 ) 3312 3313 def tochar_sql(self, expression: exp.ToChar) -> str: 3314 if expression.args.get("format"): 3315 self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function") 3316 3317 return self.sql(exp.cast(expression.this, "text")) 3318 3319 def tonumber_sql(self, expression: exp.ToNumber) -> str: 3320 if not self.SUPPORTS_TO_NUMBER: 3321 self.unsupported("Unsupported TO_NUMBER function") 3322 return self.sql(exp.cast(expression.this, "double")) 3323 3324 fmt = expression.args.get("format") 3325 if not fmt: 3326 self.unsupported("Conversion format is required for TO_NUMBER") 3327 return self.sql(exp.cast(expression.this, "double")) 3328 3329 return self.func("TO_NUMBER", expression.this, fmt) 3330 3331 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 3332 this = self.sql(expression, "this") 3333 kind = self.sql(expression, "kind") 3334 settings_sql = self.expressions(expression, key="settings", sep=" ") 3335 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 3336 return f"{this}({kind}{args})" 3337 3338 def dictrange_sql(self, expression: exp.DictRange) -> str: 3339 this = self.sql(expression, "this") 3340 max = self.sql(expression, "max") 3341 min = self.sql(expression, "min") 3342 return f"{this}(MIN {min} MAX {max})" 3343 3344 def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str: 3345 return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}" 3346 3347 def oncluster_sql(self, expression: exp.OnCluster) -> str: 3348 return "" 3349 3350 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3351 expressions = self.expressions(expression, key="expressions", flat=True) 3352 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3353 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3354 buckets = self.sql(expression, "buckets") 3355 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS" 3356 3357 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3358 this = self.sql(expression, "this") 3359 having = self.sql(expression, "having") 3360 3361 if having: 3362 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3363 3364 return self.func("ANY_VALUE", this) 3365 3366 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3367 transform = self.func("TRANSFORM", *expression.expressions) 3368 row_format_before = self.sql(expression, "row_format_before") 3369 row_format_before = f" {row_format_before}" if row_format_before else "" 3370 record_writer = self.sql(expression, "record_writer") 3371 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3372 using = f" USING {self.sql(expression, 'command_script')}" 3373 schema = self.sql(expression, "schema") 3374 schema = f" AS {schema}" if schema else "" 3375 row_format_after = self.sql(expression, "row_format_after") 3376 row_format_after = f" {row_format_after}" if row_format_after else "" 3377 record_reader = self.sql(expression, "record_reader") 3378 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3379 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}" 3380 3381 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3382 key_block_size = self.sql(expression, "key_block_size") 3383 if key_block_size: 3384 return f"KEY_BLOCK_SIZE = {key_block_size}" 3385 3386 using = self.sql(expression, "using") 3387 if using: 3388 return f"USING {using}" 3389 3390 parser = self.sql(expression, "parser") 3391 if parser: 3392 return f"WITH PARSER {parser}" 3393 3394 comment = self.sql(expression, "comment") 3395 if comment: 3396 return f"COMMENT {comment}" 3397 3398 visible = expression.args.get("visible") 3399 if visible is not None: 3400 return "VISIBLE" if visible else "INVISIBLE" 3401 3402 engine_attr = self.sql(expression, "engine_attr") 3403 if engine_attr: 3404 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3405 3406 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3407 if secondary_engine_attr: 3408 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3409 3410 self.unsupported("Unsupported index constraint option.") 3411 return "" 3412 3413 def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str: 3414 enforced = " ENFORCED" if expression.args.get("enforced") else "" 3415 return f"CHECK ({self.sql(expression, 'this')}){enforced}" 3416 3417 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3418 kind = self.sql(expression, "kind") 3419 kind = f"{kind} INDEX" if kind else "INDEX" 3420 this = self.sql(expression, "this") 3421 this = f" {this}" if this else "" 3422 index_type = self.sql(expression, "index_type") 3423 index_type = f" USING {index_type}" if index_type else "" 3424 schema = self.sql(expression, "schema") 3425 schema = f" {schema}" if schema else "" 3426 options = self.expressions(expression, key="options", sep=" ") 3427 options = f" {options}" if options else "" 3428 return f"{kind}{this}{index_type}{schema}{options}" 3429 3430 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3431 if self.NVL2_SUPPORTED: 3432 return self.function_fallback_sql(expression) 3433 3434 case = exp.Case().when( 3435 expression.this.is_(exp.null()).not_(copy=False), 3436 expression.args["true"], 3437 copy=False, 3438 ) 3439 else_cond = expression.args.get("false") 3440 if else_cond: 3441 case.else_(else_cond, copy=False) 3442 3443 return self.sql(case) 3444 3445 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3446 this = self.sql(expression, "this") 3447 expr = self.sql(expression, "expression") 3448 iterator = self.sql(expression, "iterator") 3449 condition = self.sql(expression, "condition") 3450 condition = f" IF {condition}" if condition else "" 3451 return f"{this} FOR {expr} IN {iterator}{condition}" 3452 3453 def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str: 3454 return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})" 3455 3456 def opclass_sql(self, expression: exp.Opclass) -> str: 3457 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 3458 3459 def predict_sql(self, expression: exp.Predict) -> str: 3460 model = self.sql(expression, "this") 3461 model = f"MODEL {model}" 3462 table = self.sql(expression, "expression") 3463 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3464 parameters = self.sql(expression, "params_struct") 3465 return self.func("PREDICT", model, table, parameters or None) 3466 3467 def forin_sql(self, expression: exp.ForIn) -> str: 3468 this = self.sql(expression, "this") 3469 expression_sql = self.sql(expression, "expression") 3470 return f"FOR {this} DO {expression_sql}" 3471 3472 def refresh_sql(self, expression: exp.Refresh) -> str: 3473 this = self.sql(expression, "this") 3474 table = "" if isinstance(expression.this, exp.Literal) else "TABLE " 3475 return f"REFRESH {table}{this}" 3476 3477 def operator_sql(self, expression: exp.Operator) -> str: 3478 return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})") 3479 3480 def toarray_sql(self, expression: exp.ToArray) -> str: 3481 arg = expression.this 3482 if not arg.type: 3483 from sqlglot.optimizer.annotate_types import annotate_types 3484 3485 arg = annotate_types(arg) 3486 3487 if arg.is_type(exp.DataType.Type.ARRAY): 3488 return self.sql(arg) 3489 3490 cond_for_null = arg.is_(exp.null()) 3491 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False))) 3492 3493 def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str: 3494 this = expression.this 3495 if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME): 3496 return self.sql(this) 3497 3498 return self.sql(exp.cast(this, "time")) 3499 3500 def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str: 3501 this = expression.this 3502 if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP): 3503 return self.sql(this) 3504 3505 return self.sql(exp.cast(this, "timestamp")) 3506 3507 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3508 this = expression.this 3509 time_format = self.format_time(expression) 3510 3511 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3512 return self.sql( 3513 exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date") 3514 ) 3515 3516 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3517 return self.sql(this) 3518 3519 return self.sql(exp.cast(this, "date")) 3520 3521 def unixdate_sql(self, expression: exp.UnixDate) -> str: 3522 return self.sql( 3523 exp.func( 3524 "DATEDIFF", 3525 expression.this, 3526 exp.cast(exp.Literal.string("1970-01-01"), "date"), 3527 "day", 3528 ) 3529 ) 3530 3531 def lastday_sql(self, expression: exp.LastDay) -> str: 3532 if self.LAST_DAY_SUPPORTS_DATE_PART: 3533 return self.function_fallback_sql(expression) 3534 3535 unit = expression.text("unit") 3536 if unit and unit != "MONTH": 3537 self.unsupported("Date parts are not supported in LAST_DAY.") 3538 3539 return self.func("LAST_DAY", expression.this) 3540 3541 def dateadd_sql(self, expression: exp.DateAdd) -> str: 3542 from sqlglot.dialects.dialect import unit_to_str 3543 3544 return self.func( 3545 "DATE_ADD", expression.this, expression.expression, unit_to_str(expression) 3546 ) 3547 3548 def arrayany_sql(self, expression: exp.ArrayAny) -> str: 3549 if self.CAN_IMPLEMENT_ARRAY_ANY: 3550 filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression) 3551 filtered_not_empty = exp.ArraySize(this=filtered).neq(0) 3552 original_is_empty = exp.ArraySize(this=expression.this).eq(0) 3553 return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty))) 3554 3555 from sqlglot.dialects import Dialect 3556 3557 # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect 3558 if self.dialect.__class__ != Dialect: 3559 self.unsupported("ARRAY_ANY is unsupported") 3560 3561 return self.function_fallback_sql(expression) 3562 3563 def generateseries_sql(self, expression: exp.GenerateSeries) -> str: 3564 expression.set("is_end_exclusive", None) 3565 return self.function_fallback_sql(expression) 3566 3567 def struct_sql(self, expression: exp.Struct) -> str: 3568 expression.set( 3569 "expressions", 3570 [ 3571 exp.alias_(e.expression, e.name if e.this.is_string else e.this) 3572 if isinstance(e, exp.PropertyEQ) 3573 else e 3574 for e in expression.expressions 3575 ], 3576 ) 3577 3578 return self.function_fallback_sql(expression) 3579 3580 def partitionrange_sql(self, expression: exp.PartitionRange) -> str: 3581 low = self.sql(expression, "this") 3582 high = self.sql(expression, "expression") 3583 3584 return f"{low} TO {high}" 3585 3586 def truncatetable_sql(self, expression: exp.TruncateTable) -> str: 3587 target = "DATABASE" if expression.args.get("is_database") else "TABLE" 3588 tables = f" {self.expressions(expression)}" 3589 3590 exists = " IF EXISTS" if expression.args.get("exists") else "" 3591 3592 on_cluster = self.sql(expression, "cluster") 3593 on_cluster = f" {on_cluster}" if on_cluster else "" 3594 3595 identity = self.sql(expression, "identity") 3596 identity = f" {identity} IDENTITY" if identity else "" 3597 3598 option = self.sql(expression, "option") 3599 option = f" {option}" if option else "" 3600 3601 partition = self.sql(expression, "partition") 3602 partition = f" {partition}" if partition else "" 3603 3604 return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}" 3605 3606 # This transpiles T-SQL's CONVERT function 3607 # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16 3608 def convert_sql(self, expression: exp.Convert) -> str: 3609 to = expression.this 3610 value = expression.expression 3611 style = expression.args.get("style") 3612 safe = expression.args.get("safe") 3613 strict = expression.args.get("strict") 3614 3615 if not to or not value: 3616 return "" 3617 3618 # Retrieve length of datatype and override to default if not specified 3619 if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3620 to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False) 3621 3622 transformed: t.Optional[exp.Expression] = None 3623 cast = exp.Cast if strict else exp.TryCast 3624 3625 # Check whether a conversion with format (T-SQL calls this 'style') is applicable 3626 if isinstance(style, exp.Literal) and style.is_int: 3627 from sqlglot.dialects.tsql import TSQL 3628 3629 style_value = style.name 3630 converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value) 3631 if not converted_style: 3632 self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}") 3633 3634 fmt = exp.Literal.string(converted_style) 3635 3636 if to.this == exp.DataType.Type.DATE: 3637 transformed = exp.StrToDate(this=value, format=fmt) 3638 elif to.this == exp.DataType.Type.DATETIME: 3639 transformed = exp.StrToTime(this=value, format=fmt) 3640 elif to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3641 transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe) 3642 elif to.this == exp.DataType.Type.TEXT: 3643 transformed = exp.TimeToStr(this=value, format=fmt) 3644 3645 if not transformed: 3646 transformed = cast(this=value, to=to, safe=safe) 3647 3648 return self.sql(transformed) 3649 3650 def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str: 3651 this = expression.this 3652 if isinstance(this, exp.JSONPathWildcard): 3653 this = self.json_path_part(this) 3654 return f".{this}" if this else "" 3655 3656 if exp.SAFE_IDENTIFIER_RE.match(this): 3657 return f".{this}" 3658 3659 this = self.json_path_part(this) 3660 return f"[{this}]" if self.JSON_PATH_BRACKETED_KEY_SUPPORTED else f".{this}" 3661 3662 def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str: 3663 this = self.json_path_part(expression.this) 3664 return f"[{this}]" if this else "" 3665 3666 def _simplify_unless_literal(self, expression: E) -> E: 3667 if not isinstance(expression, exp.Literal): 3668 from sqlglot.optimizer.simplify import simplify 3669 3670 expression = simplify(expression, dialect=self.dialect) 3671 3672 return expression 3673 3674 def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str: 3675 if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"): 3676 # The first modifier here will be the one closest to the AggFunc's arg 3677 mods = sorted( 3678 expression.find_all(exp.HavingMax, exp.Order, exp.Limit), 3679 key=lambda x: 0 3680 if isinstance(x, exp.HavingMax) 3681 else (1 if isinstance(x, exp.Order) else 2), 3682 ) 3683 3684 if mods: 3685 mod = mods[0] 3686 this = expression.__class__(this=mod.this.copy()) 3687 this.meta["inline"] = True 3688 mod.this.replace(this) 3689 return self.sql(expression.this) 3690 3691 agg_func = expression.find(exp.AggFunc) 3692 3693 if agg_func: 3694 return self.sql(agg_func)[:-1] + f" {text})" 3695 3696 return f"{self.sql(expression, 'this')} {text}"
logger =
<Logger sqlglot (WARNING)>
ESCAPED_UNICODE_RE =
re.compile('\\\\(\\d+)')
class
Generator:
37class Generator(metaclass=_Generator): 38 """ 39 Generator converts a given syntax tree to the corresponding SQL string. 40 41 Args: 42 pretty: Whether to format the produced SQL string. 43 Default: False. 44 identify: Determines when an identifier should be quoted. Possible values are: 45 False (default): Never quote, except in cases where it's mandatory by the dialect. 46 True or 'always': Always quote. 47 'safe': Only quote identifiers that are case insensitive. 48 normalize: Whether to normalize identifiers to lowercase. 49 Default: False. 50 pad: The pad size in a formatted string. For example, this affects the indentation of 51 a projection in a query, relative to its nesting level. 52 Default: 2. 53 indent: The indentation size in a formatted string. For example, this affects the 54 indentation of subqueries and filters under a `WHERE` clause. 55 Default: 2. 56 normalize_functions: How to normalize function names. Possible values are: 57 "upper" or True (default): Convert names to uppercase. 58 "lower": Convert names to lowercase. 59 False: Disables function name normalization. 60 unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. 61 Default ErrorLevel.WARN. 62 max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. 63 This is only relevant if unsupported_level is ErrorLevel.RAISE. 64 Default: 3 65 leading_comma: Whether the comma is leading or trailing in select expressions. 66 This is only relevant when generating in pretty mode. 67 Default: False 68 max_text_width: The max number of characters in a segment before creating new lines in pretty mode. 69 The default is on the smaller end because the length only represents a segment and not the true 70 line length. 71 Default: 80 72 comments: Whether to preserve comments in the output SQL code. 73 Default: True 74 """ 75 76 TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = { 77 **JSON_PATH_PART_TRANSFORMS, 78 exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}", 79 exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}", 80 exp.CaseSpecificColumnConstraint: lambda _, 81 e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC", 82 exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}", 83 exp.CharacterSetProperty: lambda self, 84 e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}", 85 exp.ClusteredColumnConstraint: lambda self, 86 e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})", 87 exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}", 88 exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}", 89 exp.CopyGrantsProperty: lambda *_: "COPY GRANTS", 90 exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}", 91 exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}", 92 exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}", 93 exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}", 94 exp.ExecuteAsProperty: lambda self, e: self.naked_property(e), 95 exp.ExternalProperty: lambda *_: "EXTERNAL", 96 exp.GlobalProperty: lambda *_: "GLOBAL", 97 exp.HeapProperty: lambda *_: "HEAP", 98 exp.IcebergProperty: lambda *_: "ICEBERG", 99 exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})", 100 exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}", 101 exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}", 102 exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}", 103 exp.JSONExtract: lambda self, e: self.func( 104 "JSON_EXTRACT", e.this, e.expression, *e.expressions 105 ), 106 exp.JSONExtractScalar: lambda self, e: self.func( 107 "JSON_EXTRACT_SCALAR", e.this, e.expression, *e.expressions 108 ), 109 exp.LanguageProperty: lambda self, e: self.naked_property(e), 110 exp.LocationProperty: lambda self, e: self.naked_property(e), 111 exp.LogProperty: lambda _, e: f"{'NO ' if e.args.get('no') else ''}LOG", 112 exp.MaterializedProperty: lambda *_: "MATERIALIZED", 113 exp.NonClusteredColumnConstraint: lambda self, 114 e: f"NONCLUSTERED ({self.expressions(e, 'this', indent=False)})", 115 exp.NoPrimaryIndexProperty: lambda *_: "NO PRIMARY INDEX", 116 exp.NotForReplicationColumnConstraint: lambda *_: "NOT FOR REPLICATION", 117 exp.OnCommitProperty: lambda _, 118 e: f"ON COMMIT {'DELETE' if e.args.get('delete') else 'PRESERVE'} ROWS", 119 exp.OnProperty: lambda self, e: f"ON {self.sql(e, 'this')}", 120 exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}", 121 exp.OutputModelProperty: lambda self, e: f"OUTPUT{self.sql(e, 'this')}", 122 exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}", 123 exp.RemoteWithConnectionModelProperty: lambda self, 124 e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}", 125 exp.ReturnsProperty: lambda self, e: self.naked_property(e), 126 exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}", 127 exp.SetConfigProperty: lambda self, e: self.sql(e, "this"), 128 exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET", 129 exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}", 130 exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}", 131 exp.SqlReadWriteProperty: lambda _, e: e.name, 132 exp.SqlSecurityProperty: lambda _, 133 e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}", 134 exp.StabilityProperty: lambda _, e: e.name, 135 exp.TemporaryProperty: lambda *_: "TEMPORARY", 136 exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}", 137 exp.Timestamp: lambda self, e: self.func("TIMESTAMP", e.this, e.expression), 138 exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}", 139 exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}", 140 exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions), 141 exp.TransientProperty: lambda *_: "TRANSIENT", 142 exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE", 143 exp.UnloggedProperty: lambda *_: "UNLOGGED", 144 exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]), 145 exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}", 146 exp.VolatileProperty: lambda *_: "VOLATILE", 147 exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}", 148 exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}", 149 } 150 151 # Whether null ordering is supported in order by 152 # True: Full Support, None: No support, False: No support in window specifications 153 NULL_ORDERING_SUPPORTED: t.Optional[bool] = True 154 155 # Whether ignore nulls is inside the agg or outside. 156 # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER 157 IGNORE_NULLS_IN_FUNC = False 158 159 # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported 160 LOCKING_READS_SUPPORTED = False 161 162 # Always do union distinct or union all 163 EXPLICIT_UNION = False 164 165 # Wrap derived values in parens, usually standard but spark doesn't support it 166 WRAP_DERIVED_VALUES = True 167 168 # Whether create function uses an AS before the RETURN 169 CREATE_FUNCTION_RETURN_AS = True 170 171 # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed 172 MATCHED_BY_SOURCE = True 173 174 # Whether the INTERVAL expression works only with values like '1 day' 175 SINGLE_STRING_INTERVAL = False 176 177 # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs 178 INTERVAL_ALLOWS_PLURAL_FORM = True 179 180 # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH") 181 LIMIT_FETCH = "ALL" 182 183 # Whether limit and fetch allows expresions or just limits 184 LIMIT_ONLY_LITERALS = False 185 186 # Whether a table is allowed to be renamed with a db 187 RENAME_TABLE_WITH_DB = True 188 189 # The separator for grouping sets and rollups 190 GROUPINGS_SEP = "," 191 192 # The string used for creating an index on a table 193 INDEX_ON = "ON" 194 195 # Whether join hints should be generated 196 JOIN_HINTS = True 197 198 # Whether table hints should be generated 199 TABLE_HINTS = True 200 201 # Whether query hints should be generated 202 QUERY_HINTS = True 203 204 # What kind of separator to use for query hints 205 QUERY_HINT_SEP = ", " 206 207 # Whether comparing against booleans (e.g. x IS TRUE) is supported 208 IS_BOOL_ALLOWED = True 209 210 # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement 211 DUPLICATE_KEY_UPDATE_WITH_SET = True 212 213 # Whether to generate the limit as TOP <value> instead of LIMIT <value> 214 LIMIT_IS_TOP = False 215 216 # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ... 217 RETURNING_END = True 218 219 # Whether to generate the (+) suffix for columns used in old-style join conditions 220 COLUMN_JOIN_MARKS_SUPPORTED = False 221 222 # Whether to generate an unquoted value for EXTRACT's date part argument 223 EXTRACT_ALLOWS_QUOTES = True 224 225 # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax 226 TZ_TO_WITH_TIME_ZONE = False 227 228 # Whether the NVL2 function is supported 229 NVL2_SUPPORTED = True 230 231 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax 232 SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE") 233 234 # Whether VALUES statements can be used as derived tables. 235 # MySQL 5 and Redshift do not allow this, so when False, it will convert 236 # SELECT * VALUES into SELECT UNION 237 VALUES_AS_TABLE = True 238 239 # Whether the word COLUMN is included when adding a column with ALTER TABLE 240 ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True 241 242 # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery) 243 UNNEST_WITH_ORDINALITY = True 244 245 # Whether FILTER (WHERE cond) can be used for conditional aggregation 246 AGGREGATE_FILTER_SUPPORTED = True 247 248 # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds 249 SEMI_ANTI_JOIN_WITH_SIDE = True 250 251 # Whether to include the type of a computed column in the CREATE DDL 252 COMPUTED_COLUMN_WITH_TYPE = True 253 254 # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY 255 SUPPORTS_TABLE_COPY = True 256 257 # Whether parentheses are required around the table sample's expression 258 TABLESAMPLE_REQUIRES_PARENS = True 259 260 # Whether a table sample clause's size needs to be followed by the ROWS keyword 261 TABLESAMPLE_SIZE_IS_ROWS = True 262 263 # The keyword(s) to use when generating a sample clause 264 TABLESAMPLE_KEYWORDS = "TABLESAMPLE" 265 266 # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI 267 TABLESAMPLE_WITH_METHOD = True 268 269 # The keyword to use when specifying the seed of a sample clause 270 TABLESAMPLE_SEED_KEYWORD = "SEED" 271 272 # Whether COLLATE is a function instead of a binary operator 273 COLLATE_IS_FUNC = False 274 275 # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle) 276 DATA_TYPE_SPECIFIERS_ALLOWED = False 277 278 # Whether conditions require booleans WHERE x = 0 vs WHERE x 279 ENSURE_BOOLS = False 280 281 # Whether the "RECURSIVE" keyword is required when defining recursive CTEs 282 CTE_RECURSIVE_KEYWORD_REQUIRED = True 283 284 # Whether CONCAT requires >1 arguments 285 SUPPORTS_SINGLE_ARG_CONCAT = True 286 287 # Whether LAST_DAY function supports a date part argument 288 LAST_DAY_SUPPORTS_DATE_PART = True 289 290 # Whether named columns are allowed in table aliases 291 SUPPORTS_TABLE_ALIAS_COLUMNS = True 292 293 # Whether UNPIVOT aliases are Identifiers (False means they're Literals) 294 UNPIVOT_ALIASES_ARE_IDENTIFIERS = True 295 296 # What delimiter to use for separating JSON key/value pairs 297 JSON_KEY_VALUE_PAIR_SEP = ":" 298 299 # INSERT OVERWRITE TABLE x override 300 INSERT_OVERWRITE = " OVERWRITE TABLE" 301 302 # Whether the SELECT .. INTO syntax is used instead of CTAS 303 SUPPORTS_SELECT_INTO = False 304 305 # Whether UNLOGGED tables can be created 306 SUPPORTS_UNLOGGED_TABLES = False 307 308 # Whether the CREATE TABLE LIKE statement is supported 309 SUPPORTS_CREATE_TABLE_LIKE = True 310 311 # Whether the LikeProperty needs to be specified inside of the schema clause 312 LIKE_PROPERTY_INSIDE_SCHEMA = False 313 314 # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be 315 # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args 316 MULTI_ARG_DISTINCT = True 317 318 # Whether the JSON extraction operators expect a value of type JSON 319 JSON_TYPE_REQUIRED_FOR_EXTRACTION = False 320 321 # Whether bracketed keys like ["foo"] are supported in JSON paths 322 JSON_PATH_BRACKETED_KEY_SUPPORTED = True 323 324 # Whether to escape keys using single quotes in JSON paths 325 JSON_PATH_SINGLE_QUOTE_ESCAPE = False 326 327 # The JSONPathPart expressions supported by this dialect 328 SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy() 329 330 # Whether any(f(x) for x in array) can be implemented by this dialect 331 CAN_IMPLEMENT_ARRAY_ANY = False 332 333 # Whether the function TO_NUMBER is supported 334 SUPPORTS_TO_NUMBER = True 335 336 TYPE_MAPPING = { 337 exp.DataType.Type.NCHAR: "CHAR", 338 exp.DataType.Type.NVARCHAR: "VARCHAR", 339 exp.DataType.Type.MEDIUMTEXT: "TEXT", 340 exp.DataType.Type.LONGTEXT: "TEXT", 341 exp.DataType.Type.TINYTEXT: "TEXT", 342 exp.DataType.Type.MEDIUMBLOB: "BLOB", 343 exp.DataType.Type.LONGBLOB: "BLOB", 344 exp.DataType.Type.TINYBLOB: "BLOB", 345 exp.DataType.Type.INET: "INET", 346 } 347 348 STAR_MAPPING = { 349 "except": "EXCEPT", 350 "replace": "REPLACE", 351 } 352 353 TIME_PART_SINGULARS = { 354 "MICROSECONDS": "MICROSECOND", 355 "SECONDS": "SECOND", 356 "MINUTES": "MINUTE", 357 "HOURS": "HOUR", 358 "DAYS": "DAY", 359 "WEEKS": "WEEK", 360 "MONTHS": "MONTH", 361 "QUARTERS": "QUARTER", 362 "YEARS": "YEAR", 363 } 364 365 AFTER_HAVING_MODIFIER_TRANSFORMS = { 366 "cluster": lambda self, e: self.sql(e, "cluster"), 367 "distribute": lambda self, e: self.sql(e, "distribute"), 368 "qualify": lambda self, e: self.sql(e, "qualify"), 369 "sort": lambda self, e: self.sql(e, "sort"), 370 "windows": lambda self, e: ( 371 self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True) 372 if e.args.get("windows") 373 else "" 374 ), 375 } 376 377 TOKEN_MAPPING: t.Dict[TokenType, str] = {} 378 379 STRUCT_DELIMITER = ("<", ">") 380 381 PARAMETER_TOKEN = "@" 382 NAMED_PLACEHOLDER_TOKEN = ":" 383 384 PROPERTIES_LOCATION = { 385 exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE, 386 exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA, 387 exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA, 388 exp.BackupProperty: exp.Properties.Location.POST_SCHEMA, 389 exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME, 390 exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA, 391 exp.ChecksumProperty: exp.Properties.Location.POST_NAME, 392 exp.CollateProperty: exp.Properties.Location.POST_SCHEMA, 393 exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA, 394 exp.Cluster: exp.Properties.Location.POST_SCHEMA, 395 exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA, 396 exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME, 397 exp.DefinerProperty: exp.Properties.Location.POST_CREATE, 398 exp.DictRange: exp.Properties.Location.POST_SCHEMA, 399 exp.DictProperty: exp.Properties.Location.POST_SCHEMA, 400 exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA, 401 exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA, 402 exp.EngineProperty: exp.Properties.Location.POST_SCHEMA, 403 exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA, 404 exp.ExternalProperty: exp.Properties.Location.POST_CREATE, 405 exp.FallbackProperty: exp.Properties.Location.POST_NAME, 406 exp.FileFormatProperty: exp.Properties.Location.POST_WITH, 407 exp.FreespaceProperty: exp.Properties.Location.POST_NAME, 408 exp.GlobalProperty: exp.Properties.Location.POST_CREATE, 409 exp.HeapProperty: exp.Properties.Location.POST_WITH, 410 exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA, 411 exp.IcebergProperty: exp.Properties.Location.POST_CREATE, 412 exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA, 413 exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME, 414 exp.JournalProperty: exp.Properties.Location.POST_NAME, 415 exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA, 416 exp.LikeProperty: exp.Properties.Location.POST_SCHEMA, 417 exp.LocationProperty: exp.Properties.Location.POST_SCHEMA, 418 exp.LockProperty: exp.Properties.Location.POST_SCHEMA, 419 exp.LockingProperty: exp.Properties.Location.POST_ALIAS, 420 exp.LogProperty: exp.Properties.Location.POST_NAME, 421 exp.MaterializedProperty: exp.Properties.Location.POST_CREATE, 422 exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME, 423 exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION, 424 exp.OnProperty: exp.Properties.Location.POST_SCHEMA, 425 exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION, 426 exp.Order: exp.Properties.Location.POST_SCHEMA, 427 exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA, 428 exp.PartitionedByProperty: exp.Properties.Location.POST_WITH, 429 exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA, 430 exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA, 431 exp.Property: exp.Properties.Location.POST_WITH, 432 exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA, 433 exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA, 434 exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA, 435 exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA, 436 exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA, 437 exp.SampleProperty: exp.Properties.Location.POST_SCHEMA, 438 exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA, 439 exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA, 440 exp.Set: exp.Properties.Location.POST_SCHEMA, 441 exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA, 442 exp.SetProperty: exp.Properties.Location.POST_CREATE, 443 exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA, 444 exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION, 445 exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION, 446 exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA, 447 exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA, 448 exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE, 449 exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA, 450 exp.TemporaryProperty: exp.Properties.Location.POST_CREATE, 451 exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA, 452 exp.TransientProperty: exp.Properties.Location.POST_CREATE, 453 exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA, 454 exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA, 455 exp.UnloggedProperty: exp.Properties.Location.POST_CREATE, 456 exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA, 457 exp.VolatileProperty: exp.Properties.Location.POST_CREATE, 458 exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION, 459 exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME, 460 exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA, 461 } 462 463 # Keywords that can't be used as unquoted identifier names 464 RESERVED_KEYWORDS: t.Set[str] = set() 465 466 # Expressions whose comments are separated from them for better formatting 467 WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 468 exp.Create, 469 exp.Delete, 470 exp.Drop, 471 exp.From, 472 exp.Insert, 473 exp.Join, 474 exp.Select, 475 exp.Union, 476 exp.Update, 477 exp.Where, 478 exp.With, 479 ) 480 481 # Expressions that should not have their comments generated in maybe_comment 482 EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 483 exp.Binary, 484 exp.Union, 485 ) 486 487 # Expressions that can remain unwrapped when appearing in the context of an INTERVAL 488 UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = ( 489 exp.Column, 490 exp.Literal, 491 exp.Neg, 492 exp.Paren, 493 ) 494 495 PARAMETERIZABLE_TEXT_TYPES = { 496 exp.DataType.Type.NVARCHAR, 497 exp.DataType.Type.VARCHAR, 498 exp.DataType.Type.CHAR, 499 exp.DataType.Type.NCHAR, 500 } 501 502 # Expressions that need to have all CTEs under them bubbled up to them 503 EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set() 504 505 SENTINEL_LINE_BREAK = "__SQLGLOT__LB__" 506 507 __slots__ = ( 508 "pretty", 509 "identify", 510 "normalize", 511 "pad", 512 "_indent", 513 "normalize_functions", 514 "unsupported_level", 515 "max_unsupported", 516 "leading_comma", 517 "max_text_width", 518 "comments", 519 "dialect", 520 "unsupported_messages", 521 "_escaped_quote_end", 522 "_escaped_identifier_end", 523 ) 524 525 def __init__( 526 self, 527 pretty: t.Optional[bool] = None, 528 identify: str | bool = False, 529 normalize: bool = False, 530 pad: int = 2, 531 indent: int = 2, 532 normalize_functions: t.Optional[str | bool] = None, 533 unsupported_level: ErrorLevel = ErrorLevel.WARN, 534 max_unsupported: int = 3, 535 leading_comma: bool = False, 536 max_text_width: int = 80, 537 comments: bool = True, 538 dialect: DialectType = None, 539 ): 540 import sqlglot 541 from sqlglot.dialects import Dialect 542 543 self.pretty = pretty if pretty is not None else sqlglot.pretty 544 self.identify = identify 545 self.normalize = normalize 546 self.pad = pad 547 self._indent = indent 548 self.unsupported_level = unsupported_level 549 self.max_unsupported = max_unsupported 550 self.leading_comma = leading_comma 551 self.max_text_width = max_text_width 552 self.comments = comments 553 self.dialect = Dialect.get_or_raise(dialect) 554 555 # This is both a Dialect property and a Generator argument, so we prioritize the latter 556 self.normalize_functions = ( 557 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 558 ) 559 560 self.unsupported_messages: t.List[str] = [] 561 self._escaped_quote_end: str = ( 562 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 563 ) 564 self._escaped_identifier_end: str = ( 565 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 566 ) 567 568 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 569 """ 570 Generates the SQL string corresponding to the given syntax tree. 571 572 Args: 573 expression: The syntax tree. 574 copy: Whether to copy the expression. The generator performs mutations so 575 it is safer to copy. 576 577 Returns: 578 The SQL string corresponding to `expression`. 579 """ 580 if copy: 581 expression = expression.copy() 582 583 expression = self.preprocess(expression) 584 585 self.unsupported_messages = [] 586 sql = self.sql(expression).strip() 587 588 if self.pretty: 589 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 590 591 if self.unsupported_level == ErrorLevel.IGNORE: 592 return sql 593 594 if self.unsupported_level == ErrorLevel.WARN: 595 for msg in self.unsupported_messages: 596 logger.warning(msg) 597 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 598 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 599 600 return sql 601 602 def preprocess(self, expression: exp.Expression) -> exp.Expression: 603 """Apply generic preprocessing transformations to a given expression.""" 604 if ( 605 not expression.parent 606 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 607 and any(node.parent is not expression for node in expression.find_all(exp.With)) 608 ): 609 from sqlglot.transforms import move_ctes_to_top_level 610 611 expression = move_ctes_to_top_level(expression) 612 613 if self.ENSURE_BOOLS: 614 from sqlglot.transforms import ensure_bools 615 616 expression = ensure_bools(expression) 617 618 return expression 619 620 def unsupported(self, message: str) -> None: 621 if self.unsupported_level == ErrorLevel.IMMEDIATE: 622 raise UnsupportedError(message) 623 self.unsupported_messages.append(message) 624 625 def sep(self, sep: str = " ") -> str: 626 return f"{sep.strip()}\n" if self.pretty else sep 627 628 def seg(self, sql: str, sep: str = " ") -> str: 629 return f"{self.sep(sep)}{sql}" 630 631 def pad_comment(self, comment: str) -> str: 632 comment = " " + comment if comment[0].strip() else comment 633 comment = comment + " " if comment[-1].strip() else comment 634 return comment 635 636 def maybe_comment( 637 self, 638 sql: str, 639 expression: t.Optional[exp.Expression] = None, 640 comments: t.Optional[t.List[str]] = None, 641 ) -> str: 642 comments = ( 643 ((expression and expression.comments) if comments is None else comments) # type: ignore 644 if self.comments 645 else None 646 ) 647 648 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 649 return sql 650 651 comments_sql = " ".join( 652 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 653 ) 654 655 if not comments_sql: 656 return sql 657 658 if isinstance(expression, self.WITH_SEPARATED_COMMENTS): 659 return ( 660 f"{self.sep()}{comments_sql}{sql}" 661 if not sql or sql[0].isspace() 662 else f"{comments_sql}{self.sep()}{sql}" 663 ) 664 665 return f"{sql} {comments_sql}" 666 667 def wrap(self, expression: exp.Expression | str) -> str: 668 this_sql = self.indent( 669 ( 670 self.sql(expression) 671 if isinstance(expression, exp.UNWRAPPED_QUERIES) 672 else self.sql(expression, "this") 673 ), 674 level=1, 675 pad=0, 676 ) 677 return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}" 678 679 def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str: 680 original = self.identify 681 self.identify = False 682 result = func(*args, **kwargs) 683 self.identify = original 684 return result 685 686 def normalize_func(self, name: str) -> str: 687 if self.normalize_functions == "upper" or self.normalize_functions is True: 688 return name.upper() 689 if self.normalize_functions == "lower": 690 return name.lower() 691 return name 692 693 def indent( 694 self, 695 sql: str, 696 level: int = 0, 697 pad: t.Optional[int] = None, 698 skip_first: bool = False, 699 skip_last: bool = False, 700 ) -> str: 701 if not self.pretty: 702 return sql 703 704 pad = self.pad if pad is None else pad 705 lines = sql.split("\n") 706 707 return "\n".join( 708 ( 709 line 710 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 711 else f"{' ' * (level * self._indent + pad)}{line}" 712 ) 713 for i, line in enumerate(lines) 714 ) 715 716 def sql( 717 self, 718 expression: t.Optional[str | exp.Expression], 719 key: t.Optional[str] = None, 720 comment: bool = True, 721 ) -> str: 722 if not expression: 723 return "" 724 725 if isinstance(expression, str): 726 return expression 727 728 if key: 729 value = expression.args.get(key) 730 if value: 731 return self.sql(value) 732 return "" 733 734 transform = self.TRANSFORMS.get(expression.__class__) 735 736 if callable(transform): 737 sql = transform(self, expression) 738 elif isinstance(expression, exp.Expression): 739 exp_handler_name = f"{expression.key}_sql" 740 741 if hasattr(self, exp_handler_name): 742 sql = getattr(self, exp_handler_name)(expression) 743 elif isinstance(expression, exp.Func): 744 sql = self.function_fallback_sql(expression) 745 elif isinstance(expression, exp.Property): 746 sql = self.property_sql(expression) 747 else: 748 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 749 else: 750 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 751 752 return self.maybe_comment(sql, expression) if self.comments and comment else sql 753 754 def uncache_sql(self, expression: exp.Uncache) -> str: 755 table = self.sql(expression, "this") 756 exists_sql = " IF EXISTS" if expression.args.get("exists") else "" 757 return f"UNCACHE TABLE{exists_sql} {table}" 758 759 def cache_sql(self, expression: exp.Cache) -> str: 760 lazy = " LAZY" if expression.args.get("lazy") else "" 761 table = self.sql(expression, "this") 762 options = expression.args.get("options") 763 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 764 sql = self.sql(expression, "expression") 765 sql = f" AS{self.sep()}{sql}" if sql else "" 766 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 767 return self.prepend_ctes(expression, sql) 768 769 def characterset_sql(self, expression: exp.CharacterSet) -> str: 770 if isinstance(expression.parent, exp.Cast): 771 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 772 default = "DEFAULT " if expression.args.get("default") else "" 773 return f"{default}CHARACTER SET={self.sql(expression, 'this')}" 774 775 def column_sql(self, expression: exp.Column) -> str: 776 join_mark = " (+)" if expression.args.get("join_mark") else "" 777 778 if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED: 779 join_mark = "" 780 self.unsupported("Outer join syntax using the (+) operator is not supported.") 781 782 column = ".".join( 783 self.sql(part) 784 for part in ( 785 expression.args.get("catalog"), 786 expression.args.get("db"), 787 expression.args.get("table"), 788 expression.args.get("this"), 789 ) 790 if part 791 ) 792 793 return f"{column}{join_mark}" 794 795 def columnposition_sql(self, expression: exp.ColumnPosition) -> str: 796 this = self.sql(expression, "this") 797 this = f" {this}" if this else "" 798 position = self.sql(expression, "position") 799 return f"{position}{this}" 800 801 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 802 column = self.sql(expression, "this") 803 kind = self.sql(expression, "kind") 804 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 805 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 806 kind = f"{sep}{kind}" if kind else "" 807 constraints = f" {constraints}" if constraints else "" 808 position = self.sql(expression, "position") 809 position = f" {position}" if position else "" 810 811 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 812 kind = "" 813 814 return f"{exists}{column}{kind}{constraints}{position}" 815 816 def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str: 817 this = self.sql(expression, "this") 818 kind_sql = self.sql(expression, "kind").strip() 819 return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql 820 821 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 822 this = self.sql(expression, "this") 823 if expression.args.get("not_null"): 824 persisted = " PERSISTED NOT NULL" 825 elif expression.args.get("persisted"): 826 persisted = " PERSISTED" 827 else: 828 persisted = "" 829 return f"AS {this}{persisted}" 830 831 def autoincrementcolumnconstraint_sql(self, _) -> str: 832 return self.token_sql(TokenType.AUTO_INCREMENT) 833 834 def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str: 835 if isinstance(expression.this, list): 836 this = self.wrap(self.expressions(expression, key="this", flat=True)) 837 else: 838 this = self.sql(expression, "this") 839 840 return f"COMPRESS {this}" 841 842 def generatedasidentitycolumnconstraint_sql( 843 self, expression: exp.GeneratedAsIdentityColumnConstraint 844 ) -> str: 845 this = "" 846 if expression.this is not None: 847 on_null = " ON NULL" if expression.args.get("on_null") else "" 848 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 849 850 start = expression.args.get("start") 851 start = f"START WITH {start}" if start else "" 852 increment = expression.args.get("increment") 853 increment = f" INCREMENT BY {increment}" if increment else "" 854 minvalue = expression.args.get("minvalue") 855 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 856 maxvalue = expression.args.get("maxvalue") 857 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 858 cycle = expression.args.get("cycle") 859 cycle_sql = "" 860 861 if cycle is not None: 862 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 863 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 864 865 sequence_opts = "" 866 if start or increment or cycle_sql: 867 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 868 sequence_opts = f" ({sequence_opts.strip()})" 869 870 expr = self.sql(expression, "expression") 871 expr = f"({expr})" if expr else "IDENTITY" 872 873 return f"GENERATED{this} AS {expr}{sequence_opts}" 874 875 def generatedasrowcolumnconstraint_sql( 876 self, expression: exp.GeneratedAsRowColumnConstraint 877 ) -> str: 878 start = "START" if expression.args.get("start") else "END" 879 hidden = " HIDDEN" if expression.args.get("hidden") else "" 880 return f"GENERATED ALWAYS AS ROW {start}{hidden}" 881 882 def periodforsystemtimeconstraint_sql( 883 self, expression: exp.PeriodForSystemTimeConstraint 884 ) -> str: 885 return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})" 886 887 def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str: 888 return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL" 889 890 def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str: 891 return f"AS {self.sql(expression, 'this')}" 892 893 def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str: 894 desc = expression.args.get("desc") 895 if desc is not None: 896 return f"PRIMARY KEY{' DESC' if desc else ' ASC'}" 897 return "PRIMARY KEY" 898 899 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 900 this = self.sql(expression, "this") 901 this = f" {this}" if this else "" 902 index_type = expression.args.get("index_type") 903 index_type = f" USING {index_type}" if index_type else "" 904 on_conflict = self.sql(expression, "on_conflict") 905 on_conflict = f" {on_conflict}" if on_conflict else "" 906 return f"UNIQUE{this}{index_type}{on_conflict}" 907 908 def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str: 909 return self.sql(expression, "this") 910 911 def create_sql(self, expression: exp.Create) -> str: 912 kind = self.sql(expression, "kind") 913 properties = expression.args.get("properties") 914 properties_locs = self.locate_properties(properties) if properties else defaultdict() 915 916 this = self.createable_sql(expression, properties_locs) 917 918 properties_sql = "" 919 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 920 exp.Properties.Location.POST_WITH 921 ): 922 properties_sql = self.sql( 923 exp.Properties( 924 expressions=[ 925 *properties_locs[exp.Properties.Location.POST_SCHEMA], 926 *properties_locs[exp.Properties.Location.POST_WITH], 927 ] 928 ) 929 ) 930 931 begin = " BEGIN" if expression.args.get("begin") else "" 932 end = " END" if expression.args.get("end") else "" 933 934 expression_sql = self.sql(expression, "expression") 935 if expression_sql: 936 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 937 938 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 939 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 940 postalias_props_sql = self.properties( 941 exp.Properties( 942 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 943 ), 944 wrapped=False, 945 ) 946 expression_sql = f" AS {postalias_props_sql}{expression_sql}" 947 else: 948 expression_sql = f" AS{expression_sql}" 949 950 postindex_props_sql = "" 951 if properties_locs.get(exp.Properties.Location.POST_INDEX): 952 postindex_props_sql = self.properties( 953 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 954 wrapped=False, 955 prefix=" ", 956 ) 957 958 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 959 indexes = f" {indexes}" if indexes else "" 960 index_sql = indexes + postindex_props_sql 961 962 replace = " OR REPLACE" if expression.args.get("replace") else "" 963 unique = " UNIQUE" if expression.args.get("unique") else "" 964 965 postcreate_props_sql = "" 966 if properties_locs.get(exp.Properties.Location.POST_CREATE): 967 postcreate_props_sql = self.properties( 968 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 969 sep=" ", 970 prefix=" ", 971 wrapped=False, 972 ) 973 974 modifiers = "".join((replace, unique, postcreate_props_sql)) 975 976 postexpression_props_sql = "" 977 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 978 postexpression_props_sql = self.properties( 979 exp.Properties( 980 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 981 ), 982 sep=" ", 983 prefix=" ", 984 wrapped=False, 985 ) 986 987 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 988 no_schema_binding = ( 989 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 990 ) 991 992 clone = self.sql(expression, "clone") 993 clone = f" {clone}" if clone else "" 994 995 expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 996 return self.prepend_ctes(expression, expression_sql) 997 998 def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str: 999 start = self.sql(expression, "start") 1000 start = f"START WITH {start}" if start else "" 1001 increment = self.sql(expression, "increment") 1002 increment = f" INCREMENT BY {increment}" if increment else "" 1003 minvalue = self.sql(expression, "minvalue") 1004 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 1005 maxvalue = self.sql(expression, "maxvalue") 1006 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 1007 owned = self.sql(expression, "owned") 1008 owned = f" OWNED BY {owned}" if owned else "" 1009 1010 cache = expression.args.get("cache") 1011 if cache is None: 1012 cache_str = "" 1013 elif cache is True: 1014 cache_str = " CACHE" 1015 else: 1016 cache_str = f" CACHE {cache}" 1017 1018 options = self.expressions(expression, key="options", flat=True, sep=" ") 1019 options = f" {options}" if options else "" 1020 1021 return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip() 1022 1023 def clone_sql(self, expression: exp.Clone) -> str: 1024 this = self.sql(expression, "this") 1025 shallow = "SHALLOW " if expression.args.get("shallow") else "" 1026 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 1027 return f"{shallow}{keyword} {this}" 1028 1029 def describe_sql(self, expression: exp.Describe) -> str: 1030 style = expression.args.get("style") 1031 style = f" {style}" if style else "" 1032 return f"DESCRIBE{style} {self.sql(expression, 'this')}" 1033 1034 def heredoc_sql(self, expression: exp.Heredoc) -> str: 1035 tag = self.sql(expression, "tag") 1036 return f"${tag}${self.sql(expression, 'this')}${tag}$" 1037 1038 def prepend_ctes(self, expression: exp.Expression, sql: str) -> str: 1039 with_ = self.sql(expression, "with") 1040 if with_: 1041 sql = f"{with_}{self.sep()}{sql}" 1042 return sql 1043 1044 def with_sql(self, expression: exp.With) -> str: 1045 sql = self.expressions(expression, flat=True) 1046 recursive = ( 1047 "RECURSIVE " 1048 if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive") 1049 else "" 1050 ) 1051 1052 return f"WITH {recursive}{sql}" 1053 1054 def cte_sql(self, expression: exp.CTE) -> str: 1055 alias = self.sql(expression, "alias") 1056 1057 materialized = expression.args.get("materialized") 1058 if materialized is False: 1059 materialized = "NOT MATERIALIZED " 1060 elif materialized: 1061 materialized = "MATERIALIZED " 1062 1063 return f"{alias} AS {materialized or ''}{self.wrap(expression)}" 1064 1065 def tablealias_sql(self, expression: exp.TableAlias) -> str: 1066 alias = self.sql(expression, "this") 1067 columns = self.expressions(expression, key="columns", flat=True) 1068 columns = f"({columns})" if columns else "" 1069 1070 if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS: 1071 columns = "" 1072 self.unsupported("Named columns are not supported in table alias.") 1073 1074 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 1075 alias = "_t" 1076 1077 return f"{alias}{columns}" 1078 1079 def bitstring_sql(self, expression: exp.BitString) -> str: 1080 this = self.sql(expression, "this") 1081 if self.dialect.BIT_START: 1082 return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}" 1083 return f"{int(this, 2)}" 1084 1085 def hexstring_sql(self, expression: exp.HexString) -> str: 1086 this = self.sql(expression, "this") 1087 if self.dialect.HEX_START: 1088 return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}" 1089 return f"{int(this, 16)}" 1090 1091 def bytestring_sql(self, expression: exp.ByteString) -> str: 1092 this = self.sql(expression, "this") 1093 if self.dialect.BYTE_START: 1094 return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}" 1095 return this 1096 1097 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 1098 this = self.sql(expression, "this") 1099 escape = expression.args.get("escape") 1100 1101 if self.dialect.UNICODE_START: 1102 escape = f" UESCAPE {self.sql(escape)}" if escape else "" 1103 return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}" 1104 1105 if escape: 1106 pattern = re.compile(rf"{escape.name}(\d+)") 1107 else: 1108 pattern = ESCAPED_UNICODE_RE 1109 1110 this = pattern.sub(r"\\u\1", this) 1111 return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}" 1112 1113 def rawstring_sql(self, expression: exp.RawString) -> str: 1114 string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False) 1115 return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}" 1116 1117 def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str: 1118 this = self.sql(expression, "this") 1119 specifier = self.sql(expression, "expression") 1120 specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else "" 1121 return f"{this}{specifier}" 1122 1123 def datatype_sql(self, expression: exp.DataType) -> str: 1124 type_value = expression.this 1125 1126 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 1127 type_sql = self.sql(expression, "kind") 1128 else: 1129 type_sql = ( 1130 self.TYPE_MAPPING.get(type_value, type_value.value) 1131 if isinstance(type_value, exp.DataType.Type) 1132 else type_value 1133 ) 1134 1135 nested = "" 1136 interior = self.expressions(expression, flat=True) 1137 values = "" 1138 1139 if interior: 1140 if expression.args.get("nested"): 1141 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 1142 if expression.args.get("values") is not None: 1143 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 1144 values = self.expressions(expression, key="values", flat=True) 1145 values = f"{delimiters[0]}{values}{delimiters[1]}" 1146 elif type_value == exp.DataType.Type.INTERVAL: 1147 nested = f" {interior}" 1148 else: 1149 nested = f"({interior})" 1150 1151 type_sql = f"{type_sql}{nested}{values}" 1152 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 1153 exp.DataType.Type.TIMETZ, 1154 exp.DataType.Type.TIMESTAMPTZ, 1155 ): 1156 type_sql = f"{type_sql} WITH TIME ZONE" 1157 1158 return type_sql 1159 1160 def directory_sql(self, expression: exp.Directory) -> str: 1161 local = "LOCAL " if expression.args.get("local") else "" 1162 row_format = self.sql(expression, "row_format") 1163 row_format = f" {row_format}" if row_format else "" 1164 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}" 1165 1166 def delete_sql(self, expression: exp.Delete) -> str: 1167 this = self.sql(expression, "this") 1168 this = f" FROM {this}" if this else "" 1169 using = self.sql(expression, "using") 1170 using = f" USING {using}" if using else "" 1171 where = self.sql(expression, "where") 1172 returning = self.sql(expression, "returning") 1173 limit = self.sql(expression, "limit") 1174 tables = self.expressions(expression, key="tables") 1175 tables = f" {tables}" if tables else "" 1176 if self.RETURNING_END: 1177 expression_sql = f"{this}{using}{where}{returning}{limit}" 1178 else: 1179 expression_sql = f"{returning}{this}{using}{where}{limit}" 1180 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}") 1181 1182 def drop_sql(self, expression: exp.Drop) -> str: 1183 this = self.sql(expression, "this") 1184 expressions = self.expressions(expression, flat=True) 1185 expressions = f" ({expressions})" if expressions else "" 1186 kind = expression.args["kind"] 1187 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1188 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1189 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1190 cascade = " CASCADE" if expression.args.get("cascade") else "" 1191 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1192 purge = " PURGE" if expression.args.get("purge") else "" 1193 return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{expressions}{cascade}{constraints}{purge}" 1194 1195 def except_sql(self, expression: exp.Except) -> str: 1196 return self.set_operations(expression) 1197 1198 def except_op(self, expression: exp.Except) -> str: 1199 return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}" 1200 1201 def fetch_sql(self, expression: exp.Fetch) -> str: 1202 direction = expression.args.get("direction") 1203 direction = f" {direction}" if direction else "" 1204 count = expression.args.get("count") 1205 count = f" {count}" if count else "" 1206 if expression.args.get("percent"): 1207 count = f"{count} PERCENT" 1208 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1209 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}" 1210 1211 def filter_sql(self, expression: exp.Filter) -> str: 1212 if self.AGGREGATE_FILTER_SUPPORTED: 1213 this = self.sql(expression, "this") 1214 where = self.sql(expression, "expression").strip() 1215 return f"{this} FILTER({where})" 1216 1217 agg = expression.this 1218 agg_arg = agg.this 1219 cond = expression.expression.this 1220 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1221 return self.sql(agg) 1222 1223 def hint_sql(self, expression: exp.Hint) -> str: 1224 if not self.QUERY_HINTS: 1225 self.unsupported("Hints are not supported") 1226 return "" 1227 1228 return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */" 1229 1230 def indexparameters_sql(self, expression: exp.IndexParameters) -> str: 1231 using = self.sql(expression, "using") 1232 using = f" USING {using}" if using else "" 1233 columns = self.expressions(expression, key="columns", flat=True) 1234 columns = f"({columns})" if columns else "" 1235 partition_by = self.expressions(expression, key="partition_by", flat=True) 1236 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1237 where = self.sql(expression, "where") 1238 include = self.expressions(expression, key="include", flat=True) 1239 if include: 1240 include = f" INCLUDE ({include})" 1241 with_storage = self.expressions(expression, key="with_storage", flat=True) 1242 with_storage = f" WITH ({with_storage})" if with_storage else "" 1243 tablespace = self.sql(expression, "tablespace") 1244 tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else "" 1245 1246 return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}" 1247 1248 def index_sql(self, expression: exp.Index) -> str: 1249 unique = "UNIQUE " if expression.args.get("unique") else "" 1250 primary = "PRIMARY " if expression.args.get("primary") else "" 1251 amp = "AMP " if expression.args.get("amp") else "" 1252 name = self.sql(expression, "this") 1253 name = f"{name} " if name else "" 1254 table = self.sql(expression, "table") 1255 table = f"{self.INDEX_ON} {table}" if table else "" 1256 1257 index = "INDEX " if not table else "" 1258 1259 params = self.sql(expression, "params") 1260 return f"{unique}{primary}{amp}{index}{name}{table}{params}" 1261 1262 def identifier_sql(self, expression: exp.Identifier) -> str: 1263 text = expression.name 1264 lower = text.lower() 1265 text = lower if self.normalize and not expression.quoted else text 1266 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1267 if ( 1268 expression.quoted 1269 or self.dialect.can_identify(text, self.identify) 1270 or lower in self.RESERVED_KEYWORDS 1271 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1272 ): 1273 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1274 return text 1275 1276 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1277 input_format = self.sql(expression, "input_format") 1278 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1279 output_format = self.sql(expression, "output_format") 1280 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1281 return self.sep().join((input_format, output_format)) 1282 1283 def national_sql(self, expression: exp.National, prefix: str = "N") -> str: 1284 string = self.sql(exp.Literal.string(expression.name)) 1285 return f"{prefix}{string}" 1286 1287 def partition_sql(self, expression: exp.Partition) -> str: 1288 return f"PARTITION({self.expressions(expression, flat=True)})" 1289 1290 def properties_sql(self, expression: exp.Properties) -> str: 1291 root_properties = [] 1292 with_properties = [] 1293 1294 for p in expression.expressions: 1295 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1296 if p_loc == exp.Properties.Location.POST_WITH: 1297 with_properties.append(p) 1298 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1299 root_properties.append(p) 1300 1301 return self.root_properties( 1302 exp.Properties(expressions=root_properties) 1303 ) + self.with_properties(exp.Properties(expressions=with_properties)) 1304 1305 def root_properties(self, properties: exp.Properties) -> str: 1306 if properties.expressions: 1307 return self.sep() + self.expressions(properties, indent=False, sep=" ") 1308 return "" 1309 1310 def properties( 1311 self, 1312 properties: exp.Properties, 1313 prefix: str = "", 1314 sep: str = ", ", 1315 suffix: str = "", 1316 wrapped: bool = True, 1317 ) -> str: 1318 if properties.expressions: 1319 expressions = self.expressions(properties, sep=sep, indent=False) 1320 if expressions: 1321 expressions = self.wrap(expressions) if wrapped else expressions 1322 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1323 return "" 1324 1325 def with_properties(self, properties: exp.Properties) -> str: 1326 return self.properties(properties, prefix=self.seg("WITH")) 1327 1328 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1329 properties_locs = defaultdict(list) 1330 for p in properties.expressions: 1331 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1332 if p_loc != exp.Properties.Location.UNSUPPORTED: 1333 properties_locs[p_loc].append(p) 1334 else: 1335 self.unsupported(f"Unsupported property {p.key}") 1336 1337 return properties_locs 1338 1339 def property_name(self, expression: exp.Property, string_key: bool = False) -> str: 1340 if isinstance(expression.this, exp.Dot): 1341 return self.sql(expression, "this") 1342 return f"'{expression.name}'" if string_key else expression.name 1343 1344 def property_sql(self, expression: exp.Property) -> str: 1345 property_cls = expression.__class__ 1346 if property_cls == exp.Property: 1347 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1348 1349 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1350 if not property_name: 1351 self.unsupported(f"Unsupported property {expression.key}") 1352 1353 return f"{property_name}={self.sql(expression, 'this')}" 1354 1355 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1356 if self.SUPPORTS_CREATE_TABLE_LIKE: 1357 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1358 options = f" {options}" if options else "" 1359 1360 like = f"LIKE {self.sql(expression, 'this')}{options}" 1361 if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema): 1362 like = f"({like})" 1363 1364 return like 1365 1366 if expression.expressions: 1367 self.unsupported("Transpilation of LIKE property options is unsupported") 1368 1369 select = exp.select("*").from_(expression.this).limit(0) 1370 return f"AS {self.sql(select)}" 1371 1372 def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str: 1373 no = "NO " if expression.args.get("no") else "" 1374 protection = " PROTECTION" if expression.args.get("protection") else "" 1375 return f"{no}FALLBACK{protection}" 1376 1377 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1378 no = "NO " if expression.args.get("no") else "" 1379 local = expression.args.get("local") 1380 local = f"{local} " if local else "" 1381 dual = "DUAL " if expression.args.get("dual") else "" 1382 before = "BEFORE " if expression.args.get("before") else "" 1383 after = "AFTER " if expression.args.get("after") else "" 1384 return f"{no}{local}{dual}{before}{after}JOURNAL" 1385 1386 def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str: 1387 freespace = self.sql(expression, "this") 1388 percent = " PERCENT" if expression.args.get("percent") else "" 1389 return f"FREESPACE={freespace}{percent}" 1390 1391 def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str: 1392 if expression.args.get("default"): 1393 property = "DEFAULT" 1394 elif expression.args.get("on"): 1395 property = "ON" 1396 else: 1397 property = "OFF" 1398 return f"CHECKSUM={property}" 1399 1400 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1401 if expression.args.get("no"): 1402 return "NO MERGEBLOCKRATIO" 1403 if expression.args.get("default"): 1404 return "DEFAULT MERGEBLOCKRATIO" 1405 1406 percent = " PERCENT" if expression.args.get("percent") else "" 1407 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}" 1408 1409 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1410 default = expression.args.get("default") 1411 minimum = expression.args.get("minimum") 1412 maximum = expression.args.get("maximum") 1413 if default or minimum or maximum: 1414 if default: 1415 prop = "DEFAULT" 1416 elif minimum: 1417 prop = "MINIMUM" 1418 else: 1419 prop = "MAXIMUM" 1420 return f"{prop} DATABLOCKSIZE" 1421 units = expression.args.get("units") 1422 units = f" {units}" if units else "" 1423 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}" 1424 1425 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1426 autotemp = expression.args.get("autotemp") 1427 always = expression.args.get("always") 1428 default = expression.args.get("default") 1429 manual = expression.args.get("manual") 1430 never = expression.args.get("never") 1431 1432 if autotemp is not None: 1433 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1434 elif always: 1435 prop = "ALWAYS" 1436 elif default: 1437 prop = "DEFAULT" 1438 elif manual: 1439 prop = "MANUAL" 1440 elif never: 1441 prop = "NEVER" 1442 return f"BLOCKCOMPRESSION={prop}" 1443 1444 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1445 no = expression.args.get("no") 1446 no = " NO" if no else "" 1447 concurrent = expression.args.get("concurrent") 1448 concurrent = " CONCURRENT" if concurrent else "" 1449 target = self.sql(expression, "target") 1450 target = f" {target}" if target else "" 1451 return f"WITH{no}{concurrent} ISOLATED LOADING{target}" 1452 1453 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1454 if isinstance(expression.this, list): 1455 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1456 if expression.this: 1457 modulus = self.sql(expression, "this") 1458 remainder = self.sql(expression, "expression") 1459 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1460 1461 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1462 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1463 return f"FROM ({from_expressions}) TO ({to_expressions})" 1464 1465 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1466 this = self.sql(expression, "this") 1467 1468 for_values_or_default = expression.expression 1469 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1470 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1471 else: 1472 for_values_or_default = " DEFAULT" 1473 1474 return f"PARTITION OF {this}{for_values_or_default}" 1475 1476 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1477 kind = expression.args.get("kind") 1478 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1479 for_or_in = expression.args.get("for_or_in") 1480 for_or_in = f" {for_or_in}" if for_or_in else "" 1481 lock_type = expression.args.get("lock_type") 1482 override = " OVERRIDE" if expression.args.get("override") else "" 1483 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}" 1484 1485 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1486 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1487 statistics = expression.args.get("statistics") 1488 statistics_sql = "" 1489 if statistics is not None: 1490 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1491 return f"{data_sql}{statistics_sql}" 1492 1493 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1494 sql = "WITH(SYSTEM_VERSIONING=ON" 1495 1496 if expression.this: 1497 history_table = self.sql(expression, "this") 1498 sql = f"{sql}(HISTORY_TABLE={history_table}" 1499 1500 if expression.expression: 1501 data_consistency_check = self.sql(expression, "expression") 1502 sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}" 1503 1504 sql = f"{sql})" 1505 1506 return f"{sql})" 1507 1508 def insert_sql(self, expression: exp.Insert) -> str: 1509 hint = self.sql(expression, "hint") 1510 overwrite = expression.args.get("overwrite") 1511 1512 if isinstance(expression.this, exp.Directory): 1513 this = " OVERWRITE" if overwrite else " INTO" 1514 else: 1515 this = self.INSERT_OVERWRITE if overwrite else " INTO" 1516 1517 alternative = expression.args.get("alternative") 1518 alternative = f" OR {alternative}" if alternative else "" 1519 ignore = " IGNORE" if expression.args.get("ignore") else "" 1520 is_function = expression.args.get("is_function") 1521 if is_function: 1522 this = f"{this} FUNCTION" 1523 this = f"{this} {self.sql(expression, 'this')}" 1524 1525 exists = " IF EXISTS" if expression.args.get("exists") else "" 1526 partition_sql = ( 1527 f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else "" 1528 ) 1529 where = self.sql(expression, "where") 1530 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1531 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1532 on_conflict = self.sql(expression, "conflict") 1533 on_conflict = f" {on_conflict}" if on_conflict else "" 1534 by_name = " BY NAME" if expression.args.get("by_name") else "" 1535 returning = self.sql(expression, "returning") 1536 1537 if self.RETURNING_END: 1538 expression_sql = f"{expression_sql}{on_conflict}{returning}" 1539 else: 1540 expression_sql = f"{returning}{expression_sql}{on_conflict}" 1541 1542 sql = f"INSERT{hint}{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}" 1543 return self.prepend_ctes(expression, sql) 1544 1545 def intersect_sql(self, expression: exp.Intersect) -> str: 1546 return self.set_operations(expression) 1547 1548 def intersect_op(self, expression: exp.Intersect) -> str: 1549 return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}" 1550 1551 def introducer_sql(self, expression: exp.Introducer) -> str: 1552 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 1553 1554 def kill_sql(self, expression: exp.Kill) -> str: 1555 kind = self.sql(expression, "kind") 1556 kind = f" {kind}" if kind else "" 1557 this = self.sql(expression, "this") 1558 this = f" {this}" if this else "" 1559 return f"KILL{kind}{this}" 1560 1561 def pseudotype_sql(self, expression: exp.PseudoType) -> str: 1562 return expression.name 1563 1564 def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str: 1565 return expression.name 1566 1567 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1568 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1569 1570 constraint = self.sql(expression, "constraint") 1571 constraint = f" ON CONSTRAINT {constraint}" if constraint else "" 1572 1573 conflict_keys = self.expressions(expression, key="conflict_keys", flat=True) 1574 conflict_keys = f"({conflict_keys}) " if conflict_keys else " " 1575 action = self.sql(expression, "action") 1576 1577 expressions = self.expressions(expression, flat=True) 1578 if expressions: 1579 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1580 expressions = f" {set_keyword}{expressions}" 1581 1582 return f"{conflict}{constraint}{conflict_keys}{action}{expressions}" 1583 1584 def returning_sql(self, expression: exp.Returning) -> str: 1585 return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}" 1586 1587 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1588 fields = self.sql(expression, "fields") 1589 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1590 escaped = self.sql(expression, "escaped") 1591 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1592 items = self.sql(expression, "collection_items") 1593 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1594 keys = self.sql(expression, "map_keys") 1595 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1596 lines = self.sql(expression, "lines") 1597 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1598 null = self.sql(expression, "null") 1599 null = f" NULL DEFINED AS {null}" if null else "" 1600 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}" 1601 1602 def withtablehint_sql(self, expression: exp.WithTableHint) -> str: 1603 return f"WITH ({self.expressions(expression, flat=True)})" 1604 1605 def indextablehint_sql(self, expression: exp.IndexTableHint) -> str: 1606 this = f"{self.sql(expression, 'this')} INDEX" 1607 target = self.sql(expression, "target") 1608 target = f" FOR {target}" if target else "" 1609 return f"{this}{target} ({self.expressions(expression, flat=True)})" 1610 1611 def historicaldata_sql(self, expression: exp.HistoricalData) -> str: 1612 this = self.sql(expression, "this") 1613 kind = self.sql(expression, "kind") 1614 expr = self.sql(expression, "expression") 1615 return f"{this} ({kind} => {expr})" 1616 1617 def table_parts(self, expression: exp.Table) -> str: 1618 return ".".join( 1619 self.sql(part) 1620 for part in ( 1621 expression.args.get("catalog"), 1622 expression.args.get("db"), 1623 expression.args.get("this"), 1624 ) 1625 if part is not None 1626 ) 1627 1628 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1629 table = self.table_parts(expression) 1630 only = "ONLY " if expression.args.get("only") else "" 1631 version = self.sql(expression, "version") 1632 version = f" {version}" if version else "" 1633 alias = self.sql(expression, "alias") 1634 alias = f"{sep}{alias}" if alias else "" 1635 hints = self.expressions(expression, key="hints", sep=" ") 1636 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1637 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 1638 pivots = f" {pivots}" if pivots else "" 1639 joins = self.indent( 1640 self.expressions(expression, key="joins", sep="", flat=True), skip_first=True 1641 ) 1642 laterals = self.expressions(expression, key="laterals", sep="") 1643 1644 file_format = self.sql(expression, "format") 1645 if file_format: 1646 pattern = self.sql(expression, "pattern") 1647 pattern = f", PATTERN => {pattern}" if pattern else "" 1648 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1649 1650 ordinality = expression.args.get("ordinality") or "" 1651 if ordinality: 1652 ordinality = f" WITH ORDINALITY{alias}" 1653 alias = "" 1654 1655 when = self.sql(expression, "when") 1656 if when: 1657 table = f"{table} {when}" 1658 1659 return f"{only}{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}" 1660 1661 def tablesample_sql( 1662 self, 1663 expression: exp.TableSample, 1664 sep: str = " AS ", 1665 tablesample_keyword: t.Optional[str] = None, 1666 ) -> str: 1667 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1668 table = expression.this.copy() 1669 table.set("alias", None) 1670 this = self.sql(table) 1671 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1672 else: 1673 this = self.sql(expression, "this") 1674 alias = "" 1675 1676 method = self.sql(expression, "method") 1677 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1678 numerator = self.sql(expression, "bucket_numerator") 1679 denominator = self.sql(expression, "bucket_denominator") 1680 field = self.sql(expression, "bucket_field") 1681 field = f" ON {field}" if field else "" 1682 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1683 seed = self.sql(expression, "seed") 1684 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1685 1686 size = self.sql(expression, "size") 1687 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1688 size = f"{size} ROWS" 1689 1690 percent = self.sql(expression, "percent") 1691 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1692 percent = f"{percent} PERCENT" 1693 1694 expr = f"{bucket}{percent}{size}" 1695 if self.TABLESAMPLE_REQUIRES_PARENS: 1696 expr = f"({expr})" 1697 1698 return ( 1699 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1700 ) 1701 1702 def pivot_sql(self, expression: exp.Pivot) -> str: 1703 expressions = self.expressions(expression, flat=True) 1704 1705 if expression.this: 1706 this = self.sql(expression, "this") 1707 if not expressions: 1708 return f"UNPIVOT {this}" 1709 1710 on = f"{self.seg('ON')} {expressions}" 1711 using = self.expressions(expression, key="using", flat=True) 1712 using = f"{self.seg('USING')} {using}" if using else "" 1713 group = self.sql(expression, "group") 1714 return f"PIVOT {this}{on}{using}{group}" 1715 1716 alias = self.sql(expression, "alias") 1717 alias = f" AS {alias}" if alias else "" 1718 direction = "UNPIVOT" if expression.unpivot else "PIVOT" 1719 field = self.sql(expression, "field") 1720 include_nulls = expression.args.get("include_nulls") 1721 if include_nulls is not None: 1722 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1723 else: 1724 nulls = "" 1725 return f"{direction}{nulls}({expressions} FOR {field}){alias}" 1726 1727 def version_sql(self, expression: exp.Version) -> str: 1728 this = f"FOR {expression.name}" 1729 kind = expression.text("kind") 1730 expr = self.sql(expression, "expression") 1731 return f"{this} {kind} {expr}" 1732 1733 def tuple_sql(self, expression: exp.Tuple) -> str: 1734 return f"({self.expressions(expression, flat=True)})" 1735 1736 def update_sql(self, expression: exp.Update) -> str: 1737 this = self.sql(expression, "this") 1738 set_sql = self.expressions(expression, flat=True) 1739 from_sql = self.sql(expression, "from") 1740 where_sql = self.sql(expression, "where") 1741 returning = self.sql(expression, "returning") 1742 order = self.sql(expression, "order") 1743 limit = self.sql(expression, "limit") 1744 if self.RETURNING_END: 1745 expression_sql = f"{from_sql}{where_sql}{returning}" 1746 else: 1747 expression_sql = f"{returning}{from_sql}{where_sql}" 1748 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1749 return self.prepend_ctes(expression, sql) 1750 1751 def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str: 1752 values_as_table = values_as_table and self.VALUES_AS_TABLE 1753 1754 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1755 if values_as_table or not expression.find_ancestor(exp.From, exp.Join): 1756 args = self.expressions(expression) 1757 alias = self.sql(expression, "alias") 1758 values = f"VALUES{self.seg('')}{args}" 1759 values = ( 1760 f"({values})" 1761 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1762 else values 1763 ) 1764 return f"{values} AS {alias}" if alias else values 1765 1766 # Converts `VALUES...` expression into a series of select unions. 1767 alias_node = expression.args.get("alias") 1768 column_names = alias_node and alias_node.columns 1769 1770 selects: t.List[exp.Query] = [] 1771 1772 for i, tup in enumerate(expression.expressions): 1773 row = tup.expressions 1774 1775 if i == 0 and column_names: 1776 row = [ 1777 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1778 ] 1779 1780 selects.append(exp.Select(expressions=row)) 1781 1782 if self.pretty: 1783 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1784 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1785 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1786 query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1787 return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False)) 1788 1789 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1790 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1791 return f"({unions}){alias}" 1792 1793 def var_sql(self, expression: exp.Var) -> str: 1794 return self.sql(expression, "this") 1795 1796 def into_sql(self, expression: exp.Into) -> str: 1797 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1798 unlogged = " UNLOGGED" if expression.args.get("unlogged") else "" 1799 return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}" 1800 1801 def from_sql(self, expression: exp.From) -> str: 1802 return f"{self.seg('FROM')} {self.sql(expression, 'this')}" 1803 1804 def group_sql(self, expression: exp.Group) -> str: 1805 group_by = self.op_expressions("GROUP BY", expression) 1806 1807 if expression.args.get("all"): 1808 return f"{group_by} ALL" 1809 1810 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1811 grouping_sets = ( 1812 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1813 ) 1814 1815 cube = expression.args.get("cube", []) 1816 if seq_get(cube, 0) is True: 1817 return f"{group_by}{self.seg('WITH CUBE')}" 1818 else: 1819 cube_sql = self.expressions(expression, key="cube", indent=False) 1820 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1821 1822 rollup = expression.args.get("rollup", []) 1823 if seq_get(rollup, 0) is True: 1824 return f"{group_by}{self.seg('WITH ROLLUP')}" 1825 else: 1826 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1827 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1828 1829 groupings = csv( 1830 grouping_sets, 1831 cube_sql, 1832 rollup_sql, 1833 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1834 sep=self.GROUPINGS_SEP, 1835 ) 1836 1837 if expression.args.get("expressions") and groupings: 1838 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1839 1840 return f"{group_by}{groupings}" 1841 1842 def having_sql(self, expression: exp.Having) -> str: 1843 this = self.indent(self.sql(expression, "this")) 1844 return f"{self.seg('HAVING')}{self.sep()}{this}" 1845 1846 def connect_sql(self, expression: exp.Connect) -> str: 1847 start = self.sql(expression, "start") 1848 start = self.seg(f"START WITH {start}") if start else "" 1849 nocycle = " NOCYCLE" if expression.args.get("nocycle") else "" 1850 connect = self.sql(expression, "connect") 1851 connect = self.seg(f"CONNECT BY{nocycle} {connect}") 1852 return start + connect 1853 1854 def prior_sql(self, expression: exp.Prior) -> str: 1855 return f"PRIOR {self.sql(expression, 'this')}" 1856 1857 def join_sql(self, expression: exp.Join) -> str: 1858 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1859 side = None 1860 else: 1861 side = expression.side 1862 1863 op_sql = " ".join( 1864 op 1865 for op in ( 1866 expression.method, 1867 "GLOBAL" if expression.args.get("global") else None, 1868 side, 1869 expression.kind, 1870 expression.hint if self.JOIN_HINTS else None, 1871 ) 1872 if op 1873 ) 1874 match_cond = self.sql(expression, "match_condition") 1875 match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else "" 1876 on_sql = self.sql(expression, "on") 1877 using = expression.args.get("using") 1878 1879 if not on_sql and using: 1880 on_sql = csv(*(self.sql(column) for column in using)) 1881 1882 this = expression.this 1883 this_sql = self.sql(this) 1884 1885 if on_sql: 1886 on_sql = self.indent(on_sql, skip_first=True) 1887 space = self.seg(" " * self.pad) if self.pretty else " " 1888 if using: 1889 on_sql = f"{space}USING ({on_sql})" 1890 else: 1891 on_sql = f"{space}ON {on_sql}" 1892 elif not op_sql: 1893 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 1894 return f" {this_sql}" 1895 1896 return f", {this_sql}" 1897 1898 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1899 return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}" 1900 1901 def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str: 1902 args = self.expressions(expression, flat=True) 1903 args = f"({args})" if len(args.split(",")) > 1 else args 1904 return f"{args} {arrow_sep} {self.sql(expression, 'this')}" 1905 1906 def lateral_op(self, expression: exp.Lateral) -> str: 1907 cross_apply = expression.args.get("cross_apply") 1908 1909 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 1910 if cross_apply is True: 1911 op = "INNER JOIN " 1912 elif cross_apply is False: 1913 op = "LEFT JOIN " 1914 else: 1915 op = "" 1916 1917 return f"{op}LATERAL" 1918 1919 def lateral_sql(self, expression: exp.Lateral) -> str: 1920 this = self.sql(expression, "this") 1921 1922 if expression.args.get("view"): 1923 alias = expression.args["alias"] 1924 columns = self.expressions(alias, key="columns", flat=True) 1925 table = f" {alias.name}" if alias.name else "" 1926 columns = f" AS {columns}" if columns else "" 1927 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1928 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1929 1930 alias = self.sql(expression, "alias") 1931 alias = f" AS {alias}" if alias else "" 1932 return f"{self.lateral_op(expression)} {this}{alias}" 1933 1934 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1935 this = self.sql(expression, "this") 1936 1937 args = [ 1938 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1939 for e in (expression.args.get(k) for k in ("offset", "expression")) 1940 if e 1941 ] 1942 1943 args_sql = ", ".join(self.sql(e) for e in args) 1944 args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql 1945 expressions = self.expressions(expression, flat=True) 1946 expressions = f" BY {expressions}" if expressions else "" 1947 1948 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}" 1949 1950 def offset_sql(self, expression: exp.Offset) -> str: 1951 this = self.sql(expression, "this") 1952 value = expression.expression 1953 value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value 1954 expressions = self.expressions(expression, flat=True) 1955 expressions = f" BY {expressions}" if expressions else "" 1956 return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}" 1957 1958 def setitem_sql(self, expression: exp.SetItem) -> str: 1959 kind = self.sql(expression, "kind") 1960 kind = f"{kind} " if kind else "" 1961 this = self.sql(expression, "this") 1962 expressions = self.expressions(expression) 1963 collate = self.sql(expression, "collate") 1964 collate = f" COLLATE {collate}" if collate else "" 1965 global_ = "GLOBAL " if expression.args.get("global") else "" 1966 return f"{global_}{kind}{this}{expressions}{collate}" 1967 1968 def set_sql(self, expression: exp.Set) -> str: 1969 expressions = ( 1970 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 1971 ) 1972 tag = " TAG" if expression.args.get("tag") else "" 1973 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}" 1974 1975 def pragma_sql(self, expression: exp.Pragma) -> str: 1976 return f"PRAGMA {self.sql(expression, 'this')}" 1977 1978 def lock_sql(self, expression: exp.Lock) -> str: 1979 if not self.LOCKING_READS_SUPPORTED: 1980 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 1981 return "" 1982 1983 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 1984 expressions = self.expressions(expression, flat=True) 1985 expressions = f" OF {expressions}" if expressions else "" 1986 wait = expression.args.get("wait") 1987 1988 if wait is not None: 1989 if isinstance(wait, exp.Literal): 1990 wait = f" WAIT {self.sql(wait)}" 1991 else: 1992 wait = " NOWAIT" if wait else " SKIP LOCKED" 1993 1994 return f"{lock_type}{expressions}{wait or ''}" 1995 1996 def literal_sql(self, expression: exp.Literal) -> str: 1997 text = expression.this or "" 1998 if expression.is_string: 1999 text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}" 2000 return text 2001 2002 def escape_str(self, text: str, escape_backslash: bool = True) -> str: 2003 if self.dialect.ESCAPED_SEQUENCES: 2004 to_escaped = self.dialect.ESCAPED_SEQUENCES 2005 text = "".join( 2006 to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text 2007 ) 2008 2009 if self.pretty: 2010 text = text.replace("\n", self.SENTINEL_LINE_BREAK) 2011 2012 return text.replace(self.dialect.QUOTE_END, self._escaped_quote_end) 2013 2014 def loaddata_sql(self, expression: exp.LoadData) -> str: 2015 local = " LOCAL" if expression.args.get("local") else "" 2016 inpath = f" INPATH {self.sql(expression, 'inpath')}" 2017 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 2018 this = f" INTO TABLE {self.sql(expression, 'this')}" 2019 partition = self.sql(expression, "partition") 2020 partition = f" {partition}" if partition else "" 2021 input_format = self.sql(expression, "input_format") 2022 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 2023 serde = self.sql(expression, "serde") 2024 serde = f" SERDE {serde}" if serde else "" 2025 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}" 2026 2027 def null_sql(self, *_) -> str: 2028 return "NULL" 2029 2030 def boolean_sql(self, expression: exp.Boolean) -> str: 2031 return "TRUE" if expression.this else "FALSE" 2032 2033 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 2034 this = self.sql(expression, "this") 2035 this = f"{this} " if this else this 2036 siblings = "SIBLINGS " if expression.args.get("siblings") else "" 2037 order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore 2038 interpolated_values = [ 2039 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 2040 for named_expression in expression.args.get("interpolate") or [] 2041 ] 2042 interpolate = ( 2043 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 2044 ) 2045 return f"{order}{interpolate}" 2046 2047 def withfill_sql(self, expression: exp.WithFill) -> str: 2048 from_sql = self.sql(expression, "from") 2049 from_sql = f" FROM {from_sql}" if from_sql else "" 2050 to_sql = self.sql(expression, "to") 2051 to_sql = f" TO {to_sql}" if to_sql else "" 2052 step_sql = self.sql(expression, "step") 2053 step_sql = f" STEP {step_sql}" if step_sql else "" 2054 return f"WITH FILL{from_sql}{to_sql}{step_sql}" 2055 2056 def cluster_sql(self, expression: exp.Cluster) -> str: 2057 return self.op_expressions("CLUSTER BY", expression) 2058 2059 def distribute_sql(self, expression: exp.Distribute) -> str: 2060 return self.op_expressions("DISTRIBUTE BY", expression) 2061 2062 def sort_sql(self, expression: exp.Sort) -> str: 2063 return self.op_expressions("SORT BY", expression) 2064 2065 def ordered_sql(self, expression: exp.Ordered) -> str: 2066 desc = expression.args.get("desc") 2067 asc = not desc 2068 2069 nulls_first = expression.args.get("nulls_first") 2070 nulls_last = not nulls_first 2071 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 2072 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 2073 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 2074 2075 this = self.sql(expression, "this") 2076 2077 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 2078 nulls_sort_change = "" 2079 if nulls_first and ( 2080 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 2081 ): 2082 nulls_sort_change = " NULLS FIRST" 2083 elif ( 2084 nulls_last 2085 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 2086 and not nulls_are_last 2087 ): 2088 nulls_sort_change = " NULLS LAST" 2089 2090 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 2091 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 2092 window = expression.find_ancestor(exp.Window, exp.Select) 2093 if isinstance(window, exp.Window) and window.args.get("spec"): 2094 self.unsupported( 2095 f"'{nulls_sort_change.strip()}' translation not supported in window functions" 2096 ) 2097 nulls_sort_change = "" 2098 elif self.NULL_ORDERING_SUPPORTED is None: 2099 if expression.this.is_int: 2100 self.unsupported( 2101 f"'{nulls_sort_change.strip()}' translation not supported with positional ordering" 2102 ) 2103 elif not isinstance(expression.this, exp.Rand): 2104 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 2105 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 2106 nulls_sort_change = "" 2107 2108 with_fill = self.sql(expression, "with_fill") 2109 with_fill = f" {with_fill}" if with_fill else "" 2110 2111 return f"{this}{sort_order}{nulls_sort_change}{with_fill}" 2112 2113 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 2114 partition = self.partition_by_sql(expression) 2115 order = self.sql(expression, "order") 2116 measures = self.expressions(expression, key="measures") 2117 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 2118 rows = self.sql(expression, "rows") 2119 rows = self.seg(rows) if rows else "" 2120 after = self.sql(expression, "after") 2121 after = self.seg(after) if after else "" 2122 pattern = self.sql(expression, "pattern") 2123 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 2124 definition_sqls = [ 2125 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 2126 for definition in expression.args.get("define", []) 2127 ] 2128 definitions = self.expressions(sqls=definition_sqls) 2129 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 2130 body = "".join( 2131 ( 2132 partition, 2133 order, 2134 measures, 2135 rows, 2136 after, 2137 pattern, 2138 define, 2139 ) 2140 ) 2141 alias = self.sql(expression, "alias") 2142 alias = f" {alias}" if alias else "" 2143 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}" 2144 2145 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 2146 limit = expression.args.get("limit") 2147 2148 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 2149 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 2150 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 2151 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 2152 2153 options = self.expressions(expression, key="options") 2154 if options: 2155 options = f" OPTION{self.wrap(options)}" 2156 2157 return csv( 2158 *sqls, 2159 *[self.sql(join) for join in expression.args.get("joins") or []], 2160 self.sql(expression, "connect"), 2161 self.sql(expression, "match"), 2162 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 2163 self.sql(expression, "prewhere"), 2164 self.sql(expression, "where"), 2165 self.sql(expression, "group"), 2166 self.sql(expression, "having"), 2167 *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()], 2168 self.sql(expression, "order"), 2169 *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit), 2170 *self.after_limit_modifiers(expression), 2171 options, 2172 sep="", 2173 ) 2174 2175 def queryoption_sql(self, expression: exp.QueryOption) -> str: 2176 return "" 2177 2178 def offset_limit_modifiers( 2179 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 2180 ) -> t.List[str]: 2181 return [ 2182 self.sql(expression, "offset") if fetch else self.sql(limit), 2183 self.sql(limit) if fetch else self.sql(expression, "offset"), 2184 ] 2185 2186 def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]: 2187 locks = self.expressions(expression, key="locks", sep=" ") 2188 locks = f" {locks}" if locks else "" 2189 return [locks, self.sql(expression, "sample")] 2190 2191 def select_sql(self, expression: exp.Select) -> str: 2192 into = expression.args.get("into") 2193 if not self.SUPPORTS_SELECT_INTO and into: 2194 into.pop() 2195 2196 hint = self.sql(expression, "hint") 2197 distinct = self.sql(expression, "distinct") 2198 distinct = f" {distinct}" if distinct else "" 2199 kind = self.sql(expression, "kind") 2200 2201 limit = expression.args.get("limit") 2202 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP: 2203 top = self.limit_sql(limit, top=True) 2204 limit.pop() 2205 else: 2206 top = "" 2207 2208 expressions = self.expressions(expression) 2209 2210 if kind: 2211 if kind in self.SELECT_KINDS: 2212 kind = f" AS {kind}" 2213 else: 2214 if kind == "STRUCT": 2215 expressions = self.expressions( 2216 sqls=[ 2217 self.sql( 2218 exp.Struct( 2219 expressions=[ 2220 exp.PropertyEQ(this=e.args.get("alias"), expression=e.this) 2221 if isinstance(e, exp.Alias) 2222 else e 2223 for e in expression.expressions 2224 ] 2225 ) 2226 ) 2227 ] 2228 ) 2229 kind = "" 2230 2231 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2232 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2233 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2234 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2235 sql = self.query_modifiers( 2236 expression, 2237 f"SELECT{top_distinct}{kind}{expressions}", 2238 self.sql(expression, "into", comment=False), 2239 self.sql(expression, "from", comment=False), 2240 ) 2241 2242 sql = self.prepend_ctes(expression, sql) 2243 2244 if not self.SUPPORTS_SELECT_INTO and into: 2245 if into.args.get("temporary"): 2246 table_kind = " TEMPORARY" 2247 elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"): 2248 table_kind = " UNLOGGED" 2249 else: 2250 table_kind = "" 2251 sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}" 2252 2253 return sql 2254 2255 def schema_sql(self, expression: exp.Schema) -> str: 2256 this = self.sql(expression, "this") 2257 sql = self.schema_columns_sql(expression) 2258 return f"{this} {sql}" if this and sql else this or sql 2259 2260 def schema_columns_sql(self, expression: exp.Schema) -> str: 2261 if expression.expressions: 2262 return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}" 2263 return "" 2264 2265 def star_sql(self, expression: exp.Star) -> str: 2266 except_ = self.expressions(expression, key="except", flat=True) 2267 except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else "" 2268 replace = self.expressions(expression, key="replace", flat=True) 2269 replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else "" 2270 return f"*{except_}{replace}" 2271 2272 def parameter_sql(self, expression: exp.Parameter) -> str: 2273 this = self.sql(expression, "this") 2274 return f"{self.PARAMETER_TOKEN}{this}" 2275 2276 def sessionparameter_sql(self, expression: exp.SessionParameter) -> str: 2277 this = self.sql(expression, "this") 2278 kind = expression.text("kind") 2279 if kind: 2280 kind = f"{kind}." 2281 return f"@@{kind}{this}" 2282 2283 def placeholder_sql(self, expression: exp.Placeholder) -> str: 2284 return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?" 2285 2286 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2287 alias = self.sql(expression, "alias") 2288 alias = f"{sep}{alias}" if alias else "" 2289 2290 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2291 pivots = f" {pivots}" if pivots else "" 2292 2293 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2294 return self.prepend_ctes(expression, sql) 2295 2296 def qualify_sql(self, expression: exp.Qualify) -> str: 2297 this = self.indent(self.sql(expression, "this")) 2298 return f"{self.seg('QUALIFY')}{self.sep()}{this}" 2299 2300 def set_operations(self, expression: exp.Union) -> str: 2301 sqls: t.List[str] = [] 2302 stack: t.List[t.Union[str, exp.Expression]] = [expression] 2303 2304 while stack: 2305 node = stack.pop() 2306 2307 if isinstance(node, exp.Union): 2308 stack.append(node.expression) 2309 stack.append( 2310 self.maybe_comment( 2311 getattr(self, f"{node.key}_op")(node), 2312 expression=node.this, 2313 comments=node.comments, 2314 ) 2315 ) 2316 stack.append(node.this) 2317 else: 2318 sqls.append(self.sql(node)) 2319 2320 this = self.sep().join(sqls) 2321 this = self.query_modifiers(expression, this) 2322 return self.prepend_ctes(expression, this) 2323 2324 def union_sql(self, expression: exp.Union) -> str: 2325 return self.set_operations(expression) 2326 2327 def union_op(self, expression: exp.Union) -> str: 2328 kind = " DISTINCT" if self.EXPLICIT_UNION else "" 2329 kind = kind if expression.args.get("distinct") else " ALL" 2330 by_name = " BY NAME" if expression.args.get("by_name") else "" 2331 return f"UNION{kind}{by_name}" 2332 2333 def unnest_sql(self, expression: exp.Unnest) -> str: 2334 args = self.expressions(expression, flat=True) 2335 2336 alias = expression.args.get("alias") 2337 offset = expression.args.get("offset") 2338 2339 if self.UNNEST_WITH_ORDINALITY: 2340 if alias and isinstance(offset, exp.Expression): 2341 alias.append("columns", offset) 2342 2343 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2344 columns = alias.columns 2345 alias = self.sql(columns[0]) if columns else "" 2346 else: 2347 alias = self.sql(alias) 2348 2349 alias = f" AS {alias}" if alias else alias 2350 if self.UNNEST_WITH_ORDINALITY: 2351 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2352 else: 2353 if isinstance(offset, exp.Expression): 2354 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2355 elif offset: 2356 suffix = f"{alias} WITH OFFSET" 2357 else: 2358 suffix = alias 2359 2360 return f"UNNEST({args}){suffix}" 2361 2362 def prewhere_sql(self, expression: exp.PreWhere) -> str: 2363 return "" 2364 2365 def where_sql(self, expression: exp.Where) -> str: 2366 this = self.indent(self.sql(expression, "this")) 2367 return f"{self.seg('WHERE')}{self.sep()}{this}" 2368 2369 def window_sql(self, expression: exp.Window) -> str: 2370 this = self.sql(expression, "this") 2371 partition = self.partition_by_sql(expression) 2372 order = expression.args.get("order") 2373 order = self.order_sql(order, flat=True) if order else "" 2374 spec = self.sql(expression, "spec") 2375 alias = self.sql(expression, "alias") 2376 over = self.sql(expression, "over") or "OVER" 2377 2378 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2379 2380 first = expression.args.get("first") 2381 if first is None: 2382 first = "" 2383 else: 2384 first = "FIRST" if first else "LAST" 2385 2386 if not partition and not order and not spec and alias: 2387 return f"{this} {alias}" 2388 2389 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2390 return f"{this} ({args})" 2391 2392 def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str: 2393 partition = self.expressions(expression, key="partition_by", flat=True) 2394 return f"PARTITION BY {partition}" if partition else "" 2395 2396 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2397 kind = self.sql(expression, "kind") 2398 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2399 end = ( 2400 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2401 or "CURRENT ROW" 2402 ) 2403 return f"{kind} BETWEEN {start} AND {end}" 2404 2405 def withingroup_sql(self, expression: exp.WithinGroup) -> str: 2406 this = self.sql(expression, "this") 2407 expression_sql = self.sql(expression, "expression")[1:] # order has a leading space 2408 return f"{this} WITHIN GROUP ({expression_sql})" 2409 2410 def between_sql(self, expression: exp.Between) -> str: 2411 this = self.sql(expression, "this") 2412 low = self.sql(expression, "low") 2413 high = self.sql(expression, "high") 2414 return f"{this} BETWEEN {low} AND {high}" 2415 2416 def bracket_sql(self, expression: exp.Bracket) -> str: 2417 expressions = apply_index_offset( 2418 expression.this, 2419 expression.expressions, 2420 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2421 ) 2422 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2423 return f"{self.sql(expression, 'this')}[{expressions_sql}]" 2424 2425 def all_sql(self, expression: exp.All) -> str: 2426 return f"ALL {self.wrap(expression)}" 2427 2428 def any_sql(self, expression: exp.Any) -> str: 2429 this = self.sql(expression, "this") 2430 if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)): 2431 if isinstance(expression.this, exp.UNWRAPPED_QUERIES): 2432 this = self.wrap(this) 2433 return f"ANY{this}" 2434 return f"ANY {this}" 2435 2436 def exists_sql(self, expression: exp.Exists) -> str: 2437 return f"EXISTS{self.wrap(expression)}" 2438 2439 def case_sql(self, expression: exp.Case) -> str: 2440 this = self.sql(expression, "this") 2441 statements = [f"CASE {this}" if this else "CASE"] 2442 2443 for e in expression.args["ifs"]: 2444 statements.append(f"WHEN {self.sql(e, 'this')}") 2445 statements.append(f"THEN {self.sql(e, 'true')}") 2446 2447 default = self.sql(expression, "default") 2448 2449 if default: 2450 statements.append(f"ELSE {default}") 2451 2452 statements.append("END") 2453 2454 if self.pretty and self.text_width(statements) > self.max_text_width: 2455 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2456 2457 return " ".join(statements) 2458 2459 def constraint_sql(self, expression: exp.Constraint) -> str: 2460 this = self.sql(expression, "this") 2461 expressions = self.expressions(expression, flat=True) 2462 return f"CONSTRAINT {this} {expressions}" 2463 2464 def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str: 2465 order = expression.args.get("order") 2466 order = f" OVER ({self.order_sql(order, flat=True)})" if order else "" 2467 return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}" 2468 2469 def extract_sql(self, expression: exp.Extract) -> str: 2470 this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name 2471 expression_sql = self.sql(expression, "expression") 2472 return f"EXTRACT({this} FROM {expression_sql})" 2473 2474 def trim_sql(self, expression: exp.Trim) -> str: 2475 trim_type = self.sql(expression, "position") 2476 2477 if trim_type == "LEADING": 2478 return self.func("LTRIM", expression.this) 2479 elif trim_type == "TRAILING": 2480 return self.func("RTRIM", expression.this) 2481 else: 2482 return self.func("TRIM", expression.this, expression.expression) 2483 2484 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2485 args = expression.expressions 2486 if isinstance(expression, exp.ConcatWs): 2487 args = args[1:] # Skip the delimiter 2488 2489 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2490 args = [exp.cast(e, "text") for e in args] 2491 2492 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2493 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2494 2495 return args 2496 2497 def concat_sql(self, expression: exp.Concat) -> str: 2498 expressions = self.convert_concat_args(expression) 2499 2500 # Some dialects don't allow a single-argument CONCAT call 2501 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2502 return self.sql(expressions[0]) 2503 2504 return self.func("CONCAT", *expressions) 2505 2506 def concatws_sql(self, expression: exp.ConcatWs) -> str: 2507 return self.func( 2508 "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression) 2509 ) 2510 2511 def check_sql(self, expression: exp.Check) -> str: 2512 this = self.sql(expression, key="this") 2513 return f"CHECK ({this})" 2514 2515 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2516 expressions = self.expressions(expression, flat=True) 2517 reference = self.sql(expression, "reference") 2518 reference = f" {reference}" if reference else "" 2519 delete = self.sql(expression, "delete") 2520 delete = f" ON DELETE {delete}" if delete else "" 2521 update = self.sql(expression, "update") 2522 update = f" ON UPDATE {update}" if update else "" 2523 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}" 2524 2525 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2526 expressions = self.expressions(expression, flat=True) 2527 options = self.expressions(expression, key="options", flat=True, sep=" ") 2528 options = f" {options}" if options else "" 2529 return f"PRIMARY KEY ({expressions}){options}" 2530 2531 def if_sql(self, expression: exp.If) -> str: 2532 return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false"))) 2533 2534 def matchagainst_sql(self, expression: exp.MatchAgainst) -> str: 2535 modifier = expression.args.get("modifier") 2536 modifier = f" {modifier}" if modifier else "" 2537 return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})" 2538 2539 def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str: 2540 return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}" 2541 2542 def jsonpath_sql(self, expression: exp.JSONPath) -> str: 2543 path = self.expressions(expression, sep="", flat=True).lstrip(".") 2544 return f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}" 2545 2546 def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str: 2547 if isinstance(expression, exp.JSONPathPart): 2548 transform = self.TRANSFORMS.get(expression.__class__) 2549 if not callable(transform): 2550 self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}") 2551 return "" 2552 2553 return transform(self, expression) 2554 2555 if isinstance(expression, int): 2556 return str(expression) 2557 2558 if self.JSON_PATH_SINGLE_QUOTE_ESCAPE: 2559 escaped = expression.replace("'", "\\'") 2560 escaped = f"\\'{expression}\\'" 2561 else: 2562 escaped = expression.replace('"', '\\"') 2563 escaped = f'"{escaped}"' 2564 2565 return escaped 2566 2567 def formatjson_sql(self, expression: exp.FormatJson) -> str: 2568 return f"{self.sql(expression, 'this')} FORMAT JSON" 2569 2570 def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str: 2571 null_handling = expression.args.get("null_handling") 2572 null_handling = f" {null_handling}" if null_handling else "" 2573 2574 unique_keys = expression.args.get("unique_keys") 2575 if unique_keys is not None: 2576 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2577 else: 2578 unique_keys = "" 2579 2580 return_type = self.sql(expression, "return_type") 2581 return_type = f" RETURNING {return_type}" if return_type else "" 2582 encoding = self.sql(expression, "encoding") 2583 encoding = f" ENCODING {encoding}" if encoding else "" 2584 2585 return self.func( 2586 "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG", 2587 *expression.expressions, 2588 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2589 ) 2590 2591 def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str: 2592 return self.jsonobject_sql(expression) 2593 2594 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2595 null_handling = expression.args.get("null_handling") 2596 null_handling = f" {null_handling}" if null_handling else "" 2597 return_type = self.sql(expression, "return_type") 2598 return_type = f" RETURNING {return_type}" if return_type else "" 2599 strict = " STRICT" if expression.args.get("strict") else "" 2600 return self.func( 2601 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2602 ) 2603 2604 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2605 this = self.sql(expression, "this") 2606 order = self.sql(expression, "order") 2607 null_handling = expression.args.get("null_handling") 2608 null_handling = f" {null_handling}" if null_handling else "" 2609 return_type = self.sql(expression, "return_type") 2610 return_type = f" RETURNING {return_type}" if return_type else "" 2611 strict = " STRICT" if expression.args.get("strict") else "" 2612 return self.func( 2613 "JSON_ARRAYAGG", 2614 this, 2615 suffix=f"{order}{null_handling}{return_type}{strict})", 2616 ) 2617 2618 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2619 path = self.sql(expression, "path") 2620 path = f" PATH {path}" if path else "" 2621 nested_schema = self.sql(expression, "nested_schema") 2622 2623 if nested_schema: 2624 return f"NESTED{path} {nested_schema}" 2625 2626 this = self.sql(expression, "this") 2627 kind = self.sql(expression, "kind") 2628 kind = f" {kind}" if kind else "" 2629 return f"{this}{kind}{path}" 2630 2631 def jsonschema_sql(self, expression: exp.JSONSchema) -> str: 2632 return self.func("COLUMNS", *expression.expressions) 2633 2634 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2635 this = self.sql(expression, "this") 2636 path = self.sql(expression, "path") 2637 path = f", {path}" if path else "" 2638 error_handling = expression.args.get("error_handling") 2639 error_handling = f" {error_handling}" if error_handling else "" 2640 empty_handling = expression.args.get("empty_handling") 2641 empty_handling = f" {empty_handling}" if empty_handling else "" 2642 schema = self.sql(expression, "schema") 2643 return self.func( 2644 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2645 ) 2646 2647 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2648 this = self.sql(expression, "this") 2649 kind = self.sql(expression, "kind") 2650 path = self.sql(expression, "path") 2651 path = f" {path}" if path else "" 2652 as_json = " AS JSON" if expression.args.get("as_json") else "" 2653 return f"{this} {kind}{path}{as_json}" 2654 2655 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2656 this = self.sql(expression, "this") 2657 path = self.sql(expression, "path") 2658 path = f", {path}" if path else "" 2659 expressions = self.expressions(expression) 2660 with_ = ( 2661 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2662 if expressions 2663 else "" 2664 ) 2665 return f"OPENJSON({this}{path}){with_}" 2666 2667 def in_sql(self, expression: exp.In) -> str: 2668 query = expression.args.get("query") 2669 unnest = expression.args.get("unnest") 2670 field = expression.args.get("field") 2671 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2672 2673 if query: 2674 in_sql = self.wrap(self.sql(query)) 2675 elif unnest: 2676 in_sql = self.in_unnest_op(unnest) 2677 elif field: 2678 in_sql = self.sql(field) 2679 else: 2680 in_sql = f"({self.expressions(expression, flat=True)})" 2681 2682 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}" 2683 2684 def in_unnest_op(self, unnest: exp.Unnest) -> str: 2685 return f"(SELECT {self.sql(unnest)})" 2686 2687 def interval_sql(self, expression: exp.Interval) -> str: 2688 unit = self.sql(expression, "unit") 2689 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2690 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2691 unit = f" {unit}" if unit else "" 2692 2693 if self.SINGLE_STRING_INTERVAL: 2694 this = expression.this.name if expression.this else "" 2695 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2696 2697 this = self.sql(expression, "this") 2698 if this: 2699 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2700 this = f" {this}" if unwrapped else f" ({this})" 2701 2702 return f"INTERVAL{this}{unit}" 2703 2704 def return_sql(self, expression: exp.Return) -> str: 2705 return f"RETURN {self.sql(expression, 'this')}" 2706 2707 def reference_sql(self, expression: exp.Reference) -> str: 2708 this = self.sql(expression, "this") 2709 expressions = self.expressions(expression, flat=True) 2710 expressions = f"({expressions})" if expressions else "" 2711 options = self.expressions(expression, key="options", flat=True, sep=" ") 2712 options = f" {options}" if options else "" 2713 return f"REFERENCES {this}{expressions}{options}" 2714 2715 def anonymous_sql(self, expression: exp.Anonymous) -> str: 2716 return self.func(self.sql(expression, "this"), *expression.expressions) 2717 2718 def paren_sql(self, expression: exp.Paren) -> str: 2719 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2720 return f"({sql}{self.seg(')', sep='')}" 2721 2722 def neg_sql(self, expression: exp.Neg) -> str: 2723 # This makes sure we don't convert "- - 5" to "--5", which is a comment 2724 this_sql = self.sql(expression, "this") 2725 sep = " " if this_sql[0] == "-" else "" 2726 return f"-{sep}{this_sql}" 2727 2728 def not_sql(self, expression: exp.Not) -> str: 2729 return f"NOT {self.sql(expression, 'this')}" 2730 2731 def alias_sql(self, expression: exp.Alias) -> str: 2732 alias = self.sql(expression, "alias") 2733 alias = f" AS {alias}" if alias else "" 2734 return f"{self.sql(expression, 'this')}{alias}" 2735 2736 def pivotalias_sql(self, expression: exp.PivotAlias) -> str: 2737 alias = expression.args["alias"] 2738 identifier_alias = isinstance(alias, exp.Identifier) 2739 2740 if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2741 alias.replace(exp.Literal.string(alias.output_name)) 2742 elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2743 alias.replace(exp.to_identifier(alias.output_name)) 2744 2745 return self.alias_sql(expression) 2746 2747 def aliases_sql(self, expression: exp.Aliases) -> str: 2748 return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})" 2749 2750 def atindex_sql(self, expression: exp.AtTimeZone) -> str: 2751 this = self.sql(expression, "this") 2752 index = self.sql(expression, "expression") 2753 return f"{this} AT {index}" 2754 2755 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 2756 this = self.sql(expression, "this") 2757 zone = self.sql(expression, "zone") 2758 return f"{this} AT TIME ZONE {zone}" 2759 2760 def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str: 2761 this = self.sql(expression, "this") 2762 zone = self.sql(expression, "zone") 2763 return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'" 2764 2765 def add_sql(self, expression: exp.Add) -> str: 2766 return self.binary(expression, "+") 2767 2768 def and_sql( 2769 self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None 2770 ) -> str: 2771 return self.connector_sql(expression, "AND", stack) 2772 2773 def or_sql( 2774 self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None 2775 ) -> str: 2776 return self.connector_sql(expression, "OR", stack) 2777 2778 def xor_sql( 2779 self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None 2780 ) -> str: 2781 return self.connector_sql(expression, "XOR", stack) 2782 2783 def connector_sql( 2784 self, 2785 expression: exp.Connector, 2786 op: str, 2787 stack: t.Optional[t.List[str | exp.Expression]] = None, 2788 ) -> str: 2789 if stack is not None: 2790 if expression.expressions: 2791 stack.append(self.expressions(expression, sep=f" {op} ")) 2792 else: 2793 stack.append(expression.right) 2794 if expression.comments: 2795 for comment in expression.comments: 2796 op += f" /*{self.pad_comment(comment)}*/" 2797 stack.extend((op, expression.left)) 2798 return op 2799 2800 stack = [expression] 2801 sqls: t.List[str] = [] 2802 ops = set() 2803 2804 while stack: 2805 node = stack.pop() 2806 if isinstance(node, exp.Connector): 2807 ops.add(getattr(self, f"{node.key}_sql")(node, stack)) 2808 else: 2809 sql = self.sql(node) 2810 if sqls and sqls[-1] in ops: 2811 sqls[-1] += f" {sql}" 2812 else: 2813 sqls.append(sql) 2814 2815 sep = "\n" if self.pretty and self.text_width(sqls) > self.max_text_width else " " 2816 return sep.join(sqls) 2817 2818 def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str: 2819 return self.binary(expression, "&") 2820 2821 def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str: 2822 return self.binary(expression, "<<") 2823 2824 def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str: 2825 return f"~{self.sql(expression, 'this')}" 2826 2827 def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str: 2828 return self.binary(expression, "|") 2829 2830 def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str: 2831 return self.binary(expression, ">>") 2832 2833 def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str: 2834 return self.binary(expression, "^") 2835 2836 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2837 format_sql = self.sql(expression, "format") 2838 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2839 to_sql = self.sql(expression, "to") 2840 to_sql = f" {to_sql}" if to_sql else "" 2841 action = self.sql(expression, "action") 2842 action = f" {action}" if action else "" 2843 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql}{action})" 2844 2845 def currentdate_sql(self, expression: exp.CurrentDate) -> str: 2846 zone = self.sql(expression, "this") 2847 return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE" 2848 2849 def currenttimestamp_sql(self, expression: exp.CurrentTimestamp) -> str: 2850 return self.func("CURRENT_TIMESTAMP", expression.this) 2851 2852 def collate_sql(self, expression: exp.Collate) -> str: 2853 if self.COLLATE_IS_FUNC: 2854 return self.function_fallback_sql(expression) 2855 return self.binary(expression, "COLLATE") 2856 2857 def command_sql(self, expression: exp.Command) -> str: 2858 return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}" 2859 2860 def comment_sql(self, expression: exp.Comment) -> str: 2861 this = self.sql(expression, "this") 2862 kind = expression.args["kind"] 2863 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2864 expression_sql = self.sql(expression, "expression") 2865 return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}" 2866 2867 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2868 this = self.sql(expression, "this") 2869 delete = " DELETE" if expression.args.get("delete") else "" 2870 recompress = self.sql(expression, "recompress") 2871 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2872 to_disk = self.sql(expression, "to_disk") 2873 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2874 to_volume = self.sql(expression, "to_volume") 2875 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2876 return f"{this}{delete}{recompress}{to_disk}{to_volume}" 2877 2878 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2879 where = self.sql(expression, "where") 2880 group = self.sql(expression, "group") 2881 aggregates = self.expressions(expression, key="aggregates") 2882 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2883 2884 if not (where or group or aggregates) and len(expression.expressions) == 1: 2885 return f"TTL {self.expressions(expression, flat=True)}" 2886 2887 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}" 2888 2889 def transaction_sql(self, expression: exp.Transaction) -> str: 2890 return "BEGIN" 2891 2892 def commit_sql(self, expression: exp.Commit) -> str: 2893 chain = expression.args.get("chain") 2894 if chain is not None: 2895 chain = " AND CHAIN" if chain else " AND NO CHAIN" 2896 2897 return f"COMMIT{chain or ''}" 2898 2899 def rollback_sql(self, expression: exp.Rollback) -> str: 2900 savepoint = expression.args.get("savepoint") 2901 savepoint = f" TO {savepoint}" if savepoint else "" 2902 return f"ROLLBACK{savepoint}" 2903 2904 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2905 this = self.sql(expression, "this") 2906 2907 dtype = self.sql(expression, "dtype") 2908 if dtype: 2909 collate = self.sql(expression, "collate") 2910 collate = f" COLLATE {collate}" if collate else "" 2911 using = self.sql(expression, "using") 2912 using = f" USING {using}" if using else "" 2913 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2914 2915 default = self.sql(expression, "default") 2916 if default: 2917 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2918 2919 comment = self.sql(expression, "comment") 2920 if comment: 2921 return f"ALTER COLUMN {this} COMMENT {comment}" 2922 2923 if not expression.args.get("drop"): 2924 self.unsupported("Unsupported ALTER COLUMN syntax") 2925 2926 return f"ALTER COLUMN {this} DROP DEFAULT" 2927 2928 def renametable_sql(self, expression: exp.RenameTable) -> str: 2929 if not self.RENAME_TABLE_WITH_DB: 2930 # Remove db from tables 2931 expression = expression.transform( 2932 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 2933 ).assert_is(exp.RenameTable) 2934 this = self.sql(expression, "this") 2935 return f"RENAME TO {this}" 2936 2937 def renamecolumn_sql(self, expression: exp.RenameColumn) -> str: 2938 exists = " IF EXISTS" if expression.args.get("exists") else "" 2939 old_column = self.sql(expression, "this") 2940 new_column = self.sql(expression, "to") 2941 return f"RENAME COLUMN{exists} {old_column} TO {new_column}" 2942 2943 def altertable_sql(self, expression: exp.AlterTable) -> str: 2944 actions = expression.args["actions"] 2945 2946 if isinstance(actions[0], exp.ColumnDef): 2947 actions = self.add_column_sql(expression) 2948 elif isinstance(actions[0], exp.Schema): 2949 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 2950 elif isinstance(actions[0], exp.Delete): 2951 actions = self.expressions(expression, key="actions", flat=True) 2952 else: 2953 actions = self.expressions(expression, key="actions", flat=True) 2954 2955 exists = " IF EXISTS" if expression.args.get("exists") else "" 2956 only = " ONLY" if expression.args.get("only") else "" 2957 options = self.expressions(expression, key="options") 2958 options = f", {options}" if options else "" 2959 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}" 2960 2961 def add_column_sql(self, expression: exp.AlterTable) -> str: 2962 if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD: 2963 return self.expressions( 2964 expression, 2965 key="actions", 2966 prefix="ADD COLUMN ", 2967 ) 2968 return f"ADD {self.expressions(expression, key='actions', flat=True)}" 2969 2970 def droppartition_sql(self, expression: exp.DropPartition) -> str: 2971 expressions = self.expressions(expression) 2972 exists = " IF EXISTS " if expression.args.get("exists") else " " 2973 return f"DROP{exists}{expressions}" 2974 2975 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 2976 return f"ADD {self.expressions(expression)}" 2977 2978 def distinct_sql(self, expression: exp.Distinct) -> str: 2979 this = self.expressions(expression, flat=True) 2980 2981 if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1: 2982 case = exp.case() 2983 for arg in expression.expressions: 2984 case = case.when(arg.is_(exp.null()), exp.null()) 2985 this = self.sql(case.else_(f"({this})")) 2986 2987 this = f" {this}" if this else "" 2988 2989 on = self.sql(expression, "on") 2990 on = f" ON {on}" if on else "" 2991 return f"DISTINCT{this}{on}" 2992 2993 def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: 2994 return self._embed_ignore_nulls(expression, "IGNORE NULLS") 2995 2996 def respectnulls_sql(self, expression: exp.RespectNulls) -> str: 2997 return self._embed_ignore_nulls(expression, "RESPECT NULLS") 2998 2999 def havingmax_sql(self, expression: exp.HavingMax) -> str: 3000 this_sql = self.sql(expression, "this") 3001 expression_sql = self.sql(expression, "expression") 3002 kind = "MAX" if expression.args.get("max") else "MIN" 3003 return f"{this_sql} HAVING {kind} {expression_sql}" 3004 3005 def intdiv_sql(self, expression: exp.IntDiv) -> str: 3006 return self.sql( 3007 exp.Cast( 3008 this=exp.Div(this=expression.this, expression=expression.expression), 3009 to=exp.DataType(this=exp.DataType.Type.INT), 3010 ) 3011 ) 3012 3013 def dpipe_sql(self, expression: exp.DPipe) -> str: 3014 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 3015 return self.func("CONCAT", *(exp.cast(e, "text") for e in expression.flatten())) 3016 return self.binary(expression, "||") 3017 3018 def div_sql(self, expression: exp.Div) -> str: 3019 l, r = expression.left, expression.right 3020 3021 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 3022 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 3023 3024 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 3025 if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES): 3026 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 3027 3028 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 3029 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 3030 return self.sql( 3031 exp.cast( 3032 l / r, 3033 to=exp.DataType.Type.BIGINT, 3034 ) 3035 ) 3036 3037 return self.binary(expression, "/") 3038 3039 def overlaps_sql(self, expression: exp.Overlaps) -> str: 3040 return self.binary(expression, "OVERLAPS") 3041 3042 def distance_sql(self, expression: exp.Distance) -> str: 3043 return self.binary(expression, "<->") 3044 3045 def dot_sql(self, expression: exp.Dot) -> str: 3046 return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}" 3047 3048 def eq_sql(self, expression: exp.EQ) -> str: 3049 return self.binary(expression, "=") 3050 3051 def propertyeq_sql(self, expression: exp.PropertyEQ) -> str: 3052 return self.binary(expression, ":=") 3053 3054 def escape_sql(self, expression: exp.Escape) -> str: 3055 return self.binary(expression, "ESCAPE") 3056 3057 def glob_sql(self, expression: exp.Glob) -> str: 3058 return self.binary(expression, "GLOB") 3059 3060 def gt_sql(self, expression: exp.GT) -> str: 3061 return self.binary(expression, ">") 3062 3063 def gte_sql(self, expression: exp.GTE) -> str: 3064 return self.binary(expression, ">=") 3065 3066 def ilike_sql(self, expression: exp.ILike) -> str: 3067 return self.binary(expression, "ILIKE") 3068 3069 def ilikeany_sql(self, expression: exp.ILikeAny) -> str: 3070 return self.binary(expression, "ILIKE ANY") 3071 3072 def is_sql(self, expression: exp.Is) -> str: 3073 if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean): 3074 return self.sql( 3075 expression.this if expression.expression.this else exp.not_(expression.this) 3076 ) 3077 return self.binary(expression, "IS") 3078 3079 def like_sql(self, expression: exp.Like) -> str: 3080 return self.binary(expression, "LIKE") 3081 3082 def likeany_sql(self, expression: exp.LikeAny) -> str: 3083 return self.binary(expression, "LIKE ANY") 3084 3085 def similarto_sql(self, expression: exp.SimilarTo) -> str: 3086 return self.binary(expression, "SIMILAR TO") 3087 3088 def lt_sql(self, expression: exp.LT) -> str: 3089 return self.binary(expression, "<") 3090 3091 def lte_sql(self, expression: exp.LTE) -> str: 3092 return self.binary(expression, "<=") 3093 3094 def mod_sql(self, expression: exp.Mod) -> str: 3095 return self.binary(expression, "%") 3096 3097 def mul_sql(self, expression: exp.Mul) -> str: 3098 return self.binary(expression, "*") 3099 3100 def neq_sql(self, expression: exp.NEQ) -> str: 3101 return self.binary(expression, "<>") 3102 3103 def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str: 3104 return self.binary(expression, "IS NOT DISTINCT FROM") 3105 3106 def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str: 3107 return self.binary(expression, "IS DISTINCT FROM") 3108 3109 def slice_sql(self, expression: exp.Slice) -> str: 3110 return self.binary(expression, ":") 3111 3112 def sub_sql(self, expression: exp.Sub) -> str: 3113 return self.binary(expression, "-") 3114 3115 def trycast_sql(self, expression: exp.TryCast) -> str: 3116 return self.cast_sql(expression, safe_prefix="TRY_") 3117 3118 def log_sql(self, expression: exp.Log) -> str: 3119 this = expression.this 3120 expr = expression.expression 3121 3122 if self.dialect.LOG_BASE_FIRST is False: 3123 this, expr = expr, this 3124 elif self.dialect.LOG_BASE_FIRST is None and expr: 3125 if this.name in ("2", "10"): 3126 return self.func(f"LOG{this.name}", expr) 3127 3128 self.unsupported(f"Unsupported logarithm with base {self.sql(this)}") 3129 3130 return self.func("LOG", this, expr) 3131 3132 def use_sql(self, expression: exp.Use) -> str: 3133 kind = self.sql(expression, "kind") 3134 kind = f" {kind}" if kind else "" 3135 this = self.sql(expression, "this") 3136 this = f" {this}" if this else "" 3137 return f"USE{kind}{this}" 3138 3139 def binary(self, expression: exp.Binary, op: str) -> str: 3140 op = self.maybe_comment(op, comments=expression.comments) 3141 return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}" 3142 3143 def function_fallback_sql(self, expression: exp.Func) -> str: 3144 args = [] 3145 3146 for key in expression.arg_types: 3147 arg_value = expression.args.get(key) 3148 3149 if isinstance(arg_value, list): 3150 for value in arg_value: 3151 args.append(value) 3152 elif arg_value is not None: 3153 args.append(arg_value) 3154 3155 if self.normalize_functions: 3156 name = expression.sql_name() 3157 else: 3158 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 3159 3160 return self.func(name, *args) 3161 3162 def func( 3163 self, 3164 name: str, 3165 *args: t.Optional[exp.Expression | str], 3166 prefix: str = "(", 3167 suffix: str = ")", 3168 ) -> str: 3169 return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}" 3170 3171 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 3172 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 3173 if self.pretty and self.text_width(arg_sqls) > self.max_text_width: 3174 return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 3175 return ", ".join(arg_sqls) 3176 3177 def text_width(self, args: t.Iterable) -> int: 3178 return sum(len(arg) for arg in args) 3179 3180 def format_time( 3181 self, 3182 expression: exp.Expression, 3183 inverse_time_mapping: t.Optional[t.Dict[str, str]] = None, 3184 inverse_time_trie: t.Optional[t.Dict] = None, 3185 ) -> t.Optional[str]: 3186 return format_time( 3187 self.sql(expression, "format"), 3188 inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING, 3189 inverse_time_trie or self.dialect.INVERSE_TIME_TRIE, 3190 ) 3191 3192 def expressions( 3193 self, 3194 expression: t.Optional[exp.Expression] = None, 3195 key: t.Optional[str] = None, 3196 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 3197 flat: bool = False, 3198 indent: bool = True, 3199 skip_first: bool = False, 3200 sep: str = ", ", 3201 prefix: str = "", 3202 ) -> str: 3203 expressions = expression.args.get(key or "expressions") if expression else sqls 3204 3205 if not expressions: 3206 return "" 3207 3208 if flat: 3209 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 3210 3211 num_sqls = len(expressions) 3212 3213 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 3214 if self.pretty: 3215 if self.leading_comma: 3216 pad = " " * len(sep) 3217 else: 3218 stripped_sep = sep.strip() 3219 3220 result_sqls = [] 3221 for i, e in enumerate(expressions): 3222 sql = self.sql(e, comment=False) 3223 if not sql: 3224 continue 3225 3226 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 3227 3228 if self.pretty: 3229 if self.leading_comma: 3230 result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}") 3231 else: 3232 result_sqls.append( 3233 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 3234 ) 3235 else: 3236 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 3237 3238 result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls) 3239 return self.indent(result_sql, skip_first=skip_first) if indent else result_sql 3240 3241 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 3242 flat = flat or isinstance(expression.parent, exp.Properties) 3243 expressions_sql = self.expressions(expression, flat=flat) 3244 if flat: 3245 return f"{op} {expressions_sql}" 3246 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}" 3247 3248 def naked_property(self, expression: exp.Property) -> str: 3249 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 3250 if not property_name: 3251 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 3252 return f"{property_name} {self.sql(expression, 'this')}" 3253 3254 def tag_sql(self, expression: exp.Tag) -> str: 3255 return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}" 3256 3257 def token_sql(self, token_type: TokenType) -> str: 3258 return self.TOKEN_MAPPING.get(token_type, token_type.name) 3259 3260 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 3261 this = self.sql(expression, "this") 3262 expressions = self.no_identify(self.expressions, expression) 3263 expressions = ( 3264 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 3265 ) 3266 return f"{this}{expressions}" 3267 3268 def joinhint_sql(self, expression: exp.JoinHint) -> str: 3269 this = self.sql(expression, "this") 3270 expressions = self.expressions(expression, flat=True) 3271 return f"{this}({expressions})" 3272 3273 def kwarg_sql(self, expression: exp.Kwarg) -> str: 3274 return self.binary(expression, "=>") 3275 3276 def when_sql(self, expression: exp.When) -> str: 3277 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 3278 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 3279 condition = self.sql(expression, "condition") 3280 condition = f" AND {condition}" if condition else "" 3281 3282 then_expression = expression.args.get("then") 3283 if isinstance(then_expression, exp.Insert): 3284 then = f"INSERT {self.sql(then_expression, 'this')}" 3285 if "expression" in then_expression.args: 3286 then += f" VALUES {self.sql(then_expression, 'expression')}" 3287 elif isinstance(then_expression, exp.Update): 3288 if isinstance(then_expression.args.get("expressions"), exp.Star): 3289 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 3290 else: 3291 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 3292 else: 3293 then = self.sql(then_expression) 3294 return f"WHEN {matched}{source}{condition} THEN {then}" 3295 3296 def merge_sql(self, expression: exp.Merge) -> str: 3297 table = expression.this 3298 table_alias = "" 3299 3300 hints = table.args.get("hints") 3301 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 3302 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 3303 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 3304 3305 this = self.sql(table) 3306 using = f"USING {self.sql(expression, 'using')}" 3307 on = f"ON {self.sql(expression, 'on')}" 3308 expressions = self.expressions(expression, sep=" ") 3309 3310 return self.prepend_ctes( 3311 expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}" 3312 ) 3313 3314 def tochar_sql(self, expression: exp.ToChar) -> str: 3315 if expression.args.get("format"): 3316 self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function") 3317 3318 return self.sql(exp.cast(expression.this, "text")) 3319 3320 def tonumber_sql(self, expression: exp.ToNumber) -> str: 3321 if not self.SUPPORTS_TO_NUMBER: 3322 self.unsupported("Unsupported TO_NUMBER function") 3323 return self.sql(exp.cast(expression.this, "double")) 3324 3325 fmt = expression.args.get("format") 3326 if not fmt: 3327 self.unsupported("Conversion format is required for TO_NUMBER") 3328 return self.sql(exp.cast(expression.this, "double")) 3329 3330 return self.func("TO_NUMBER", expression.this, fmt) 3331 3332 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 3333 this = self.sql(expression, "this") 3334 kind = self.sql(expression, "kind") 3335 settings_sql = self.expressions(expression, key="settings", sep=" ") 3336 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 3337 return f"{this}({kind}{args})" 3338 3339 def dictrange_sql(self, expression: exp.DictRange) -> str: 3340 this = self.sql(expression, "this") 3341 max = self.sql(expression, "max") 3342 min = self.sql(expression, "min") 3343 return f"{this}(MIN {min} MAX {max})" 3344 3345 def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str: 3346 return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}" 3347 3348 def oncluster_sql(self, expression: exp.OnCluster) -> str: 3349 return "" 3350 3351 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3352 expressions = self.expressions(expression, key="expressions", flat=True) 3353 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3354 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3355 buckets = self.sql(expression, "buckets") 3356 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS" 3357 3358 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3359 this = self.sql(expression, "this") 3360 having = self.sql(expression, "having") 3361 3362 if having: 3363 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3364 3365 return self.func("ANY_VALUE", this) 3366 3367 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3368 transform = self.func("TRANSFORM", *expression.expressions) 3369 row_format_before = self.sql(expression, "row_format_before") 3370 row_format_before = f" {row_format_before}" if row_format_before else "" 3371 record_writer = self.sql(expression, "record_writer") 3372 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3373 using = f" USING {self.sql(expression, 'command_script')}" 3374 schema = self.sql(expression, "schema") 3375 schema = f" AS {schema}" if schema else "" 3376 row_format_after = self.sql(expression, "row_format_after") 3377 row_format_after = f" {row_format_after}" if row_format_after else "" 3378 record_reader = self.sql(expression, "record_reader") 3379 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3380 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}" 3381 3382 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3383 key_block_size = self.sql(expression, "key_block_size") 3384 if key_block_size: 3385 return f"KEY_BLOCK_SIZE = {key_block_size}" 3386 3387 using = self.sql(expression, "using") 3388 if using: 3389 return f"USING {using}" 3390 3391 parser = self.sql(expression, "parser") 3392 if parser: 3393 return f"WITH PARSER {parser}" 3394 3395 comment = self.sql(expression, "comment") 3396 if comment: 3397 return f"COMMENT {comment}" 3398 3399 visible = expression.args.get("visible") 3400 if visible is not None: 3401 return "VISIBLE" if visible else "INVISIBLE" 3402 3403 engine_attr = self.sql(expression, "engine_attr") 3404 if engine_attr: 3405 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3406 3407 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3408 if secondary_engine_attr: 3409 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3410 3411 self.unsupported("Unsupported index constraint option.") 3412 return "" 3413 3414 def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str: 3415 enforced = " ENFORCED" if expression.args.get("enforced") else "" 3416 return f"CHECK ({self.sql(expression, 'this')}){enforced}" 3417 3418 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3419 kind = self.sql(expression, "kind") 3420 kind = f"{kind} INDEX" if kind else "INDEX" 3421 this = self.sql(expression, "this") 3422 this = f" {this}" if this else "" 3423 index_type = self.sql(expression, "index_type") 3424 index_type = f" USING {index_type}" if index_type else "" 3425 schema = self.sql(expression, "schema") 3426 schema = f" {schema}" if schema else "" 3427 options = self.expressions(expression, key="options", sep=" ") 3428 options = f" {options}" if options else "" 3429 return f"{kind}{this}{index_type}{schema}{options}" 3430 3431 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3432 if self.NVL2_SUPPORTED: 3433 return self.function_fallback_sql(expression) 3434 3435 case = exp.Case().when( 3436 expression.this.is_(exp.null()).not_(copy=False), 3437 expression.args["true"], 3438 copy=False, 3439 ) 3440 else_cond = expression.args.get("false") 3441 if else_cond: 3442 case.else_(else_cond, copy=False) 3443 3444 return self.sql(case) 3445 3446 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3447 this = self.sql(expression, "this") 3448 expr = self.sql(expression, "expression") 3449 iterator = self.sql(expression, "iterator") 3450 condition = self.sql(expression, "condition") 3451 condition = f" IF {condition}" if condition else "" 3452 return f"{this} FOR {expr} IN {iterator}{condition}" 3453 3454 def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str: 3455 return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})" 3456 3457 def opclass_sql(self, expression: exp.Opclass) -> str: 3458 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 3459 3460 def predict_sql(self, expression: exp.Predict) -> str: 3461 model = self.sql(expression, "this") 3462 model = f"MODEL {model}" 3463 table = self.sql(expression, "expression") 3464 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3465 parameters = self.sql(expression, "params_struct") 3466 return self.func("PREDICT", model, table, parameters or None) 3467 3468 def forin_sql(self, expression: exp.ForIn) -> str: 3469 this = self.sql(expression, "this") 3470 expression_sql = self.sql(expression, "expression") 3471 return f"FOR {this} DO {expression_sql}" 3472 3473 def refresh_sql(self, expression: exp.Refresh) -> str: 3474 this = self.sql(expression, "this") 3475 table = "" if isinstance(expression.this, exp.Literal) else "TABLE " 3476 return f"REFRESH {table}{this}" 3477 3478 def operator_sql(self, expression: exp.Operator) -> str: 3479 return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})") 3480 3481 def toarray_sql(self, expression: exp.ToArray) -> str: 3482 arg = expression.this 3483 if not arg.type: 3484 from sqlglot.optimizer.annotate_types import annotate_types 3485 3486 arg = annotate_types(arg) 3487 3488 if arg.is_type(exp.DataType.Type.ARRAY): 3489 return self.sql(arg) 3490 3491 cond_for_null = arg.is_(exp.null()) 3492 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False))) 3493 3494 def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str: 3495 this = expression.this 3496 if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME): 3497 return self.sql(this) 3498 3499 return self.sql(exp.cast(this, "time")) 3500 3501 def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str: 3502 this = expression.this 3503 if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP): 3504 return self.sql(this) 3505 3506 return self.sql(exp.cast(this, "timestamp")) 3507 3508 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3509 this = expression.this 3510 time_format = self.format_time(expression) 3511 3512 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3513 return self.sql( 3514 exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date") 3515 ) 3516 3517 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3518 return self.sql(this) 3519 3520 return self.sql(exp.cast(this, "date")) 3521 3522 def unixdate_sql(self, expression: exp.UnixDate) -> str: 3523 return self.sql( 3524 exp.func( 3525 "DATEDIFF", 3526 expression.this, 3527 exp.cast(exp.Literal.string("1970-01-01"), "date"), 3528 "day", 3529 ) 3530 ) 3531 3532 def lastday_sql(self, expression: exp.LastDay) -> str: 3533 if self.LAST_DAY_SUPPORTS_DATE_PART: 3534 return self.function_fallback_sql(expression) 3535 3536 unit = expression.text("unit") 3537 if unit and unit != "MONTH": 3538 self.unsupported("Date parts are not supported in LAST_DAY.") 3539 3540 return self.func("LAST_DAY", expression.this) 3541 3542 def dateadd_sql(self, expression: exp.DateAdd) -> str: 3543 from sqlglot.dialects.dialect import unit_to_str 3544 3545 return self.func( 3546 "DATE_ADD", expression.this, expression.expression, unit_to_str(expression) 3547 ) 3548 3549 def arrayany_sql(self, expression: exp.ArrayAny) -> str: 3550 if self.CAN_IMPLEMENT_ARRAY_ANY: 3551 filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression) 3552 filtered_not_empty = exp.ArraySize(this=filtered).neq(0) 3553 original_is_empty = exp.ArraySize(this=expression.this).eq(0) 3554 return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty))) 3555 3556 from sqlglot.dialects import Dialect 3557 3558 # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect 3559 if self.dialect.__class__ != Dialect: 3560 self.unsupported("ARRAY_ANY is unsupported") 3561 3562 return self.function_fallback_sql(expression) 3563 3564 def generateseries_sql(self, expression: exp.GenerateSeries) -> str: 3565 expression.set("is_end_exclusive", None) 3566 return self.function_fallback_sql(expression) 3567 3568 def struct_sql(self, expression: exp.Struct) -> str: 3569 expression.set( 3570 "expressions", 3571 [ 3572 exp.alias_(e.expression, e.name if e.this.is_string else e.this) 3573 if isinstance(e, exp.PropertyEQ) 3574 else e 3575 for e in expression.expressions 3576 ], 3577 ) 3578 3579 return self.function_fallback_sql(expression) 3580 3581 def partitionrange_sql(self, expression: exp.PartitionRange) -> str: 3582 low = self.sql(expression, "this") 3583 high = self.sql(expression, "expression") 3584 3585 return f"{low} TO {high}" 3586 3587 def truncatetable_sql(self, expression: exp.TruncateTable) -> str: 3588 target = "DATABASE" if expression.args.get("is_database") else "TABLE" 3589 tables = f" {self.expressions(expression)}" 3590 3591 exists = " IF EXISTS" if expression.args.get("exists") else "" 3592 3593 on_cluster = self.sql(expression, "cluster") 3594 on_cluster = f" {on_cluster}" if on_cluster else "" 3595 3596 identity = self.sql(expression, "identity") 3597 identity = f" {identity} IDENTITY" if identity else "" 3598 3599 option = self.sql(expression, "option") 3600 option = f" {option}" if option else "" 3601 3602 partition = self.sql(expression, "partition") 3603 partition = f" {partition}" if partition else "" 3604 3605 return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}" 3606 3607 # This transpiles T-SQL's CONVERT function 3608 # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16 3609 def convert_sql(self, expression: exp.Convert) -> str: 3610 to = expression.this 3611 value = expression.expression 3612 style = expression.args.get("style") 3613 safe = expression.args.get("safe") 3614 strict = expression.args.get("strict") 3615 3616 if not to or not value: 3617 return "" 3618 3619 # Retrieve length of datatype and override to default if not specified 3620 if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3621 to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False) 3622 3623 transformed: t.Optional[exp.Expression] = None 3624 cast = exp.Cast if strict else exp.TryCast 3625 3626 # Check whether a conversion with format (T-SQL calls this 'style') is applicable 3627 if isinstance(style, exp.Literal) and style.is_int: 3628 from sqlglot.dialects.tsql import TSQL 3629 3630 style_value = style.name 3631 converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value) 3632 if not converted_style: 3633 self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}") 3634 3635 fmt = exp.Literal.string(converted_style) 3636 3637 if to.this == exp.DataType.Type.DATE: 3638 transformed = exp.StrToDate(this=value, format=fmt) 3639 elif to.this == exp.DataType.Type.DATETIME: 3640 transformed = exp.StrToTime(this=value, format=fmt) 3641 elif to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3642 transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe) 3643 elif to.this == exp.DataType.Type.TEXT: 3644 transformed = exp.TimeToStr(this=value, format=fmt) 3645 3646 if not transformed: 3647 transformed = cast(this=value, to=to, safe=safe) 3648 3649 return self.sql(transformed) 3650 3651 def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str: 3652 this = expression.this 3653 if isinstance(this, exp.JSONPathWildcard): 3654 this = self.json_path_part(this) 3655 return f".{this}" if this else "" 3656 3657 if exp.SAFE_IDENTIFIER_RE.match(this): 3658 return f".{this}" 3659 3660 this = self.json_path_part(this) 3661 return f"[{this}]" if self.JSON_PATH_BRACKETED_KEY_SUPPORTED else f".{this}" 3662 3663 def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str: 3664 this = self.json_path_part(expression.this) 3665 return f"[{this}]" if this else "" 3666 3667 def _simplify_unless_literal(self, expression: E) -> E: 3668 if not isinstance(expression, exp.Literal): 3669 from sqlglot.optimizer.simplify import simplify 3670 3671 expression = simplify(expression, dialect=self.dialect) 3672 3673 return expression 3674 3675 def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str: 3676 if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"): 3677 # The first modifier here will be the one closest to the AggFunc's arg 3678 mods = sorted( 3679 expression.find_all(exp.HavingMax, exp.Order, exp.Limit), 3680 key=lambda x: 0 3681 if isinstance(x, exp.HavingMax) 3682 else (1 if isinstance(x, exp.Order) else 2), 3683 ) 3684 3685 if mods: 3686 mod = mods[0] 3687 this = expression.__class__(this=mod.this.copy()) 3688 this.meta["inline"] = True 3689 mod.this.replace(this) 3690 return self.sql(expression.this) 3691 3692 agg_func = expression.find(exp.AggFunc) 3693 3694 if agg_func: 3695 return self.sql(agg_func)[:-1] + f" {text})" 3696 3697 return f"{self.sql(expression, 'this')} {text}"
Generator converts a given syntax tree to the corresponding SQL string.
Arguments:
- pretty: Whether to format the produced SQL string. Default: False.
- identify: Determines when an identifier should be quoted. Possible values are: False (default): Never quote, except in cases where it's mandatory by the dialect. True or 'always': Always quote. 'safe': Only quote identifiers that are case insensitive.
- normalize: Whether to normalize identifiers to lowercase. Default: False.
- pad: The pad size in a formatted string. For example, this affects the indentation of a projection in a query, relative to its nesting level. Default: 2.
- indent: The indentation size in a formatted string. For example, this affects the
indentation of subqueries and filters under a
WHERE
clause. Default: 2. - normalize_functions: How to normalize function names. Possible values are: "upper" or True (default): Convert names to uppercase. "lower": Convert names to lowercase. False: Disables function name normalization.
- unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. Default ErrorLevel.WARN.
- max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. This is only relevant if unsupported_level is ErrorLevel.RAISE. Default: 3
- leading_comma: Whether the comma is leading or trailing in select expressions. This is only relevant when generating in pretty mode. Default: False
- max_text_width: The max number of characters in a segment before creating new lines in pretty mode. The default is on the smaller end because the length only represents a segment and not the true line length. Default: 80
- comments: Whether to preserve comments in the output SQL code. Default: True
Generator( pretty: Optional[bool] = None, identify: str | bool = False, normalize: bool = False, pad: int = 2, indent: int = 2, normalize_functions: Union[str, bool, NoneType] = None, unsupported_level: sqlglot.errors.ErrorLevel = <ErrorLevel.WARN: 'WARN'>, max_unsupported: int = 3, leading_comma: bool = False, max_text_width: int = 80, comments: bool = True, dialect: Union[str, sqlglot.dialects.dialect.Dialect, Type[sqlglot.dialects.dialect.Dialect], NoneType] = None)
525 def __init__( 526 self, 527 pretty: t.Optional[bool] = None, 528 identify: str | bool = False, 529 normalize: bool = False, 530 pad: int = 2, 531 indent: int = 2, 532 normalize_functions: t.Optional[str | bool] = None, 533 unsupported_level: ErrorLevel = ErrorLevel.WARN, 534 max_unsupported: int = 3, 535 leading_comma: bool = False, 536 max_text_width: int = 80, 537 comments: bool = True, 538 dialect: DialectType = None, 539 ): 540 import sqlglot 541 from sqlglot.dialects import Dialect 542 543 self.pretty = pretty if pretty is not None else sqlglot.pretty 544 self.identify = identify 545 self.normalize = normalize 546 self.pad = pad 547 self._indent = indent 548 self.unsupported_level = unsupported_level 549 self.max_unsupported = max_unsupported 550 self.leading_comma = leading_comma 551 self.max_text_width = max_text_width 552 self.comments = comments 553 self.dialect = Dialect.get_or_raise(dialect) 554 555 # This is both a Dialect property and a Generator argument, so we prioritize the latter 556 self.normalize_functions = ( 557 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 558 ) 559 560 self.unsupported_messages: t.List[str] = [] 561 self._escaped_quote_end: str = ( 562 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 563 ) 564 self._escaped_identifier_end: str = ( 565 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 566 )
TRANSFORMS: Dict[Type[sqlglot.expressions.Expression], Callable[..., str]] =
{<class 'sqlglot.expressions.JSONPathFilter'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathKey'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRecursive'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRoot'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathScript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSelector'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSlice'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSubscript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathUnion'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathWildcard'>: <function <lambda>>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.BackupProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CaseSpecificColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CommentColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DateFormatColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.DefaultColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExcludeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.GlobalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IcebergProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InheritsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONExtract'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.JSONExtractScalar'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LanguageProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LocationProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.LogProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.MaterializedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NonClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.NotForReplicationColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnCommitProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OnUpdateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.OutputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.PathColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ReturnsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SampleProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetConfigProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SettingsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SharingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.StabilityProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.Timestamp'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ToTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransformModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TransientProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UppercaseColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.UnloggedProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VarMap'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.VolatileProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithOperator'>: <function Generator.<lambda>>}
SUPPORTED_JSON_PATH_PARTS =
{<class 'sqlglot.expressions.JSONPathFilter'>, <class 'sqlglot.expressions.JSONPathUnion'>, <class 'sqlglot.expressions.JSONPathSubscript'>, <class 'sqlglot.expressions.JSONPathSelector'>, <class 'sqlglot.expressions.JSONPathSlice'>, <class 'sqlglot.expressions.JSONPathScript'>, <class 'sqlglot.expressions.JSONPathWildcard'>, <class 'sqlglot.expressions.JSONPathRoot'>, <class 'sqlglot.expressions.JSONPathRecursive'>, <class 'sqlglot.expressions.JSONPathKey'>}
TYPE_MAPPING =
{<Type.NCHAR: 'NCHAR'>: 'CHAR', <Type.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <Type.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <Type.LONGTEXT: 'LONGTEXT'>: 'TEXT', <Type.TINYTEXT: 'TINYTEXT'>: 'TEXT', <Type.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <Type.LONGBLOB: 'LONGBLOB'>: 'BLOB', <Type.TINYBLOB: 'TINYBLOB'>: 'BLOB', <Type.INET: 'INET'>: 'INET'}
TIME_PART_SINGULARS =
{'MICROSECONDS': 'MICROSECOND', 'SECONDS': 'SECOND', 'MINUTES': 'MINUTE', 'HOURS': 'HOUR', 'DAYS': 'DAY', 'WEEKS': 'WEEK', 'MONTHS': 'MONTH', 'QUARTERS': 'QUARTER', 'YEARS': 'YEAR'}
AFTER_HAVING_MODIFIER_TRANSFORMS =
{'cluster': <function Generator.<lambda>>, 'distribute': <function Generator.<lambda>>, 'qualify': <function Generator.<lambda>>, 'sort': <function Generator.<lambda>>, 'windows': <function Generator.<lambda>>}
PROPERTIES_LOCATION =
{<class 'sqlglot.expressions.AlgorithmProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.AutoIncrementProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BackupProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BlockCompressionProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CharacterSetProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ChecksumProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CollateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Cluster'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ClusteredByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DataBlocksizeProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.DefinerProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.DictRange'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DictProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DistStyleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.EngineProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ExternalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.FallbackProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.FileFormatProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.FreespaceProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.GlobalProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.HeapProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.InheritsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IcebergProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.InputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.IsolatedLoadingProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.JournalProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.LanguageProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LikeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LocationProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.LockingProperty'>: <Location.POST_ALIAS: 'POST_ALIAS'>, <class 'sqlglot.expressions.LogProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.MaterializedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.MergeBlockRatioProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.NoPrimaryIndexProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.OnProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OnCommitProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.Order'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.OutputModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PartitionedByProperty'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.PartitionedOfProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.PrimaryKey'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Property'>: <Location.POST_WITH: 'POST_WITH'>, <class 'sqlglot.expressions.RemoteWithConnectionModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ReturnsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatDelimitedProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.RowFormatSerdeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SampleProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SchemaCommentProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SerdeProperties'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Set'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SettingsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SetProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.SetConfigProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SharingProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SequenceProperties'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.SortKeyProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlReadWriteProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.SqlSecurityProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.StabilityProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TemporaryProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ToTableProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.TransientProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.TransformModelProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.MergeTreeTTL'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.UnloggedProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.ViewAttributeProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.VolatileProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.WithDataProperty'>: <Location.POST_EXPRESSION: 'POST_EXPRESSION'>, <class 'sqlglot.expressions.WithJournalTableProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.WithSystemVersioningProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>}
WITH_SEPARATED_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] =
(<class 'sqlglot.expressions.Create'>, <class 'sqlglot.expressions.Delete'>, <class 'sqlglot.expressions.Drop'>, <class 'sqlglot.expressions.From'>, <class 'sqlglot.expressions.Insert'>, <class 'sqlglot.expressions.Join'>, <class 'sqlglot.expressions.Select'>, <class 'sqlglot.expressions.Union'>, <class 'sqlglot.expressions.Update'>, <class 'sqlglot.expressions.Where'>, <class 'sqlglot.expressions.With'>)
EXCLUDE_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] =
(<class 'sqlglot.expressions.Binary'>, <class 'sqlglot.expressions.Union'>)
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] =
(<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
PARAMETERIZABLE_TEXT_TYPES =
{<Type.CHAR: 'CHAR'>, <Type.NCHAR: 'NCHAR'>, <Type.NVARCHAR: 'NVARCHAR'>, <Type.VARCHAR: 'VARCHAR'>}
568 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 569 """ 570 Generates the SQL string corresponding to the given syntax tree. 571 572 Args: 573 expression: The syntax tree. 574 copy: Whether to copy the expression. The generator performs mutations so 575 it is safer to copy. 576 577 Returns: 578 The SQL string corresponding to `expression`. 579 """ 580 if copy: 581 expression = expression.copy() 582 583 expression = self.preprocess(expression) 584 585 self.unsupported_messages = [] 586 sql = self.sql(expression).strip() 587 588 if self.pretty: 589 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 590 591 if self.unsupported_level == ErrorLevel.IGNORE: 592 return sql 593 594 if self.unsupported_level == ErrorLevel.WARN: 595 for msg in self.unsupported_messages: 596 logger.warning(msg) 597 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 598 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 599 600 return sql
Generates the SQL string corresponding to the given syntax tree.
Arguments:
- expression: The syntax tree.
- copy: Whether to copy the expression. The generator performs mutations so it is safer to copy.
Returns:
The SQL string corresponding to
expression
.
def
preprocess( self, expression: sqlglot.expressions.Expression) -> sqlglot.expressions.Expression:
602 def preprocess(self, expression: exp.Expression) -> exp.Expression: 603 """Apply generic preprocessing transformations to a given expression.""" 604 if ( 605 not expression.parent 606 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 607 and any(node.parent is not expression for node in expression.find_all(exp.With)) 608 ): 609 from sqlglot.transforms import move_ctes_to_top_level 610 611 expression = move_ctes_to_top_level(expression) 612 613 if self.ENSURE_BOOLS: 614 from sqlglot.transforms import ensure_bools 615 616 expression = ensure_bools(expression) 617 618 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:
636 def maybe_comment( 637 self, 638 sql: str, 639 expression: t.Optional[exp.Expression] = None, 640 comments: t.Optional[t.List[str]] = None, 641 ) -> str: 642 comments = ( 643 ((expression and expression.comments) if comments is None else comments) # type: ignore 644 if self.comments 645 else None 646 ) 647 648 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 649 return sql 650 651 comments_sql = " ".join( 652 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 653 ) 654 655 if not comments_sql: 656 return sql 657 658 if isinstance(expression, self.WITH_SEPARATED_COMMENTS): 659 return ( 660 f"{self.sep()}{comments_sql}{sql}" 661 if not sql or sql[0].isspace() 662 else f"{comments_sql}{self.sep()}{sql}" 663 ) 664 665 return f"{sql} {comments_sql}"
667 def wrap(self, expression: exp.Expression | str) -> str: 668 this_sql = self.indent( 669 ( 670 self.sql(expression) 671 if isinstance(expression, exp.UNWRAPPED_QUERIES) 672 else self.sql(expression, "this") 673 ), 674 level=1, 675 pad=0, 676 ) 677 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:
693 def indent( 694 self, 695 sql: str, 696 level: int = 0, 697 pad: t.Optional[int] = None, 698 skip_first: bool = False, 699 skip_last: bool = False, 700 ) -> str: 701 if not self.pretty: 702 return sql 703 704 pad = self.pad if pad is None else pad 705 lines = sql.split("\n") 706 707 return "\n".join( 708 ( 709 line 710 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 711 else f"{' ' * (level * self._indent + pad)}{line}" 712 ) 713 for i, line in enumerate(lines) 714 )
def
sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str:
716 def sql( 717 self, 718 expression: t.Optional[str | exp.Expression], 719 key: t.Optional[str] = None, 720 comment: bool = True, 721 ) -> str: 722 if not expression: 723 return "" 724 725 if isinstance(expression, str): 726 return expression 727 728 if key: 729 value = expression.args.get(key) 730 if value: 731 return self.sql(value) 732 return "" 733 734 transform = self.TRANSFORMS.get(expression.__class__) 735 736 if callable(transform): 737 sql = transform(self, expression) 738 elif isinstance(expression, exp.Expression): 739 exp_handler_name = f"{expression.key}_sql" 740 741 if hasattr(self, exp_handler_name): 742 sql = getattr(self, exp_handler_name)(expression) 743 elif isinstance(expression, exp.Func): 744 sql = self.function_fallback_sql(expression) 745 elif isinstance(expression, exp.Property): 746 sql = self.property_sql(expression) 747 else: 748 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 749 else: 750 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 751 752 return self.maybe_comment(sql, expression) if self.comments and comment else sql
759 def cache_sql(self, expression: exp.Cache) -> str: 760 lazy = " LAZY" if expression.args.get("lazy") else "" 761 table = self.sql(expression, "this") 762 options = expression.args.get("options") 763 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 764 sql = self.sql(expression, "expression") 765 sql = f" AS{self.sep()}{sql}" if sql else "" 766 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 767 return self.prepend_ctes(expression, sql)
769 def characterset_sql(self, expression: exp.CharacterSet) -> str: 770 if isinstance(expression.parent, exp.Cast): 771 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 772 default = "DEFAULT " if expression.args.get("default") else "" 773 return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
775 def column_sql(self, expression: exp.Column) -> str: 776 join_mark = " (+)" if expression.args.get("join_mark") else "" 777 778 if join_mark and not self.COLUMN_JOIN_MARKS_SUPPORTED: 779 join_mark = "" 780 self.unsupported("Outer join syntax using the (+) operator is not supported.") 781 782 column = ".".join( 783 self.sql(part) 784 for part in ( 785 expression.args.get("catalog"), 786 expression.args.get("db"), 787 expression.args.get("table"), 788 expression.args.get("this"), 789 ) 790 if part 791 ) 792 793 return f"{column}{join_mark}"
801 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 802 column = self.sql(expression, "this") 803 kind = self.sql(expression, "kind") 804 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 805 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 806 kind = f"{sep}{kind}" if kind else "" 807 constraints = f" {constraints}" if constraints else "" 808 position = self.sql(expression, "position") 809 position = f" {position}" if position else "" 810 811 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 812 kind = "" 813 814 return f"{exists}{column}{kind}{constraints}{position}"
def
computedcolumnconstraint_sql(self, expression: sqlglot.expressions.ComputedColumnConstraint) -> str:
821 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 822 this = self.sql(expression, "this") 823 if expression.args.get("not_null"): 824 persisted = " PERSISTED NOT NULL" 825 elif expression.args.get("persisted"): 826 persisted = " PERSISTED" 827 else: 828 persisted = "" 829 return f"AS {this}{persisted}"
def
compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str:
def
generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str:
842 def generatedasidentitycolumnconstraint_sql( 843 self, expression: exp.GeneratedAsIdentityColumnConstraint 844 ) -> str: 845 this = "" 846 if expression.this is not None: 847 on_null = " ON NULL" if expression.args.get("on_null") else "" 848 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 849 850 start = expression.args.get("start") 851 start = f"START WITH {start}" if start else "" 852 increment = expression.args.get("increment") 853 increment = f" INCREMENT BY {increment}" if increment else "" 854 minvalue = expression.args.get("minvalue") 855 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 856 maxvalue = expression.args.get("maxvalue") 857 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 858 cycle = expression.args.get("cycle") 859 cycle_sql = "" 860 861 if cycle is not None: 862 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 863 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 864 865 sequence_opts = "" 866 if start or increment or cycle_sql: 867 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 868 sequence_opts = f" ({sequence_opts.strip()})" 869 870 expr = self.sql(expression, "expression") 871 expr = f"({expr})" if expr else "IDENTITY" 872 873 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:
899 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 900 this = self.sql(expression, "this") 901 this = f" {this}" if this else "" 902 index_type = expression.args.get("index_type") 903 index_type = f" USING {index_type}" if index_type else "" 904 on_conflict = self.sql(expression, "on_conflict") 905 on_conflict = f" {on_conflict}" if on_conflict else "" 906 return f"UNIQUE{this}{index_type}{on_conflict}"
911 def create_sql(self, expression: exp.Create) -> str: 912 kind = self.sql(expression, "kind") 913 properties = expression.args.get("properties") 914 properties_locs = self.locate_properties(properties) if properties else defaultdict() 915 916 this = self.createable_sql(expression, properties_locs) 917 918 properties_sql = "" 919 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 920 exp.Properties.Location.POST_WITH 921 ): 922 properties_sql = self.sql( 923 exp.Properties( 924 expressions=[ 925 *properties_locs[exp.Properties.Location.POST_SCHEMA], 926 *properties_locs[exp.Properties.Location.POST_WITH], 927 ] 928 ) 929 ) 930 931 begin = " BEGIN" if expression.args.get("begin") else "" 932 end = " END" if expression.args.get("end") else "" 933 934 expression_sql = self.sql(expression, "expression") 935 if expression_sql: 936 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 937 938 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 939 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 940 postalias_props_sql = self.properties( 941 exp.Properties( 942 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 943 ), 944 wrapped=False, 945 ) 946 expression_sql = f" AS {postalias_props_sql}{expression_sql}" 947 else: 948 expression_sql = f" AS{expression_sql}" 949 950 postindex_props_sql = "" 951 if properties_locs.get(exp.Properties.Location.POST_INDEX): 952 postindex_props_sql = self.properties( 953 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 954 wrapped=False, 955 prefix=" ", 956 ) 957 958 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 959 indexes = f" {indexes}" if indexes else "" 960 index_sql = indexes + postindex_props_sql 961 962 replace = " OR REPLACE" if expression.args.get("replace") else "" 963 unique = " UNIQUE" if expression.args.get("unique") else "" 964 965 postcreate_props_sql = "" 966 if properties_locs.get(exp.Properties.Location.POST_CREATE): 967 postcreate_props_sql = self.properties( 968 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 969 sep=" ", 970 prefix=" ", 971 wrapped=False, 972 ) 973 974 modifiers = "".join((replace, unique, postcreate_props_sql)) 975 976 postexpression_props_sql = "" 977 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 978 postexpression_props_sql = self.properties( 979 exp.Properties( 980 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 981 ), 982 sep=" ", 983 prefix=" ", 984 wrapped=False, 985 ) 986 987 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 988 no_schema_binding = ( 989 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 990 ) 991 992 clone = self.sql(expression, "clone") 993 clone = f" {clone}" if clone else "" 994 995 expression_sql = f"CREATE{modifiers} {kind}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 996 return self.prepend_ctes(expression, expression_sql)
998 def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str: 999 start = self.sql(expression, "start") 1000 start = f"START WITH {start}" if start else "" 1001 increment = self.sql(expression, "increment") 1002 increment = f" INCREMENT BY {increment}" if increment else "" 1003 minvalue = self.sql(expression, "minvalue") 1004 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 1005 maxvalue = self.sql(expression, "maxvalue") 1006 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 1007 owned = self.sql(expression, "owned") 1008 owned = f" OWNED BY {owned}" if owned else "" 1009 1010 cache = expression.args.get("cache") 1011 if cache is None: 1012 cache_str = "" 1013 elif cache is True: 1014 cache_str = " CACHE" 1015 else: 1016 cache_str = f" CACHE {cache}" 1017 1018 options = self.expressions(expression, key="options", flat=True, sep=" ") 1019 options = f" {options}" if options else "" 1020 1021 return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
1023 def clone_sql(self, expression: exp.Clone) -> str: 1024 this = self.sql(expression, "this") 1025 shallow = "SHALLOW " if expression.args.get("shallow") else "" 1026 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 1027 return f"{shallow}{keyword} {this}"
1054 def cte_sql(self, expression: exp.CTE) -> str: 1055 alias = self.sql(expression, "alias") 1056 1057 materialized = expression.args.get("materialized") 1058 if materialized is False: 1059 materialized = "NOT MATERIALIZED " 1060 elif materialized: 1061 materialized = "MATERIALIZED " 1062 1063 return f"{alias} AS {materialized or ''}{self.wrap(expression)}"
1065 def tablealias_sql(self, expression: exp.TableAlias) -> str: 1066 alias = self.sql(expression, "this") 1067 columns = self.expressions(expression, key="columns", flat=True) 1068 columns = f"({columns})" if columns else "" 1069 1070 if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS: 1071 columns = "" 1072 self.unsupported("Named columns are not supported in table alias.") 1073 1074 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 1075 alias = "_t" 1076 1077 return f"{alias}{columns}"
1097 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 1098 this = self.sql(expression, "this") 1099 escape = expression.args.get("escape") 1100 1101 if self.dialect.UNICODE_START: 1102 escape = f" UESCAPE {self.sql(escape)}" if escape else "" 1103 return f"{self.dialect.UNICODE_START}{this}{self.dialect.UNICODE_END}{escape}" 1104 1105 if escape: 1106 pattern = re.compile(rf"{escape.name}(\d+)") 1107 else: 1108 pattern = ESCAPED_UNICODE_RE 1109 1110 this = pattern.sub(r"\\u\1", this) 1111 return f"{self.dialect.QUOTE_START}{this}{self.dialect.QUOTE_END}"
1123 def datatype_sql(self, expression: exp.DataType) -> str: 1124 type_value = expression.this 1125 1126 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 1127 type_sql = self.sql(expression, "kind") 1128 else: 1129 type_sql = ( 1130 self.TYPE_MAPPING.get(type_value, type_value.value) 1131 if isinstance(type_value, exp.DataType.Type) 1132 else type_value 1133 ) 1134 1135 nested = "" 1136 interior = self.expressions(expression, flat=True) 1137 values = "" 1138 1139 if interior: 1140 if expression.args.get("nested"): 1141 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 1142 if expression.args.get("values") is not None: 1143 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 1144 values = self.expressions(expression, key="values", flat=True) 1145 values = f"{delimiters[0]}{values}{delimiters[1]}" 1146 elif type_value == exp.DataType.Type.INTERVAL: 1147 nested = f" {interior}" 1148 else: 1149 nested = f"({interior})" 1150 1151 type_sql = f"{type_sql}{nested}{values}" 1152 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 1153 exp.DataType.Type.TIMETZ, 1154 exp.DataType.Type.TIMESTAMPTZ, 1155 ): 1156 type_sql = f"{type_sql} WITH TIME ZONE" 1157 1158 return type_sql
1160 def directory_sql(self, expression: exp.Directory) -> str: 1161 local = "LOCAL " if expression.args.get("local") else "" 1162 row_format = self.sql(expression, "row_format") 1163 row_format = f" {row_format}" if row_format else "" 1164 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
1166 def delete_sql(self, expression: exp.Delete) -> str: 1167 this = self.sql(expression, "this") 1168 this = f" FROM {this}" if this else "" 1169 using = self.sql(expression, "using") 1170 using = f" USING {using}" if using else "" 1171 where = self.sql(expression, "where") 1172 returning = self.sql(expression, "returning") 1173 limit = self.sql(expression, "limit") 1174 tables = self.expressions(expression, key="tables") 1175 tables = f" {tables}" if tables else "" 1176 if self.RETURNING_END: 1177 expression_sql = f"{this}{using}{where}{returning}{limit}" 1178 else: 1179 expression_sql = f"{returning}{this}{using}{where}{limit}" 1180 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
1182 def drop_sql(self, expression: exp.Drop) -> str: 1183 this = self.sql(expression, "this") 1184 expressions = self.expressions(expression, flat=True) 1185 expressions = f" ({expressions})" if expressions else "" 1186 kind = expression.args["kind"] 1187 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1188 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1189 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1190 cascade = " CASCADE" if expression.args.get("cascade") else "" 1191 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1192 purge = " PURGE" if expression.args.get("purge") else "" 1193 return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{expressions}{cascade}{constraints}{purge}"
1201 def fetch_sql(self, expression: exp.Fetch) -> str: 1202 direction = expression.args.get("direction") 1203 direction = f" {direction}" if direction else "" 1204 count = expression.args.get("count") 1205 count = f" {count}" if count else "" 1206 if expression.args.get("percent"): 1207 count = f"{count} PERCENT" 1208 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1209 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
1211 def filter_sql(self, expression: exp.Filter) -> str: 1212 if self.AGGREGATE_FILTER_SUPPORTED: 1213 this = self.sql(expression, "this") 1214 where = self.sql(expression, "expression").strip() 1215 return f"{this} FILTER({where})" 1216 1217 agg = expression.this 1218 agg_arg = agg.this 1219 cond = expression.expression.this 1220 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1221 return self.sql(agg)
1230 def indexparameters_sql(self, expression: exp.IndexParameters) -> str: 1231 using = self.sql(expression, "using") 1232 using = f" USING {using}" if using else "" 1233 columns = self.expressions(expression, key="columns", flat=True) 1234 columns = f"({columns})" if columns else "" 1235 partition_by = self.expressions(expression, key="partition_by", flat=True) 1236 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1237 where = self.sql(expression, "where") 1238 include = self.expressions(expression, key="include", flat=True) 1239 if include: 1240 include = f" INCLUDE ({include})" 1241 with_storage = self.expressions(expression, key="with_storage", flat=True) 1242 with_storage = f" WITH ({with_storage})" if with_storage else "" 1243 tablespace = self.sql(expression, "tablespace") 1244 tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else "" 1245 1246 return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}"
1248 def index_sql(self, expression: exp.Index) -> str: 1249 unique = "UNIQUE " if expression.args.get("unique") else "" 1250 primary = "PRIMARY " if expression.args.get("primary") else "" 1251 amp = "AMP " if expression.args.get("amp") else "" 1252 name = self.sql(expression, "this") 1253 name = f"{name} " if name else "" 1254 table = self.sql(expression, "table") 1255 table = f"{self.INDEX_ON} {table}" if table else "" 1256 1257 index = "INDEX " if not table else "" 1258 1259 params = self.sql(expression, "params") 1260 return f"{unique}{primary}{amp}{index}{name}{table}{params}"
1262 def identifier_sql(self, expression: exp.Identifier) -> str: 1263 text = expression.name 1264 lower = text.lower() 1265 text = lower if self.normalize and not expression.quoted else text 1266 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1267 if ( 1268 expression.quoted 1269 or self.dialect.can_identify(text, self.identify) 1270 or lower in self.RESERVED_KEYWORDS 1271 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1272 ): 1273 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1274 return text
1276 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1277 input_format = self.sql(expression, "input_format") 1278 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1279 output_format = self.sql(expression, "output_format") 1280 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1281 return self.sep().join((input_format, output_format))
1290 def properties_sql(self, expression: exp.Properties) -> str: 1291 root_properties = [] 1292 with_properties = [] 1293 1294 for p in expression.expressions: 1295 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1296 if p_loc == exp.Properties.Location.POST_WITH: 1297 with_properties.append(p) 1298 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1299 root_properties.append(p) 1300 1301 return self.root_properties( 1302 exp.Properties(expressions=root_properties) 1303 ) + 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:
1310 def properties( 1311 self, 1312 properties: exp.Properties, 1313 prefix: str = "", 1314 sep: str = ", ", 1315 suffix: str = "", 1316 wrapped: bool = True, 1317 ) -> str: 1318 if properties.expressions: 1319 expressions = self.expressions(properties, sep=sep, indent=False) 1320 if expressions: 1321 expressions = self.wrap(expressions) if wrapped else expressions 1322 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1323 return ""
1328 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1329 properties_locs = defaultdict(list) 1330 for p in properties.expressions: 1331 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1332 if p_loc != exp.Properties.Location.UNSUPPORTED: 1333 properties_locs[p_loc].append(p) 1334 else: 1335 self.unsupported(f"Unsupported property {p.key}") 1336 1337 return properties_locs
def
property_name( self, expression: sqlglot.expressions.Property, string_key: bool = False) -> str:
1344 def property_sql(self, expression: exp.Property) -> str: 1345 property_cls = expression.__class__ 1346 if property_cls == exp.Property: 1347 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1348 1349 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1350 if not property_name: 1351 self.unsupported(f"Unsupported property {expression.key}") 1352 1353 return f"{property_name}={self.sql(expression, 'this')}"
1355 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1356 if self.SUPPORTS_CREATE_TABLE_LIKE: 1357 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1358 options = f" {options}" if options else "" 1359 1360 like = f"LIKE {self.sql(expression, 'this')}{options}" 1361 if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema): 1362 like = f"({like})" 1363 1364 return like 1365 1366 if expression.expressions: 1367 self.unsupported("Transpilation of LIKE property options is unsupported") 1368 1369 select = exp.select("*").from_(expression.this).limit(0) 1370 return f"AS {self.sql(select)}"
1377 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1378 no = "NO " if expression.args.get("no") else "" 1379 local = expression.args.get("local") 1380 local = f"{local} " if local else "" 1381 dual = "DUAL " if expression.args.get("dual") else "" 1382 before = "BEFORE " if expression.args.get("before") else "" 1383 after = "AFTER " if expression.args.get("after") else "" 1384 return f"{no}{local}{dual}{before}{after}JOURNAL"
def
mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str:
1400 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1401 if expression.args.get("no"): 1402 return "NO MERGEBLOCKRATIO" 1403 if expression.args.get("default"): 1404 return "DEFAULT MERGEBLOCKRATIO" 1405 1406 percent = " PERCENT" if expression.args.get("percent") else "" 1407 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
1409 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1410 default = expression.args.get("default") 1411 minimum = expression.args.get("minimum") 1412 maximum = expression.args.get("maximum") 1413 if default or minimum or maximum: 1414 if default: 1415 prop = "DEFAULT" 1416 elif minimum: 1417 prop = "MINIMUM" 1418 else: 1419 prop = "MAXIMUM" 1420 return f"{prop} DATABLOCKSIZE" 1421 units = expression.args.get("units") 1422 units = f" {units}" if units else "" 1423 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
def
blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str:
1425 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1426 autotemp = expression.args.get("autotemp") 1427 always = expression.args.get("always") 1428 default = expression.args.get("default") 1429 manual = expression.args.get("manual") 1430 never = expression.args.get("never") 1431 1432 if autotemp is not None: 1433 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1434 elif always: 1435 prop = "ALWAYS" 1436 elif default: 1437 prop = "DEFAULT" 1438 elif manual: 1439 prop = "MANUAL" 1440 elif never: 1441 prop = "NEVER" 1442 return f"BLOCKCOMPRESSION={prop}"
def
isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str:
1444 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1445 no = expression.args.get("no") 1446 no = " NO" if no else "" 1447 concurrent = expression.args.get("concurrent") 1448 concurrent = " CONCURRENT" if concurrent else "" 1449 target = self.sql(expression, "target") 1450 target = f" {target}" if target else "" 1451 return f"WITH{no}{concurrent} ISOLATED LOADING{target}"
1453 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1454 if isinstance(expression.this, list): 1455 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1456 if expression.this: 1457 modulus = self.sql(expression, "this") 1458 remainder = self.sql(expression, "expression") 1459 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1460 1461 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1462 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1463 return f"FROM ({from_expressions}) TO ({to_expressions})"
1465 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1466 this = self.sql(expression, "this") 1467 1468 for_values_or_default = expression.expression 1469 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1470 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1471 else: 1472 for_values_or_default = " DEFAULT" 1473 1474 return f"PARTITION OF {this}{for_values_or_default}"
1476 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1477 kind = expression.args.get("kind") 1478 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1479 for_or_in = expression.args.get("for_or_in") 1480 for_or_in = f" {for_or_in}" if for_or_in else "" 1481 lock_type = expression.args.get("lock_type") 1482 override = " OVERRIDE" if expression.args.get("override") else "" 1483 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
1485 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1486 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1487 statistics = expression.args.get("statistics") 1488 statistics_sql = "" 1489 if statistics is not None: 1490 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1491 return f"{data_sql}{statistics_sql}"
def
withsystemversioningproperty_sql( self, expression: sqlglot.expressions.WithSystemVersioningProperty) -> str:
1493 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1494 sql = "WITH(SYSTEM_VERSIONING=ON" 1495 1496 if expression.this: 1497 history_table = self.sql(expression, "this") 1498 sql = f"{sql}(HISTORY_TABLE={history_table}" 1499 1500 if expression.expression: 1501 data_consistency_check = self.sql(expression, "expression") 1502 sql = f"{sql}, DATA_CONSISTENCY_CHECK={data_consistency_check}" 1503 1504 sql = f"{sql})" 1505 1506 return f"{sql})"
1508 def insert_sql(self, expression: exp.Insert) -> str: 1509 hint = self.sql(expression, "hint") 1510 overwrite = expression.args.get("overwrite") 1511 1512 if isinstance(expression.this, exp.Directory): 1513 this = " OVERWRITE" if overwrite else " INTO" 1514 else: 1515 this = self.INSERT_OVERWRITE if overwrite else " INTO" 1516 1517 alternative = expression.args.get("alternative") 1518 alternative = f" OR {alternative}" if alternative else "" 1519 ignore = " IGNORE" if expression.args.get("ignore") else "" 1520 is_function = expression.args.get("is_function") 1521 if is_function: 1522 this = f"{this} FUNCTION" 1523 this = f"{this} {self.sql(expression, 'this')}" 1524 1525 exists = " IF EXISTS" if expression.args.get("exists") else "" 1526 partition_sql = ( 1527 f" {self.sql(expression, 'partition')}" if expression.args.get("partition") else "" 1528 ) 1529 where = self.sql(expression, "where") 1530 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1531 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1532 on_conflict = self.sql(expression, "conflict") 1533 on_conflict = f" {on_conflict}" if on_conflict else "" 1534 by_name = " BY NAME" if expression.args.get("by_name") else "" 1535 returning = self.sql(expression, "returning") 1536 1537 if self.RETURNING_END: 1538 expression_sql = f"{expression_sql}{on_conflict}{returning}" 1539 else: 1540 expression_sql = f"{returning}{expression_sql}{on_conflict}" 1541 1542 sql = f"INSERT{hint}{alternative}{ignore}{this}{by_name}{exists}{partition_sql}{where}{expression_sql}" 1543 return self.prepend_ctes(expression, sql)
1567 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1568 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1569 1570 constraint = self.sql(expression, "constraint") 1571 constraint = f" ON CONSTRAINT {constraint}" if constraint else "" 1572 1573 conflict_keys = self.expressions(expression, key="conflict_keys", flat=True) 1574 conflict_keys = f"({conflict_keys}) " if conflict_keys else " " 1575 action = self.sql(expression, "action") 1576 1577 expressions = self.expressions(expression, flat=True) 1578 if expressions: 1579 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1580 expressions = f" {set_keyword}{expressions}" 1581 1582 return f"{conflict}{constraint}{conflict_keys}{action}{expressions}"
def
rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str:
1587 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1588 fields = self.sql(expression, "fields") 1589 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1590 escaped = self.sql(expression, "escaped") 1591 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1592 items = self.sql(expression, "collection_items") 1593 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1594 keys = self.sql(expression, "map_keys") 1595 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1596 lines = self.sql(expression, "lines") 1597 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1598 null = self.sql(expression, "null") 1599 null = f" NULL DEFINED AS {null}" if null else "" 1600 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
1628 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1629 table = self.table_parts(expression) 1630 only = "ONLY " if expression.args.get("only") else "" 1631 version = self.sql(expression, "version") 1632 version = f" {version}" if version else "" 1633 alias = self.sql(expression, "alias") 1634 alias = f"{sep}{alias}" if alias else "" 1635 hints = self.expressions(expression, key="hints", sep=" ") 1636 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1637 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 1638 pivots = f" {pivots}" if pivots else "" 1639 joins = self.indent( 1640 self.expressions(expression, key="joins", sep="", flat=True), skip_first=True 1641 ) 1642 laterals = self.expressions(expression, key="laterals", sep="") 1643 1644 file_format = self.sql(expression, "format") 1645 if file_format: 1646 pattern = self.sql(expression, "pattern") 1647 pattern = f", PATTERN => {pattern}" if pattern else "" 1648 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1649 1650 ordinality = expression.args.get("ordinality") or "" 1651 if ordinality: 1652 ordinality = f" WITH ORDINALITY{alias}" 1653 alias = "" 1654 1655 when = self.sql(expression, "when") 1656 if when: 1657 table = f"{table} {when}" 1658 1659 return f"{only}{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:
1661 def tablesample_sql( 1662 self, 1663 expression: exp.TableSample, 1664 sep: str = " AS ", 1665 tablesample_keyword: t.Optional[str] = None, 1666 ) -> str: 1667 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1668 table = expression.this.copy() 1669 table.set("alias", None) 1670 this = self.sql(table) 1671 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1672 else: 1673 this = self.sql(expression, "this") 1674 alias = "" 1675 1676 method = self.sql(expression, "method") 1677 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1678 numerator = self.sql(expression, "bucket_numerator") 1679 denominator = self.sql(expression, "bucket_denominator") 1680 field = self.sql(expression, "bucket_field") 1681 field = f" ON {field}" if field else "" 1682 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1683 seed = self.sql(expression, "seed") 1684 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1685 1686 size = self.sql(expression, "size") 1687 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1688 size = f"{size} ROWS" 1689 1690 percent = self.sql(expression, "percent") 1691 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1692 percent = f"{percent} PERCENT" 1693 1694 expr = f"{bucket}{percent}{size}" 1695 if self.TABLESAMPLE_REQUIRES_PARENS: 1696 expr = f"({expr})" 1697 1698 return ( 1699 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1700 )
1702 def pivot_sql(self, expression: exp.Pivot) -> str: 1703 expressions = self.expressions(expression, flat=True) 1704 1705 if expression.this: 1706 this = self.sql(expression, "this") 1707 if not expressions: 1708 return f"UNPIVOT {this}" 1709 1710 on = f"{self.seg('ON')} {expressions}" 1711 using = self.expressions(expression, key="using", flat=True) 1712 using = f"{self.seg('USING')} {using}" if using else "" 1713 group = self.sql(expression, "group") 1714 return f"PIVOT {this}{on}{using}{group}" 1715 1716 alias = self.sql(expression, "alias") 1717 alias = f" AS {alias}" if alias else "" 1718 direction = "UNPIVOT" if expression.unpivot else "PIVOT" 1719 field = self.sql(expression, "field") 1720 include_nulls = expression.args.get("include_nulls") 1721 if include_nulls is not None: 1722 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1723 else: 1724 nulls = "" 1725 return f"{direction}{nulls}({expressions} FOR {field}){alias}"
1736 def update_sql(self, expression: exp.Update) -> str: 1737 this = self.sql(expression, "this") 1738 set_sql = self.expressions(expression, flat=True) 1739 from_sql = self.sql(expression, "from") 1740 where_sql = self.sql(expression, "where") 1741 returning = self.sql(expression, "returning") 1742 order = self.sql(expression, "order") 1743 limit = self.sql(expression, "limit") 1744 if self.RETURNING_END: 1745 expression_sql = f"{from_sql}{where_sql}{returning}" 1746 else: 1747 expression_sql = f"{returning}{from_sql}{where_sql}" 1748 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1749 return self.prepend_ctes(expression, sql)
1751 def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str: 1752 values_as_table = values_as_table and self.VALUES_AS_TABLE 1753 1754 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1755 if values_as_table or not expression.find_ancestor(exp.From, exp.Join): 1756 args = self.expressions(expression) 1757 alias = self.sql(expression, "alias") 1758 values = f"VALUES{self.seg('')}{args}" 1759 values = ( 1760 f"({values})" 1761 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1762 else values 1763 ) 1764 return f"{values} AS {alias}" if alias else values 1765 1766 # Converts `VALUES...` expression into a series of select unions. 1767 alias_node = expression.args.get("alias") 1768 column_names = alias_node and alias_node.columns 1769 1770 selects: t.List[exp.Query] = [] 1771 1772 for i, tup in enumerate(expression.expressions): 1773 row = tup.expressions 1774 1775 if i == 0 and column_names: 1776 row = [ 1777 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1778 ] 1779 1780 selects.append(exp.Select(expressions=row)) 1781 1782 if self.pretty: 1783 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1784 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1785 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1786 query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1787 return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False)) 1788 1789 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1790 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1791 return f"({unions}){alias}"
1804 def group_sql(self, expression: exp.Group) -> str: 1805 group_by = self.op_expressions("GROUP BY", expression) 1806 1807 if expression.args.get("all"): 1808 return f"{group_by} ALL" 1809 1810 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1811 grouping_sets = ( 1812 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1813 ) 1814 1815 cube = expression.args.get("cube", []) 1816 if seq_get(cube, 0) is True: 1817 return f"{group_by}{self.seg('WITH CUBE')}" 1818 else: 1819 cube_sql = self.expressions(expression, key="cube", indent=False) 1820 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1821 1822 rollup = expression.args.get("rollup", []) 1823 if seq_get(rollup, 0) is True: 1824 return f"{group_by}{self.seg('WITH ROLLUP')}" 1825 else: 1826 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1827 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1828 1829 groupings = csv( 1830 grouping_sets, 1831 cube_sql, 1832 rollup_sql, 1833 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1834 sep=self.GROUPINGS_SEP, 1835 ) 1836 1837 if expression.args.get("expressions") and groupings: 1838 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1839 1840 return f"{group_by}{groupings}"
1846 def connect_sql(self, expression: exp.Connect) -> str: 1847 start = self.sql(expression, "start") 1848 start = self.seg(f"START WITH {start}") if start else "" 1849 nocycle = " NOCYCLE" if expression.args.get("nocycle") else "" 1850 connect = self.sql(expression, "connect") 1851 connect = self.seg(f"CONNECT BY{nocycle} {connect}") 1852 return start + connect
1857 def join_sql(self, expression: exp.Join) -> str: 1858 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1859 side = None 1860 else: 1861 side = expression.side 1862 1863 op_sql = " ".join( 1864 op 1865 for op in ( 1866 expression.method, 1867 "GLOBAL" if expression.args.get("global") else None, 1868 side, 1869 expression.kind, 1870 expression.hint if self.JOIN_HINTS else None, 1871 ) 1872 if op 1873 ) 1874 match_cond = self.sql(expression, "match_condition") 1875 match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else "" 1876 on_sql = self.sql(expression, "on") 1877 using = expression.args.get("using") 1878 1879 if not on_sql and using: 1880 on_sql = csv(*(self.sql(column) for column in using)) 1881 1882 this = expression.this 1883 this_sql = self.sql(this) 1884 1885 if on_sql: 1886 on_sql = self.indent(on_sql, skip_first=True) 1887 space = self.seg(" " * self.pad) if self.pretty else " " 1888 if using: 1889 on_sql = f"{space}USING ({on_sql})" 1890 else: 1891 on_sql = f"{space}ON {on_sql}" 1892 elif not op_sql: 1893 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 1894 return f" {this_sql}" 1895 1896 return f", {this_sql}" 1897 1898 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 1899 return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}"
1906 def lateral_op(self, expression: exp.Lateral) -> str: 1907 cross_apply = expression.args.get("cross_apply") 1908 1909 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 1910 if cross_apply is True: 1911 op = "INNER JOIN " 1912 elif cross_apply is False: 1913 op = "LEFT JOIN " 1914 else: 1915 op = "" 1916 1917 return f"{op}LATERAL"
1919 def lateral_sql(self, expression: exp.Lateral) -> str: 1920 this = self.sql(expression, "this") 1921 1922 if expression.args.get("view"): 1923 alias = expression.args["alias"] 1924 columns = self.expressions(alias, key="columns", flat=True) 1925 table = f" {alias.name}" if alias.name else "" 1926 columns = f" AS {columns}" if columns else "" 1927 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 1928 return f"{op_sql}{self.sep()}{this}{table}{columns}" 1929 1930 alias = self.sql(expression, "alias") 1931 alias = f" AS {alias}" if alias else "" 1932 return f"{self.lateral_op(expression)} {this}{alias}"
1934 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 1935 this = self.sql(expression, "this") 1936 1937 args = [ 1938 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 1939 for e in (expression.args.get(k) for k in ("offset", "expression")) 1940 if e 1941 ] 1942 1943 args_sql = ", ".join(self.sql(e) for e in args) 1944 args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql 1945 expressions = self.expressions(expression, flat=True) 1946 expressions = f" BY {expressions}" if expressions else "" 1947 1948 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"
1950 def offset_sql(self, expression: exp.Offset) -> str: 1951 this = self.sql(expression, "this") 1952 value = expression.expression 1953 value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value 1954 expressions = self.expressions(expression, flat=True) 1955 expressions = f" BY {expressions}" if expressions else "" 1956 return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
1958 def setitem_sql(self, expression: exp.SetItem) -> str: 1959 kind = self.sql(expression, "kind") 1960 kind = f"{kind} " if kind else "" 1961 this = self.sql(expression, "this") 1962 expressions = self.expressions(expression) 1963 collate = self.sql(expression, "collate") 1964 collate = f" COLLATE {collate}" if collate else "" 1965 global_ = "GLOBAL " if expression.args.get("global") else "" 1966 return f"{global_}{kind}{this}{expressions}{collate}"
1968 def set_sql(self, expression: exp.Set) -> str: 1969 expressions = ( 1970 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 1971 ) 1972 tag = " TAG" if expression.args.get("tag") else "" 1973 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
1978 def lock_sql(self, expression: exp.Lock) -> str: 1979 if not self.LOCKING_READS_SUPPORTED: 1980 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 1981 return "" 1982 1983 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 1984 expressions = self.expressions(expression, flat=True) 1985 expressions = f" OF {expressions}" if expressions else "" 1986 wait = expression.args.get("wait") 1987 1988 if wait is not None: 1989 if isinstance(wait, exp.Literal): 1990 wait = f" WAIT {self.sql(wait)}" 1991 else: 1992 wait = " NOWAIT" if wait else " SKIP LOCKED" 1993 1994 return f"{lock_type}{expressions}{wait or ''}"
def
escape_str(self, text: str, escape_backslash: bool = True) -> str:
2002 def escape_str(self, text: str, escape_backslash: bool = True) -> str: 2003 if self.dialect.ESCAPED_SEQUENCES: 2004 to_escaped = self.dialect.ESCAPED_SEQUENCES 2005 text = "".join( 2006 to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text 2007 ) 2008 2009 if self.pretty: 2010 text = text.replace("\n", self.SENTINEL_LINE_BREAK) 2011 2012 return text.replace(self.dialect.QUOTE_END, self._escaped_quote_end)
2014 def loaddata_sql(self, expression: exp.LoadData) -> str: 2015 local = " LOCAL" if expression.args.get("local") else "" 2016 inpath = f" INPATH {self.sql(expression, 'inpath')}" 2017 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 2018 this = f" INTO TABLE {self.sql(expression, 'this')}" 2019 partition = self.sql(expression, "partition") 2020 partition = f" {partition}" if partition else "" 2021 input_format = self.sql(expression, "input_format") 2022 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 2023 serde = self.sql(expression, "serde") 2024 serde = f" SERDE {serde}" if serde else "" 2025 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
2033 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 2034 this = self.sql(expression, "this") 2035 this = f"{this} " if this else this 2036 siblings = "SIBLINGS " if expression.args.get("siblings") else "" 2037 order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore 2038 interpolated_values = [ 2039 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 2040 for named_expression in expression.args.get("interpolate") or [] 2041 ] 2042 interpolate = ( 2043 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 2044 ) 2045 return f"{order}{interpolate}"
2047 def withfill_sql(self, expression: exp.WithFill) -> str: 2048 from_sql = self.sql(expression, "from") 2049 from_sql = f" FROM {from_sql}" if from_sql else "" 2050 to_sql = self.sql(expression, "to") 2051 to_sql = f" TO {to_sql}" if to_sql else "" 2052 step_sql = self.sql(expression, "step") 2053 step_sql = f" STEP {step_sql}" if step_sql else "" 2054 return f"WITH FILL{from_sql}{to_sql}{step_sql}"
2065 def ordered_sql(self, expression: exp.Ordered) -> str: 2066 desc = expression.args.get("desc") 2067 asc = not desc 2068 2069 nulls_first = expression.args.get("nulls_first") 2070 nulls_last = not nulls_first 2071 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 2072 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 2073 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 2074 2075 this = self.sql(expression, "this") 2076 2077 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 2078 nulls_sort_change = "" 2079 if nulls_first and ( 2080 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 2081 ): 2082 nulls_sort_change = " NULLS FIRST" 2083 elif ( 2084 nulls_last 2085 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 2086 and not nulls_are_last 2087 ): 2088 nulls_sort_change = " NULLS LAST" 2089 2090 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 2091 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 2092 window = expression.find_ancestor(exp.Window, exp.Select) 2093 if isinstance(window, exp.Window) and window.args.get("spec"): 2094 self.unsupported( 2095 f"'{nulls_sort_change.strip()}' translation not supported in window functions" 2096 ) 2097 nulls_sort_change = "" 2098 elif self.NULL_ORDERING_SUPPORTED is None: 2099 if expression.this.is_int: 2100 self.unsupported( 2101 f"'{nulls_sort_change.strip()}' translation not supported with positional ordering" 2102 ) 2103 elif not isinstance(expression.this, exp.Rand): 2104 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 2105 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 2106 nulls_sort_change = "" 2107 2108 with_fill = self.sql(expression, "with_fill") 2109 with_fill = f" {with_fill}" if with_fill else "" 2110 2111 return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
2113 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 2114 partition = self.partition_by_sql(expression) 2115 order = self.sql(expression, "order") 2116 measures = self.expressions(expression, key="measures") 2117 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 2118 rows = self.sql(expression, "rows") 2119 rows = self.seg(rows) if rows else "" 2120 after = self.sql(expression, "after") 2121 after = self.seg(after) if after else "" 2122 pattern = self.sql(expression, "pattern") 2123 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 2124 definition_sqls = [ 2125 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 2126 for definition in expression.args.get("define", []) 2127 ] 2128 definitions = self.expressions(sqls=definition_sqls) 2129 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 2130 body = "".join( 2131 ( 2132 partition, 2133 order, 2134 measures, 2135 rows, 2136 after, 2137 pattern, 2138 define, 2139 ) 2140 ) 2141 alias = self.sql(expression, "alias") 2142 alias = f" {alias}" if alias else "" 2143 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
2145 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 2146 limit = expression.args.get("limit") 2147 2148 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 2149 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 2150 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 2151 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 2152 2153 options = self.expressions(expression, key="options") 2154 if options: 2155 options = f" OPTION{self.wrap(options)}" 2156 2157 return csv( 2158 *sqls, 2159 *[self.sql(join) for join in expression.args.get("joins") or []], 2160 self.sql(expression, "connect"), 2161 self.sql(expression, "match"), 2162 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 2163 self.sql(expression, "prewhere"), 2164 self.sql(expression, "where"), 2165 self.sql(expression, "group"), 2166 self.sql(expression, "having"), 2167 *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()], 2168 self.sql(expression, "order"), 2169 *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit), 2170 *self.after_limit_modifiers(expression), 2171 options, 2172 sep="", 2173 )
def
offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]:
2178 def offset_limit_modifiers( 2179 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 2180 ) -> t.List[str]: 2181 return [ 2182 self.sql(expression, "offset") if fetch else self.sql(limit), 2183 self.sql(limit) if fetch else self.sql(expression, "offset"), 2184 ]
2191 def select_sql(self, expression: exp.Select) -> str: 2192 into = expression.args.get("into") 2193 if not self.SUPPORTS_SELECT_INTO and into: 2194 into.pop() 2195 2196 hint = self.sql(expression, "hint") 2197 distinct = self.sql(expression, "distinct") 2198 distinct = f" {distinct}" if distinct else "" 2199 kind = self.sql(expression, "kind") 2200 2201 limit = expression.args.get("limit") 2202 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP: 2203 top = self.limit_sql(limit, top=True) 2204 limit.pop() 2205 else: 2206 top = "" 2207 2208 expressions = self.expressions(expression) 2209 2210 if kind: 2211 if kind in self.SELECT_KINDS: 2212 kind = f" AS {kind}" 2213 else: 2214 if kind == "STRUCT": 2215 expressions = self.expressions( 2216 sqls=[ 2217 self.sql( 2218 exp.Struct( 2219 expressions=[ 2220 exp.PropertyEQ(this=e.args.get("alias"), expression=e.this) 2221 if isinstance(e, exp.Alias) 2222 else e 2223 for e in expression.expressions 2224 ] 2225 ) 2226 ) 2227 ] 2228 ) 2229 kind = "" 2230 2231 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2232 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2233 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2234 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2235 sql = self.query_modifiers( 2236 expression, 2237 f"SELECT{top_distinct}{kind}{expressions}", 2238 self.sql(expression, "into", comment=False), 2239 self.sql(expression, "from", comment=False), 2240 ) 2241 2242 sql = self.prepend_ctes(expression, sql) 2243 2244 if not self.SUPPORTS_SELECT_INTO and into: 2245 if into.args.get("temporary"): 2246 table_kind = " TEMPORARY" 2247 elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"): 2248 table_kind = " UNLOGGED" 2249 else: 2250 table_kind = "" 2251 sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}" 2252 2253 return sql
2265 def star_sql(self, expression: exp.Star) -> str: 2266 except_ = self.expressions(expression, key="except", flat=True) 2267 except_ = f"{self.seg(self.STAR_MAPPING['except'])} ({except_})" if except_ else "" 2268 replace = self.expressions(expression, key="replace", flat=True) 2269 replace = f"{self.seg(self.STAR_MAPPING['replace'])} ({replace})" if replace else "" 2270 return f"*{except_}{replace}"
2286 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2287 alias = self.sql(expression, "alias") 2288 alias = f"{sep}{alias}" if alias else "" 2289 2290 pivots = self.expressions(expression, key="pivots", sep=" ", flat=True) 2291 pivots = f" {pivots}" if pivots else "" 2292 2293 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2294 return self.prepend_ctes(expression, sql)
2300 def set_operations(self, expression: exp.Union) -> str: 2301 sqls: t.List[str] = [] 2302 stack: t.List[t.Union[str, exp.Expression]] = [expression] 2303 2304 while stack: 2305 node = stack.pop() 2306 2307 if isinstance(node, exp.Union): 2308 stack.append(node.expression) 2309 stack.append( 2310 self.maybe_comment( 2311 getattr(self, f"{node.key}_op")(node), 2312 expression=node.this, 2313 comments=node.comments, 2314 ) 2315 ) 2316 stack.append(node.this) 2317 else: 2318 sqls.append(self.sql(node)) 2319 2320 this = self.sep().join(sqls) 2321 this = self.query_modifiers(expression, this) 2322 return self.prepend_ctes(expression, this)
2333 def unnest_sql(self, expression: exp.Unnest) -> str: 2334 args = self.expressions(expression, flat=True) 2335 2336 alias = expression.args.get("alias") 2337 offset = expression.args.get("offset") 2338 2339 if self.UNNEST_WITH_ORDINALITY: 2340 if alias and isinstance(offset, exp.Expression): 2341 alias.append("columns", offset) 2342 2343 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2344 columns = alias.columns 2345 alias = self.sql(columns[0]) if columns else "" 2346 else: 2347 alias = self.sql(alias) 2348 2349 alias = f" AS {alias}" if alias else alias 2350 if self.UNNEST_WITH_ORDINALITY: 2351 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2352 else: 2353 if isinstance(offset, exp.Expression): 2354 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2355 elif offset: 2356 suffix = f"{alias} WITH OFFSET" 2357 else: 2358 suffix = alias 2359 2360 return f"UNNEST({args}){suffix}"
2369 def window_sql(self, expression: exp.Window) -> str: 2370 this = self.sql(expression, "this") 2371 partition = self.partition_by_sql(expression) 2372 order = expression.args.get("order") 2373 order = self.order_sql(order, flat=True) if order else "" 2374 spec = self.sql(expression, "spec") 2375 alias = self.sql(expression, "alias") 2376 over = self.sql(expression, "over") or "OVER" 2377 2378 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2379 2380 first = expression.args.get("first") 2381 if first is None: 2382 first = "" 2383 else: 2384 first = "FIRST" if first else "LAST" 2385 2386 if not partition and not order and not spec and alias: 2387 return f"{this} {alias}" 2388 2389 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2390 return f"{this} ({args})"
def
partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
2396 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2397 kind = self.sql(expression, "kind") 2398 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2399 end = ( 2400 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2401 or "CURRENT ROW" 2402 ) 2403 return f"{kind} BETWEEN {start} AND {end}"
2416 def bracket_sql(self, expression: exp.Bracket) -> str: 2417 expressions = apply_index_offset( 2418 expression.this, 2419 expression.expressions, 2420 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2421 ) 2422 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2423 return f"{self.sql(expression, 'this')}[{expressions_sql}]"
2428 def any_sql(self, expression: exp.Any) -> str: 2429 this = self.sql(expression, "this") 2430 if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)): 2431 if isinstance(expression.this, exp.UNWRAPPED_QUERIES): 2432 this = self.wrap(this) 2433 return f"ANY{this}" 2434 return f"ANY {this}"
2439 def case_sql(self, expression: exp.Case) -> str: 2440 this = self.sql(expression, "this") 2441 statements = [f"CASE {this}" if this else "CASE"] 2442 2443 for e in expression.args["ifs"]: 2444 statements.append(f"WHEN {self.sql(e, 'this')}") 2445 statements.append(f"THEN {self.sql(e, 'true')}") 2446 2447 default = self.sql(expression, "default") 2448 2449 if default: 2450 statements.append(f"ELSE {default}") 2451 2452 statements.append("END") 2453 2454 if self.pretty and self.text_width(statements) > self.max_text_width: 2455 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2456 2457 return " ".join(statements)
2474 def trim_sql(self, expression: exp.Trim) -> str: 2475 trim_type = self.sql(expression, "position") 2476 2477 if trim_type == "LEADING": 2478 return self.func("LTRIM", expression.this) 2479 elif trim_type == "TRAILING": 2480 return self.func("RTRIM", expression.this) 2481 else: 2482 return self.func("TRIM", expression.this, expression.expression)
def
convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]:
2484 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2485 args = expression.expressions 2486 if isinstance(expression, exp.ConcatWs): 2487 args = args[1:] # Skip the delimiter 2488 2489 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2490 args = [exp.cast(e, "text") for e in args] 2491 2492 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2493 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2494 2495 return args
2497 def concat_sql(self, expression: exp.Concat) -> str: 2498 expressions = self.convert_concat_args(expression) 2499 2500 # Some dialects don't allow a single-argument CONCAT call 2501 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2502 return self.sql(expressions[0]) 2503 2504 return self.func("CONCAT", *expressions)
2515 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2516 expressions = self.expressions(expression, flat=True) 2517 reference = self.sql(expression, "reference") 2518 reference = f" {reference}" if reference else "" 2519 delete = self.sql(expression, "delete") 2520 delete = f" ON DELETE {delete}" if delete else "" 2521 update = self.sql(expression, "update") 2522 update = f" ON UPDATE {update}" if update else "" 2523 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
2525 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2526 expressions = self.expressions(expression, flat=True) 2527 options = self.expressions(expression, key="options", flat=True, sep=" ") 2528 options = f" {options}" if options else "" 2529 return f"PRIMARY KEY ({expressions}){options}"
2546 def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str: 2547 if isinstance(expression, exp.JSONPathPart): 2548 transform = self.TRANSFORMS.get(expression.__class__) 2549 if not callable(transform): 2550 self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}") 2551 return "" 2552 2553 return transform(self, expression) 2554 2555 if isinstance(expression, int): 2556 return str(expression) 2557 2558 if self.JSON_PATH_SINGLE_QUOTE_ESCAPE: 2559 escaped = expression.replace("'", "\\'") 2560 escaped = f"\\'{expression}\\'" 2561 else: 2562 escaped = expression.replace('"', '\\"') 2563 escaped = f'"{escaped}"' 2564 2565 return escaped
def
jsonobject_sql( self, expression: sqlglot.expressions.JSONObject | sqlglot.expressions.JSONObjectAgg) -> str:
2570 def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str: 2571 null_handling = expression.args.get("null_handling") 2572 null_handling = f" {null_handling}" if null_handling else "" 2573 2574 unique_keys = expression.args.get("unique_keys") 2575 if unique_keys is not None: 2576 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2577 else: 2578 unique_keys = "" 2579 2580 return_type = self.sql(expression, "return_type") 2581 return_type = f" RETURNING {return_type}" if return_type else "" 2582 encoding = self.sql(expression, "encoding") 2583 encoding = f" ENCODING {encoding}" if encoding else "" 2584 2585 return self.func( 2586 "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG", 2587 *expression.expressions, 2588 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2589 )
2594 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2595 null_handling = expression.args.get("null_handling") 2596 null_handling = f" {null_handling}" if null_handling else "" 2597 return_type = self.sql(expression, "return_type") 2598 return_type = f" RETURNING {return_type}" if return_type else "" 2599 strict = " STRICT" if expression.args.get("strict") else "" 2600 return self.func( 2601 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2602 )
2604 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2605 this = self.sql(expression, "this") 2606 order = self.sql(expression, "order") 2607 null_handling = expression.args.get("null_handling") 2608 null_handling = f" {null_handling}" if null_handling else "" 2609 return_type = self.sql(expression, "return_type") 2610 return_type = f" RETURNING {return_type}" if return_type else "" 2611 strict = " STRICT" if expression.args.get("strict") else "" 2612 return self.func( 2613 "JSON_ARRAYAGG", 2614 this, 2615 suffix=f"{order}{null_handling}{return_type}{strict})", 2616 )
2618 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2619 path = self.sql(expression, "path") 2620 path = f" PATH {path}" if path else "" 2621 nested_schema = self.sql(expression, "nested_schema") 2622 2623 if nested_schema: 2624 return f"NESTED{path} {nested_schema}" 2625 2626 this = self.sql(expression, "this") 2627 kind = self.sql(expression, "kind") 2628 kind = f" {kind}" if kind else "" 2629 return f"{this}{kind}{path}"
2634 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2635 this = self.sql(expression, "this") 2636 path = self.sql(expression, "path") 2637 path = f", {path}" if path else "" 2638 error_handling = expression.args.get("error_handling") 2639 error_handling = f" {error_handling}" if error_handling else "" 2640 empty_handling = expression.args.get("empty_handling") 2641 empty_handling = f" {empty_handling}" if empty_handling else "" 2642 schema = self.sql(expression, "schema") 2643 return self.func( 2644 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2645 )
2647 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2648 this = self.sql(expression, "this") 2649 kind = self.sql(expression, "kind") 2650 path = self.sql(expression, "path") 2651 path = f" {path}" if path else "" 2652 as_json = " AS JSON" if expression.args.get("as_json") else "" 2653 return f"{this} {kind}{path}{as_json}"
2655 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2656 this = self.sql(expression, "this") 2657 path = self.sql(expression, "path") 2658 path = f", {path}" if path else "" 2659 expressions = self.expressions(expression) 2660 with_ = ( 2661 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2662 if expressions 2663 else "" 2664 ) 2665 return f"OPENJSON({this}{path}){with_}"
2667 def in_sql(self, expression: exp.In) -> str: 2668 query = expression.args.get("query") 2669 unnest = expression.args.get("unnest") 2670 field = expression.args.get("field") 2671 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2672 2673 if query: 2674 in_sql = self.wrap(self.sql(query)) 2675 elif unnest: 2676 in_sql = self.in_unnest_op(unnest) 2677 elif field: 2678 in_sql = self.sql(field) 2679 else: 2680 in_sql = f"({self.expressions(expression, flat=True)})" 2681 2682 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
2687 def interval_sql(self, expression: exp.Interval) -> str: 2688 unit = self.sql(expression, "unit") 2689 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2690 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2691 unit = f" {unit}" if unit else "" 2692 2693 if self.SINGLE_STRING_INTERVAL: 2694 this = expression.this.name if expression.this else "" 2695 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2696 2697 this = self.sql(expression, "this") 2698 if this: 2699 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2700 this = f" {this}" if unwrapped else f" ({this})" 2701 2702 return f"INTERVAL{this}{unit}"
2707 def reference_sql(self, expression: exp.Reference) -> str: 2708 this = self.sql(expression, "this") 2709 expressions = self.expressions(expression, flat=True) 2710 expressions = f"({expressions})" if expressions else "" 2711 options = self.expressions(expression, key="options", flat=True, sep=" ") 2712 options = f" {options}" if options else "" 2713 return f"REFERENCES {this}{expressions}{options}"
2736 def pivotalias_sql(self, expression: exp.PivotAlias) -> str: 2737 alias = expression.args["alias"] 2738 identifier_alias = isinstance(alias, exp.Identifier) 2739 2740 if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2741 alias.replace(exp.Literal.string(alias.output_name)) 2742 elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2743 alias.replace(exp.to_identifier(alias.output_name)) 2744 2745 return self.alias_sql(expression)
def
and_sql( self, expression: sqlglot.expressions.And, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
def
or_sql( self, expression: sqlglot.expressions.Or, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
def
xor_sql( self, expression: sqlglot.expressions.Xor, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
def
connector_sql( self, expression: sqlglot.expressions.Connector, op: str, stack: Optional[List[str | sqlglot.expressions.Expression]] = None) -> str:
2783 def connector_sql( 2784 self, 2785 expression: exp.Connector, 2786 op: str, 2787 stack: t.Optional[t.List[str | exp.Expression]] = None, 2788 ) -> str: 2789 if stack is not None: 2790 if expression.expressions: 2791 stack.append(self.expressions(expression, sep=f" {op} ")) 2792 else: 2793 stack.append(expression.right) 2794 if expression.comments: 2795 for comment in expression.comments: 2796 op += f" /*{self.pad_comment(comment)}*/" 2797 stack.extend((op, expression.left)) 2798 return op 2799 2800 stack = [expression] 2801 sqls: t.List[str] = [] 2802 ops = set() 2803 2804 while stack: 2805 node = stack.pop() 2806 if isinstance(node, exp.Connector): 2807 ops.add(getattr(self, f"{node.key}_sql")(node, stack)) 2808 else: 2809 sql = self.sql(node) 2810 if sqls and sqls[-1] in ops: 2811 sqls[-1] += f" {sql}" 2812 else: 2813 sqls.append(sql) 2814 2815 sep = "\n" if self.pretty and self.text_width(sqls) > self.max_text_width else " " 2816 return sep.join(sqls)
def
cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str:
2836 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2837 format_sql = self.sql(expression, "format") 2838 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2839 to_sql = self.sql(expression, "to") 2840 to_sql = f" {to_sql}" if to_sql else "" 2841 action = self.sql(expression, "action") 2842 action = f" {action}" if action else "" 2843 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql}{action})"
2860 def comment_sql(self, expression: exp.Comment) -> str: 2861 this = self.sql(expression, "this") 2862 kind = expression.args["kind"] 2863 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 2864 expression_sql = self.sql(expression, "expression") 2865 return f"COMMENT{exists_sql}ON {kind} {this} IS {expression_sql}"
2867 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 2868 this = self.sql(expression, "this") 2869 delete = " DELETE" if expression.args.get("delete") else "" 2870 recompress = self.sql(expression, "recompress") 2871 recompress = f" RECOMPRESS {recompress}" if recompress else "" 2872 to_disk = self.sql(expression, "to_disk") 2873 to_disk = f" TO DISK {to_disk}" if to_disk else "" 2874 to_volume = self.sql(expression, "to_volume") 2875 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 2876 return f"{this}{delete}{recompress}{to_disk}{to_volume}"
2878 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 2879 where = self.sql(expression, "where") 2880 group = self.sql(expression, "group") 2881 aggregates = self.expressions(expression, key="aggregates") 2882 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 2883 2884 if not (where or group or aggregates) and len(expression.expressions) == 1: 2885 return f"TTL {self.expressions(expression, flat=True)}" 2886 2887 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
2904 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 2905 this = self.sql(expression, "this") 2906 2907 dtype = self.sql(expression, "dtype") 2908 if dtype: 2909 collate = self.sql(expression, "collate") 2910 collate = f" COLLATE {collate}" if collate else "" 2911 using = self.sql(expression, "using") 2912 using = f" USING {using}" if using else "" 2913 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 2914 2915 default = self.sql(expression, "default") 2916 if default: 2917 return f"ALTER COLUMN {this} SET DEFAULT {default}" 2918 2919 comment = self.sql(expression, "comment") 2920 if comment: 2921 return f"ALTER COLUMN {this} COMMENT {comment}" 2922 2923 if not expression.args.get("drop"): 2924 self.unsupported("Unsupported ALTER COLUMN syntax") 2925 2926 return f"ALTER COLUMN {this} DROP DEFAULT"
2928 def renametable_sql(self, expression: exp.RenameTable) -> str: 2929 if not self.RENAME_TABLE_WITH_DB: 2930 # Remove db from tables 2931 expression = expression.transform( 2932 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 2933 ).assert_is(exp.RenameTable) 2934 this = self.sql(expression, "this") 2935 return f"RENAME TO {this}"
2943 def altertable_sql(self, expression: exp.AlterTable) -> str: 2944 actions = expression.args["actions"] 2945 2946 if isinstance(actions[0], exp.ColumnDef): 2947 actions = self.add_column_sql(expression) 2948 elif isinstance(actions[0], exp.Schema): 2949 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 2950 elif isinstance(actions[0], exp.Delete): 2951 actions = self.expressions(expression, key="actions", flat=True) 2952 else: 2953 actions = self.expressions(expression, key="actions", flat=True) 2954 2955 exists = " IF EXISTS" if expression.args.get("exists") else "" 2956 only = " ONLY" if expression.args.get("only") else "" 2957 options = self.expressions(expression, key="options") 2958 options = f", {options}" if options else "" 2959 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')} {actions}{options}"
2978 def distinct_sql(self, expression: exp.Distinct) -> str: 2979 this = self.expressions(expression, flat=True) 2980 2981 if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1: 2982 case = exp.case() 2983 for arg in expression.expressions: 2984 case = case.when(arg.is_(exp.null()), exp.null()) 2985 this = self.sql(case.else_(f"({this})")) 2986 2987 this = f" {this}" if this else "" 2988 2989 on = self.sql(expression, "on") 2990 on = f" ON {on}" if on else "" 2991 return f"DISTINCT{this}{on}"
3018 def div_sql(self, expression: exp.Div) -> str: 3019 l, r = expression.left, expression.right 3020 3021 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 3022 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 3023 3024 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 3025 if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES): 3026 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 3027 3028 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 3029 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 3030 return self.sql( 3031 exp.cast( 3032 l / r, 3033 to=exp.DataType.Type.BIGINT, 3034 ) 3035 ) 3036 3037 return self.binary(expression, "/")
3118 def log_sql(self, expression: exp.Log) -> str: 3119 this = expression.this 3120 expr = expression.expression 3121 3122 if self.dialect.LOG_BASE_FIRST is False: 3123 this, expr = expr, this 3124 elif self.dialect.LOG_BASE_FIRST is None and expr: 3125 if this.name in ("2", "10"): 3126 return self.func(f"LOG{this.name}", expr) 3127 3128 self.unsupported(f"Unsupported logarithm with base {self.sql(this)}") 3129 3130 return self.func("LOG", this, expr)
3143 def function_fallback_sql(self, expression: exp.Func) -> str: 3144 args = [] 3145 3146 for key in expression.arg_types: 3147 arg_value = expression.args.get(key) 3148 3149 if isinstance(arg_value, list): 3150 for value in arg_value: 3151 args.append(value) 3152 elif arg_value is not None: 3153 args.append(arg_value) 3154 3155 if self.normalize_functions: 3156 name = expression.sql_name() 3157 else: 3158 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 3159 3160 return self.func(name, *args)
def
func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')') -> str:
3171 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 3172 arg_sqls = tuple(self.sql(arg) for arg in args if arg is not None) 3173 if self.pretty and self.text_width(arg_sqls) > self.max_text_width: 3174 return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 3175 return ", ".join(arg_sqls)
def
format_time( self, expression: sqlglot.expressions.Expression, inverse_time_mapping: Optional[Dict[str, str]] = None, inverse_time_trie: Optional[Dict] = None) -> Optional[str]:
3180 def format_time( 3181 self, 3182 expression: exp.Expression, 3183 inverse_time_mapping: t.Optional[t.Dict[str, str]] = None, 3184 inverse_time_trie: t.Optional[t.Dict] = None, 3185 ) -> t.Optional[str]: 3186 return format_time( 3187 self.sql(expression, "format"), 3188 inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING, 3189 inverse_time_trie or self.dialect.INVERSE_TIME_TRIE, 3190 )
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:
3192 def expressions( 3193 self, 3194 expression: t.Optional[exp.Expression] = None, 3195 key: t.Optional[str] = None, 3196 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 3197 flat: bool = False, 3198 indent: bool = True, 3199 skip_first: bool = False, 3200 sep: str = ", ", 3201 prefix: str = "", 3202 ) -> str: 3203 expressions = expression.args.get(key or "expressions") if expression else sqls 3204 3205 if not expressions: 3206 return "" 3207 3208 if flat: 3209 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 3210 3211 num_sqls = len(expressions) 3212 3213 # These are calculated once in case we have the leading_comma / pretty option set, correspondingly 3214 if self.pretty: 3215 if self.leading_comma: 3216 pad = " " * len(sep) 3217 else: 3218 stripped_sep = sep.strip() 3219 3220 result_sqls = [] 3221 for i, e in enumerate(expressions): 3222 sql = self.sql(e, comment=False) 3223 if not sql: 3224 continue 3225 3226 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 3227 3228 if self.pretty: 3229 if self.leading_comma: 3230 result_sqls.append(f"{sep if i > 0 else pad}{prefix}{sql}{comments}") 3231 else: 3232 result_sqls.append( 3233 f"{prefix}{sql}{stripped_sep if i + 1 < num_sqls else ''}{comments}" 3234 ) 3235 else: 3236 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 3237 3238 result_sql = "\n".join(result_sqls) if self.pretty else "".join(result_sqls) 3239 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:
3241 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 3242 flat = flat or isinstance(expression.parent, exp.Properties) 3243 expressions_sql = self.expressions(expression, flat=flat) 3244 if flat: 3245 return f"{op} {expressions_sql}" 3246 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
3248 def naked_property(self, expression: exp.Property) -> str: 3249 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 3250 if not property_name: 3251 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 3252 return f"{property_name} {self.sql(expression, 'this')}"
3260 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 3261 this = self.sql(expression, "this") 3262 expressions = self.no_identify(self.expressions, expression) 3263 expressions = ( 3264 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 3265 ) 3266 return f"{this}{expressions}"
3276 def when_sql(self, expression: exp.When) -> str: 3277 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 3278 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 3279 condition = self.sql(expression, "condition") 3280 condition = f" AND {condition}" if condition else "" 3281 3282 then_expression = expression.args.get("then") 3283 if isinstance(then_expression, exp.Insert): 3284 then = f"INSERT {self.sql(then_expression, 'this')}" 3285 if "expression" in then_expression.args: 3286 then += f" VALUES {self.sql(then_expression, 'expression')}" 3287 elif isinstance(then_expression, exp.Update): 3288 if isinstance(then_expression.args.get("expressions"), exp.Star): 3289 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 3290 else: 3291 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 3292 else: 3293 then = self.sql(then_expression) 3294 return f"WHEN {matched}{source}{condition} THEN {then}"
3296 def merge_sql(self, expression: exp.Merge) -> str: 3297 table = expression.this 3298 table_alias = "" 3299 3300 hints = table.args.get("hints") 3301 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 3302 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 3303 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 3304 3305 this = self.sql(table) 3306 using = f"USING {self.sql(expression, 'using')}" 3307 on = f"ON {self.sql(expression, 'on')}" 3308 expressions = self.expressions(expression, sep=" ") 3309 3310 return self.prepend_ctes( 3311 expression, f"MERGE INTO {this}{table_alias} {using} {on} {expressions}" 3312 )
3320 def tonumber_sql(self, expression: exp.ToNumber) -> str: 3321 if not self.SUPPORTS_TO_NUMBER: 3322 self.unsupported("Unsupported TO_NUMBER function") 3323 return self.sql(exp.cast(expression.this, "double")) 3324 3325 fmt = expression.args.get("format") 3326 if not fmt: 3327 self.unsupported("Conversion format is required for TO_NUMBER") 3328 return self.sql(exp.cast(expression.this, "double")) 3329 3330 return self.func("TO_NUMBER", expression.this, fmt)
3332 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 3333 this = self.sql(expression, "this") 3334 kind = self.sql(expression, "kind") 3335 settings_sql = self.expressions(expression, key="settings", sep=" ") 3336 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 3337 return f"{this}({kind}{args})"
3351 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3352 expressions = self.expressions(expression, key="expressions", flat=True) 3353 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3354 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3355 buckets = self.sql(expression, "buckets") 3356 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
3358 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3359 this = self.sql(expression, "this") 3360 having = self.sql(expression, "having") 3361 3362 if having: 3363 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3364 3365 return self.func("ANY_VALUE", this)
3367 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3368 transform = self.func("TRANSFORM", *expression.expressions) 3369 row_format_before = self.sql(expression, "row_format_before") 3370 row_format_before = f" {row_format_before}" if row_format_before else "" 3371 record_writer = self.sql(expression, "record_writer") 3372 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3373 using = f" USING {self.sql(expression, 'command_script')}" 3374 schema = self.sql(expression, "schema") 3375 schema = f" AS {schema}" if schema else "" 3376 row_format_after = self.sql(expression, "row_format_after") 3377 row_format_after = f" {row_format_after}" if row_format_after else "" 3378 record_reader = self.sql(expression, "record_reader") 3379 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3380 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
3382 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3383 key_block_size = self.sql(expression, "key_block_size") 3384 if key_block_size: 3385 return f"KEY_BLOCK_SIZE = {key_block_size}" 3386 3387 using = self.sql(expression, "using") 3388 if using: 3389 return f"USING {using}" 3390 3391 parser = self.sql(expression, "parser") 3392 if parser: 3393 return f"WITH PARSER {parser}" 3394 3395 comment = self.sql(expression, "comment") 3396 if comment: 3397 return f"COMMENT {comment}" 3398 3399 visible = expression.args.get("visible") 3400 if visible is not None: 3401 return "VISIBLE" if visible else "INVISIBLE" 3402 3403 engine_attr = self.sql(expression, "engine_attr") 3404 if engine_attr: 3405 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3406 3407 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3408 if secondary_engine_attr: 3409 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3410 3411 self.unsupported("Unsupported index constraint option.") 3412 return ""
3418 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3419 kind = self.sql(expression, "kind") 3420 kind = f"{kind} INDEX" if kind else "INDEX" 3421 this = self.sql(expression, "this") 3422 this = f" {this}" if this else "" 3423 index_type = self.sql(expression, "index_type") 3424 index_type = f" USING {index_type}" if index_type else "" 3425 schema = self.sql(expression, "schema") 3426 schema = f" {schema}" if schema else "" 3427 options = self.expressions(expression, key="options", sep=" ") 3428 options = f" {options}" if options else "" 3429 return f"{kind}{this}{index_type}{schema}{options}"
3431 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3432 if self.NVL2_SUPPORTED: 3433 return self.function_fallback_sql(expression) 3434 3435 case = exp.Case().when( 3436 expression.this.is_(exp.null()).not_(copy=False), 3437 expression.args["true"], 3438 copy=False, 3439 ) 3440 else_cond = expression.args.get("false") 3441 if else_cond: 3442 case.else_(else_cond, copy=False) 3443 3444 return self.sql(case)
3446 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3447 this = self.sql(expression, "this") 3448 expr = self.sql(expression, "expression") 3449 iterator = self.sql(expression, "iterator") 3450 condition = self.sql(expression, "condition") 3451 condition = f" IF {condition}" if condition else "" 3452 return f"{this} FOR {expr} IN {iterator}{condition}"
3460 def predict_sql(self, expression: exp.Predict) -> str: 3461 model = self.sql(expression, "this") 3462 model = f"MODEL {model}" 3463 table = self.sql(expression, "expression") 3464 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3465 parameters = self.sql(expression, "params_struct") 3466 return self.func("PREDICT", model, table, parameters or None)
3481 def toarray_sql(self, expression: exp.ToArray) -> str: 3482 arg = expression.this 3483 if not arg.type: 3484 from sqlglot.optimizer.annotate_types import annotate_types 3485 3486 arg = annotate_types(arg) 3487 3488 if arg.is_type(exp.DataType.Type.ARRAY): 3489 return self.sql(arg) 3490 3491 cond_for_null = arg.is_(exp.null()) 3492 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
3508 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3509 this = expression.this 3510 time_format = self.format_time(expression) 3511 3512 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3513 return self.sql( 3514 exp.cast(exp.StrToTime(this=this, format=expression.args["format"]), "date") 3515 ) 3516 3517 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3518 return self.sql(this) 3519 3520 return self.sql(exp.cast(this, "date"))
3532 def lastday_sql(self, expression: exp.LastDay) -> str: 3533 if self.LAST_DAY_SUPPORTS_DATE_PART: 3534 return self.function_fallback_sql(expression) 3535 3536 unit = expression.text("unit") 3537 if unit and unit != "MONTH": 3538 self.unsupported("Date parts are not supported in LAST_DAY.") 3539 3540 return self.func("LAST_DAY", expression.this)
3549 def arrayany_sql(self, expression: exp.ArrayAny) -> str: 3550 if self.CAN_IMPLEMENT_ARRAY_ANY: 3551 filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression) 3552 filtered_not_empty = exp.ArraySize(this=filtered).neq(0) 3553 original_is_empty = exp.ArraySize(this=expression.this).eq(0) 3554 return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty))) 3555 3556 from sqlglot.dialects import Dialect 3557 3558 # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect 3559 if self.dialect.__class__ != Dialect: 3560 self.unsupported("ARRAY_ANY is unsupported") 3561 3562 return self.function_fallback_sql(expression)
3568 def struct_sql(self, expression: exp.Struct) -> str: 3569 expression.set( 3570 "expressions", 3571 [ 3572 exp.alias_(e.expression, e.name if e.this.is_string else e.this) 3573 if isinstance(e, exp.PropertyEQ) 3574 else e 3575 for e in expression.expressions 3576 ], 3577 ) 3578 3579 return self.function_fallback_sql(expression)
3587 def truncatetable_sql(self, expression: exp.TruncateTable) -> str: 3588 target = "DATABASE" if expression.args.get("is_database") else "TABLE" 3589 tables = f" {self.expressions(expression)}" 3590 3591 exists = " IF EXISTS" if expression.args.get("exists") else "" 3592 3593 on_cluster = self.sql(expression, "cluster") 3594 on_cluster = f" {on_cluster}" if on_cluster else "" 3595 3596 identity = self.sql(expression, "identity") 3597 identity = f" {identity} IDENTITY" if identity else "" 3598 3599 option = self.sql(expression, "option") 3600 option = f" {option}" if option else "" 3601 3602 partition = self.sql(expression, "partition") 3603 partition = f" {partition}" if partition else "" 3604 3605 return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
3609 def convert_sql(self, expression: exp.Convert) -> str: 3610 to = expression.this 3611 value = expression.expression 3612 style = expression.args.get("style") 3613 safe = expression.args.get("safe") 3614 strict = expression.args.get("strict") 3615 3616 if not to or not value: 3617 return "" 3618 3619 # Retrieve length of datatype and override to default if not specified 3620 if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3621 to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False) 3622 3623 transformed: t.Optional[exp.Expression] = None 3624 cast = exp.Cast if strict else exp.TryCast 3625 3626 # Check whether a conversion with format (T-SQL calls this 'style') is applicable 3627 if isinstance(style, exp.Literal) and style.is_int: 3628 from sqlglot.dialects.tsql import TSQL 3629 3630 style_value = style.name 3631 converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value) 3632 if not converted_style: 3633 self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}") 3634 3635 fmt = exp.Literal.string(converted_style) 3636 3637 if to.this == exp.DataType.Type.DATE: 3638 transformed = exp.StrToDate(this=value, format=fmt) 3639 elif to.this == exp.DataType.Type.DATETIME: 3640 transformed = exp.StrToTime(this=value, format=fmt) 3641 elif to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3642 transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe) 3643 elif to.this == exp.DataType.Type.TEXT: 3644 transformed = exp.TimeToStr(this=value, format=fmt) 3645 3646 if not transformed: 3647 transformed = cast(this=value, to=to, safe=safe) 3648 3649 return self.sql(transformed)