sqlglot.generator
1from __future__ import annotations 2 3import logging 4import re 5import typing as t 6from collections import defaultdict 7from functools import reduce 8 9from sqlglot import exp 10from sqlglot.errors import ErrorLevel, UnsupportedError, concat_messages 11from sqlglot.helper import apply_index_offset, csv, name_sequence, seq_get 12from sqlglot.jsonpath import ALL_JSON_PATH_PARTS, JSON_PATH_PART_TRANSFORMS 13from sqlglot.time import format_time 14from sqlglot.tokens import TokenType 15 16if t.TYPE_CHECKING: 17 from sqlglot._typing import E 18 from sqlglot.dialects.dialect import DialectType 19 20logger = logging.getLogger("sqlglot") 21 22ESCAPED_UNICODE_RE = re.compile(r"\\(\d+)") 23 24 25class _Generator(type): 26 def __new__(cls, clsname, bases, attrs): 27 klass = super().__new__(cls, clsname, bases, attrs) 28 29 # Remove transforms that correspond to unsupported JSONPathPart expressions 30 for part in ALL_JSON_PATH_PARTS - klass.SUPPORTED_JSON_PATH_PARTS: 31 klass.TRANSFORMS.pop(part, None) 32 33 return klass 34 35 36class Generator(metaclass=_Generator): 37 """ 38 Generator converts a given syntax tree to the corresponding SQL string. 39 40 Args: 41 pretty: Whether to format the produced SQL string. 42 Default: False. 43 identify: Determines when an identifier should be quoted. Possible values are: 44 False (default): Never quote, except in cases where it's mandatory by the dialect. 45 True or 'always': Always quote. 46 'safe': Only quote identifiers that are case insensitive. 47 normalize: Whether to normalize identifiers to lowercase. 48 Default: False. 49 pad: The pad size in a formatted string. For example, this affects the indentation of 50 a projection in a query, relative to its nesting level. 51 Default: 2. 52 indent: The indentation size in a formatted string. For example, this affects the 53 indentation of subqueries and filters under a `WHERE` clause. 54 Default: 2. 55 normalize_functions: How to normalize function names. Possible values are: 56 "upper" or True (default): Convert names to uppercase. 57 "lower": Convert names to lowercase. 58 False: Disables function name normalization. 59 unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. 60 Default ErrorLevel.WARN. 61 max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. 62 This is only relevant if unsupported_level is ErrorLevel.RAISE. 63 Default: 3 64 leading_comma: Whether the comma is leading or trailing in select expressions. 65 This is only relevant when generating in pretty mode. 66 Default: False 67 max_text_width: The max number of characters in a segment before creating new lines in pretty mode. 68 The default is on the smaller end because the length only represents a segment and not the true 69 line length. 70 Default: 80 71 comments: Whether to preserve comments in the output SQL code. 72 Default: True 73 """ 74 75 TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = { 76 **JSON_PATH_PART_TRANSFORMS, 77 exp.AllowedValuesProperty: lambda self, 78 e: f"ALLOWED_VALUES {self.expressions(e, flat=True)}", 79 exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}", 80 exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}", 81 exp.CaseSpecificColumnConstraint: lambda _, 82 e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC", 83 exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}", 84 exp.CharacterSetProperty: lambda self, 85 e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}", 86 exp.ClusteredColumnConstraint: lambda self, 87 e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})", 88 exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}", 89 exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}", 90 exp.ConnectByRoot: lambda self, e: f"CONNECT_BY_ROOT {self.sql(e, 'this')}", 91 exp.CopyGrantsProperty: lambda *_: "COPY GRANTS", 92 exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}", 93 exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}", 94 exp.DynamicProperty: lambda *_: "DYNAMIC", 95 exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}", 96 exp.EphemeralColumnConstraint: lambda self, 97 e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}", 98 exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}", 99 exp.ExecuteAsProperty: lambda self, e: self.naked_property(e), 100 exp.ExternalProperty: lambda *_: "EXTERNAL", 101 exp.GlobalProperty: lambda *_: "GLOBAL", 102 exp.HeapProperty: lambda *_: "HEAP", 103 exp.IcebergProperty: lambda *_: "ICEBERG", 104 exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})", 105 exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}", 106 exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}", 107 exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}", 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.ProjectionPolicyColumnConstraint: lambda self, 123 e: f"PROJECTION POLICY {self.sql(e, 'this')}", 124 exp.RemoteWithConnectionModelProperty: lambda self, 125 e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}", 126 exp.ReturnsProperty: lambda self, e: ( 127 "RETURNS NULL ON NULL INPUT" if e.args.get("null") else self.naked_property(e) 128 ), 129 exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}", 130 exp.SecureProperty: lambda *_: "SECURE", 131 exp.SetConfigProperty: lambda self, e: self.sql(e, "this"), 132 exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET", 133 exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}", 134 exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}", 135 exp.SqlReadWriteProperty: lambda _, e: e.name, 136 exp.SqlSecurityProperty: lambda _, 137 e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}", 138 exp.StabilityProperty: lambda _, e: e.name, 139 exp.StrictProperty: lambda *_: "STRICT", 140 exp.TemporaryProperty: lambda *_: "TEMPORARY", 141 exp.TagColumnConstraint: lambda self, e: f"TAG ({self.expressions(e, flat=True)})", 142 exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}", 143 exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}", 144 exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}", 145 exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions), 146 exp.TransientProperty: lambda *_: "TRANSIENT", 147 exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE", 148 exp.UnloggedProperty: lambda *_: "UNLOGGED", 149 exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]), 150 exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}", 151 exp.VolatileProperty: lambda *_: "VOLATILE", 152 exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}", 153 exp.WithSchemaBindingProperty: lambda self, e: f"WITH SCHEMA {self.sql(e, 'this')}", 154 exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}", 155 } 156 157 # Whether null ordering is supported in order by 158 # True: Full Support, None: No support, False: No support in window specifications 159 NULL_ORDERING_SUPPORTED: t.Optional[bool] = True 160 161 # Whether ignore nulls is inside the agg or outside. 162 # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER 163 IGNORE_NULLS_IN_FUNC = False 164 165 # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported 166 LOCKING_READS_SUPPORTED = False 167 168 # Always do <set op> distinct or <set op> all 169 EXPLICIT_SET_OP = False 170 171 # Wrap derived values in parens, usually standard but spark doesn't support it 172 WRAP_DERIVED_VALUES = True 173 174 # Whether create function uses an AS before the RETURN 175 CREATE_FUNCTION_RETURN_AS = True 176 177 # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed 178 MATCHED_BY_SOURCE = True 179 180 # Whether the INTERVAL expression works only with values like '1 day' 181 SINGLE_STRING_INTERVAL = False 182 183 # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs 184 INTERVAL_ALLOWS_PLURAL_FORM = True 185 186 # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH") 187 LIMIT_FETCH = "ALL" 188 189 # Whether limit and fetch allows expresions or just limits 190 LIMIT_ONLY_LITERALS = False 191 192 # Whether a table is allowed to be renamed with a db 193 RENAME_TABLE_WITH_DB = True 194 195 # The separator for grouping sets and rollups 196 GROUPINGS_SEP = "," 197 198 # The string used for creating an index on a table 199 INDEX_ON = "ON" 200 201 # Whether join hints should be generated 202 JOIN_HINTS = True 203 204 # Whether table hints should be generated 205 TABLE_HINTS = True 206 207 # Whether query hints should be generated 208 QUERY_HINTS = True 209 210 # What kind of separator to use for query hints 211 QUERY_HINT_SEP = ", " 212 213 # Whether comparing against booleans (e.g. x IS TRUE) is supported 214 IS_BOOL_ALLOWED = True 215 216 # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement 217 DUPLICATE_KEY_UPDATE_WITH_SET = True 218 219 # Whether to generate the limit as TOP <value> instead of LIMIT <value> 220 LIMIT_IS_TOP = False 221 222 # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ... 223 RETURNING_END = True 224 225 # Whether to generate an unquoted value for EXTRACT's date part argument 226 EXTRACT_ALLOWS_QUOTES = True 227 228 # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax 229 TZ_TO_WITH_TIME_ZONE = False 230 231 # Whether the NVL2 function is supported 232 NVL2_SUPPORTED = True 233 234 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax 235 SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE") 236 237 # Whether VALUES statements can be used as derived tables. 238 # MySQL 5 and Redshift do not allow this, so when False, it will convert 239 # SELECT * VALUES into SELECT UNION 240 VALUES_AS_TABLE = True 241 242 # Whether the word COLUMN is included when adding a column with ALTER TABLE 243 ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True 244 245 # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery) 246 UNNEST_WITH_ORDINALITY = True 247 248 # Whether FILTER (WHERE cond) can be used for conditional aggregation 249 AGGREGATE_FILTER_SUPPORTED = True 250 251 # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds 252 SEMI_ANTI_JOIN_WITH_SIDE = True 253 254 # Whether to include the type of a computed column in the CREATE DDL 255 COMPUTED_COLUMN_WITH_TYPE = True 256 257 # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY 258 SUPPORTS_TABLE_COPY = True 259 260 # Whether parentheses are required around the table sample's expression 261 TABLESAMPLE_REQUIRES_PARENS = True 262 263 # Whether a table sample clause's size needs to be followed by the ROWS keyword 264 TABLESAMPLE_SIZE_IS_ROWS = True 265 266 # The keyword(s) to use when generating a sample clause 267 TABLESAMPLE_KEYWORDS = "TABLESAMPLE" 268 269 # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI 270 TABLESAMPLE_WITH_METHOD = True 271 272 # The keyword to use when specifying the seed of a sample clause 273 TABLESAMPLE_SEED_KEYWORD = "SEED" 274 275 # Whether COLLATE is a function instead of a binary operator 276 COLLATE_IS_FUNC = False 277 278 # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle) 279 DATA_TYPE_SPECIFIERS_ALLOWED = False 280 281 # Whether conditions require booleans WHERE x = 0 vs WHERE x 282 ENSURE_BOOLS = False 283 284 # Whether the "RECURSIVE" keyword is required when defining recursive CTEs 285 CTE_RECURSIVE_KEYWORD_REQUIRED = True 286 287 # Whether CONCAT requires >1 arguments 288 SUPPORTS_SINGLE_ARG_CONCAT = True 289 290 # Whether LAST_DAY function supports a date part argument 291 LAST_DAY_SUPPORTS_DATE_PART = True 292 293 # Whether named columns are allowed in table aliases 294 SUPPORTS_TABLE_ALIAS_COLUMNS = True 295 296 # Whether UNPIVOT aliases are Identifiers (False means they're Literals) 297 UNPIVOT_ALIASES_ARE_IDENTIFIERS = True 298 299 # What delimiter to use for separating JSON key/value pairs 300 JSON_KEY_VALUE_PAIR_SEP = ":" 301 302 # INSERT OVERWRITE TABLE x override 303 INSERT_OVERWRITE = " OVERWRITE TABLE" 304 305 # Whether the SELECT .. INTO syntax is used instead of CTAS 306 SUPPORTS_SELECT_INTO = False 307 308 # Whether UNLOGGED tables can be created 309 SUPPORTS_UNLOGGED_TABLES = False 310 311 # Whether the CREATE TABLE LIKE statement is supported 312 SUPPORTS_CREATE_TABLE_LIKE = True 313 314 # Whether the LikeProperty needs to be specified inside of the schema clause 315 LIKE_PROPERTY_INSIDE_SCHEMA = False 316 317 # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be 318 # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args 319 MULTI_ARG_DISTINCT = True 320 321 # Whether the JSON extraction operators expect a value of type JSON 322 JSON_TYPE_REQUIRED_FOR_EXTRACTION = False 323 324 # Whether bracketed keys like ["foo"] are supported in JSON paths 325 JSON_PATH_BRACKETED_KEY_SUPPORTED = True 326 327 # Whether to escape keys using single quotes in JSON paths 328 JSON_PATH_SINGLE_QUOTE_ESCAPE = False 329 330 # The JSONPathPart expressions supported by this dialect 331 SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy() 332 333 # Whether any(f(x) for x in array) can be implemented by this dialect 334 CAN_IMPLEMENT_ARRAY_ANY = False 335 336 # Whether the function TO_NUMBER is supported 337 SUPPORTS_TO_NUMBER = True 338 339 # Whether or not set op modifiers apply to the outer set op or select. 340 # SELECT * FROM x UNION SELECT * FROM y LIMIT 1 341 # True means limit 1 happens after the set op, False means it it happens on y. 342 SET_OP_MODIFIERS = True 343 344 # Whether parameters from COPY statement are wrapped in parentheses 345 COPY_PARAMS_ARE_WRAPPED = True 346 347 # Whether values of params are set with "=" token or empty space 348 COPY_PARAMS_EQ_REQUIRED = False 349 350 # Whether COPY statement has INTO keyword 351 COPY_HAS_INTO_KEYWORD = True 352 353 # Whether the conditional TRY(expression) function is supported 354 TRY_SUPPORTED = True 355 356 # Whether the UESCAPE syntax in unicode strings is supported 357 SUPPORTS_UESCAPE = True 358 359 # The keyword to use when generating a star projection with excluded columns 360 STAR_EXCEPT = "EXCEPT" 361 362 # The HEX function name 363 HEX_FUNC = "HEX" 364 365 # The keywords to use when prefixing & separating WITH based properties 366 WITH_PROPERTIES_PREFIX = "WITH" 367 368 # Whether to quote the generated expression of exp.JsonPath 369 QUOTE_JSON_PATH = True 370 371 # Whether the text pattern/fill (3rd) parameter of RPAD()/LPAD() is optional (defaults to space) 372 PAD_FILL_PATTERN_IS_REQUIRED = False 373 374 # The name to generate for the JSONPath expression. If `None`, only `this` will be generated 375 PARSE_JSON_NAME: t.Optional[str] = "PARSE_JSON" 376 377 TYPE_MAPPING = { 378 exp.DataType.Type.NCHAR: "CHAR", 379 exp.DataType.Type.NVARCHAR: "VARCHAR", 380 exp.DataType.Type.MEDIUMTEXT: "TEXT", 381 exp.DataType.Type.LONGTEXT: "TEXT", 382 exp.DataType.Type.TINYTEXT: "TEXT", 383 exp.DataType.Type.MEDIUMBLOB: "BLOB", 384 exp.DataType.Type.LONGBLOB: "BLOB", 385 exp.DataType.Type.TINYBLOB: "BLOB", 386 exp.DataType.Type.INET: "INET", 387 exp.DataType.Type.ROWVERSION: "VARBINARY", 388 } 389 390 TIME_PART_SINGULARS = { 391 "MICROSECONDS": "MICROSECOND", 392 "SECONDS": "SECOND", 393 "MINUTES": "MINUTE", 394 "HOURS": "HOUR", 395 "DAYS": "DAY", 396 "WEEKS": "WEEK", 397 "MONTHS": "MONTH", 398 "QUARTERS": "QUARTER", 399 "YEARS": "YEAR", 400 } 401 402 AFTER_HAVING_MODIFIER_TRANSFORMS = { 403 "cluster": lambda self, e: self.sql(e, "cluster"), 404 "distribute": lambda self, e: self.sql(e, "distribute"), 405 "sort": lambda self, e: self.sql(e, "sort"), 406 "windows": lambda self, e: ( 407 self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True) 408 if e.args.get("windows") 409 else "" 410 ), 411 "qualify": lambda self, e: self.sql(e, "qualify"), 412 } 413 414 TOKEN_MAPPING: t.Dict[TokenType, str] = {} 415 416 STRUCT_DELIMITER = ("<", ">") 417 418 PARAMETER_TOKEN = "@" 419 NAMED_PLACEHOLDER_TOKEN = ":" 420 421 PROPERTIES_LOCATION = { 422 exp.AllowedValuesProperty: exp.Properties.Location.POST_SCHEMA, 423 exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE, 424 exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA, 425 exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA, 426 exp.BackupProperty: exp.Properties.Location.POST_SCHEMA, 427 exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME, 428 exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA, 429 exp.ChecksumProperty: exp.Properties.Location.POST_NAME, 430 exp.CollateProperty: exp.Properties.Location.POST_SCHEMA, 431 exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA, 432 exp.Cluster: exp.Properties.Location.POST_SCHEMA, 433 exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA, 434 exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME, 435 exp.DataDeletionProperty: exp.Properties.Location.POST_SCHEMA, 436 exp.DefinerProperty: exp.Properties.Location.POST_CREATE, 437 exp.DictRange: exp.Properties.Location.POST_SCHEMA, 438 exp.DictProperty: exp.Properties.Location.POST_SCHEMA, 439 exp.DynamicProperty: exp.Properties.Location.POST_CREATE, 440 exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA, 441 exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA, 442 exp.EngineProperty: exp.Properties.Location.POST_SCHEMA, 443 exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA, 444 exp.ExternalProperty: exp.Properties.Location.POST_CREATE, 445 exp.FallbackProperty: exp.Properties.Location.POST_NAME, 446 exp.FileFormatProperty: exp.Properties.Location.POST_WITH, 447 exp.FreespaceProperty: exp.Properties.Location.POST_NAME, 448 exp.GlobalProperty: exp.Properties.Location.POST_CREATE, 449 exp.HeapProperty: exp.Properties.Location.POST_WITH, 450 exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA, 451 exp.IcebergProperty: exp.Properties.Location.POST_CREATE, 452 exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA, 453 exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME, 454 exp.JournalProperty: exp.Properties.Location.POST_NAME, 455 exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA, 456 exp.LikeProperty: exp.Properties.Location.POST_SCHEMA, 457 exp.LocationProperty: exp.Properties.Location.POST_SCHEMA, 458 exp.LockProperty: exp.Properties.Location.POST_SCHEMA, 459 exp.LockingProperty: exp.Properties.Location.POST_ALIAS, 460 exp.LogProperty: exp.Properties.Location.POST_NAME, 461 exp.MaterializedProperty: exp.Properties.Location.POST_CREATE, 462 exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME, 463 exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION, 464 exp.OnProperty: exp.Properties.Location.POST_SCHEMA, 465 exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION, 466 exp.Order: exp.Properties.Location.POST_SCHEMA, 467 exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA, 468 exp.PartitionedByProperty: exp.Properties.Location.POST_WITH, 469 exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA, 470 exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA, 471 exp.Property: exp.Properties.Location.POST_WITH, 472 exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA, 473 exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA, 474 exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA, 475 exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA, 476 exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA, 477 exp.SampleProperty: exp.Properties.Location.POST_SCHEMA, 478 exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA, 479 exp.SecureProperty: exp.Properties.Location.POST_CREATE, 480 exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA, 481 exp.Set: exp.Properties.Location.POST_SCHEMA, 482 exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA, 483 exp.SetProperty: exp.Properties.Location.POST_CREATE, 484 exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA, 485 exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION, 486 exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION, 487 exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA, 488 exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA, 489 exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE, 490 exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA, 491 exp.StrictProperty: exp.Properties.Location.POST_SCHEMA, 492 exp.TemporaryProperty: exp.Properties.Location.POST_CREATE, 493 exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA, 494 exp.TransientProperty: exp.Properties.Location.POST_CREATE, 495 exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA, 496 exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA, 497 exp.UnloggedProperty: exp.Properties.Location.POST_CREATE, 498 exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA, 499 exp.VolatileProperty: exp.Properties.Location.POST_CREATE, 500 exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION, 501 exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME, 502 exp.WithSchemaBindingProperty: exp.Properties.Location.POST_SCHEMA, 503 exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA, 504 } 505 506 # Keywords that can't be used as unquoted identifier names 507 RESERVED_KEYWORDS: t.Set[str] = set() 508 509 # Expressions whose comments are separated from them for better formatting 510 WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 511 exp.Command, 512 exp.Create, 513 exp.Delete, 514 exp.Drop, 515 exp.From, 516 exp.Insert, 517 exp.Join, 518 exp.Select, 519 exp.SetOperation, 520 exp.Update, 521 exp.Where, 522 exp.With, 523 ) 524 525 # Expressions that should not have their comments generated in maybe_comment 526 EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 527 exp.Binary, 528 exp.SetOperation, 529 ) 530 531 # Expressions that can remain unwrapped when appearing in the context of an INTERVAL 532 UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = ( 533 exp.Column, 534 exp.Literal, 535 exp.Neg, 536 exp.Paren, 537 ) 538 539 PARAMETERIZABLE_TEXT_TYPES = { 540 exp.DataType.Type.NVARCHAR, 541 exp.DataType.Type.VARCHAR, 542 exp.DataType.Type.CHAR, 543 exp.DataType.Type.NCHAR, 544 } 545 546 # Expressions that need to have all CTEs under them bubbled up to them 547 EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set() 548 549 SENTINEL_LINE_BREAK = "__SQLGLOT__LB__" 550 551 __slots__ = ( 552 "pretty", 553 "identify", 554 "normalize", 555 "pad", 556 "_indent", 557 "normalize_functions", 558 "unsupported_level", 559 "max_unsupported", 560 "leading_comma", 561 "max_text_width", 562 "comments", 563 "dialect", 564 "unsupported_messages", 565 "_escaped_quote_end", 566 "_escaped_identifier_end", 567 "_next_name", 568 ) 569 570 def __init__( 571 self, 572 pretty: t.Optional[bool] = None, 573 identify: str | bool = False, 574 normalize: bool = False, 575 pad: int = 2, 576 indent: int = 2, 577 normalize_functions: t.Optional[str | bool] = None, 578 unsupported_level: ErrorLevel = ErrorLevel.WARN, 579 max_unsupported: int = 3, 580 leading_comma: bool = False, 581 max_text_width: int = 80, 582 comments: bool = True, 583 dialect: DialectType = None, 584 ): 585 import sqlglot 586 from sqlglot.dialects import Dialect 587 588 self.pretty = pretty if pretty is not None else sqlglot.pretty 589 self.identify = identify 590 self.normalize = normalize 591 self.pad = pad 592 self._indent = indent 593 self.unsupported_level = unsupported_level 594 self.max_unsupported = max_unsupported 595 self.leading_comma = leading_comma 596 self.max_text_width = max_text_width 597 self.comments = comments 598 self.dialect = Dialect.get_or_raise(dialect) 599 600 # This is both a Dialect property and a Generator argument, so we prioritize the latter 601 self.normalize_functions = ( 602 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 603 ) 604 605 self.unsupported_messages: t.List[str] = [] 606 self._escaped_quote_end: str = ( 607 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 608 ) 609 self._escaped_identifier_end: str = ( 610 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 611 ) 612 613 self._next_name = name_sequence("_t") 614 615 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 616 """ 617 Generates the SQL string corresponding to the given syntax tree. 618 619 Args: 620 expression: The syntax tree. 621 copy: Whether to copy the expression. The generator performs mutations so 622 it is safer to copy. 623 624 Returns: 625 The SQL string corresponding to `expression`. 626 """ 627 if copy: 628 expression = expression.copy() 629 630 expression = self.preprocess(expression) 631 632 self.unsupported_messages = [] 633 sql = self.sql(expression).strip() 634 635 if self.pretty: 636 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 637 638 if self.unsupported_level == ErrorLevel.IGNORE: 639 return sql 640 641 if self.unsupported_level == ErrorLevel.WARN: 642 for msg in self.unsupported_messages: 643 logger.warning(msg) 644 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 645 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 646 647 return sql 648 649 def preprocess(self, expression: exp.Expression) -> exp.Expression: 650 """Apply generic preprocessing transformations to a given expression.""" 651 if ( 652 not expression.parent 653 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 654 and any(node.parent is not expression for node in expression.find_all(exp.With)) 655 ): 656 from sqlglot.transforms import move_ctes_to_top_level 657 658 expression = move_ctes_to_top_level(expression) 659 660 if self.ENSURE_BOOLS: 661 from sqlglot.transforms import ensure_bools 662 663 expression = ensure_bools(expression) 664 665 return expression 666 667 def unsupported(self, message: str) -> None: 668 if self.unsupported_level == ErrorLevel.IMMEDIATE: 669 raise UnsupportedError(message) 670 self.unsupported_messages.append(message) 671 672 def sep(self, sep: str = " ") -> str: 673 return f"{sep.strip()}\n" if self.pretty else sep 674 675 def seg(self, sql: str, sep: str = " ") -> str: 676 return f"{self.sep(sep)}{sql}" 677 678 def pad_comment(self, comment: str) -> str: 679 comment = " " + comment if comment[0].strip() else comment 680 comment = comment + " " if comment[-1].strip() else comment 681 return comment 682 683 def maybe_comment( 684 self, 685 sql: str, 686 expression: t.Optional[exp.Expression] = None, 687 comments: t.Optional[t.List[str]] = None, 688 separated: bool = False, 689 ) -> str: 690 comments = ( 691 ((expression and expression.comments) if comments is None else comments) # type: ignore 692 if self.comments 693 else None 694 ) 695 696 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 697 return sql 698 699 comments_sql = " ".join( 700 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 701 ) 702 703 if not comments_sql: 704 return sql 705 706 comments_sql = self._replace_line_breaks(comments_sql) 707 708 if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS): 709 return ( 710 f"{self.sep()}{comments_sql}{sql}" 711 if not sql or sql[0].isspace() 712 else f"{comments_sql}{self.sep()}{sql}" 713 ) 714 715 return f"{sql} {comments_sql}" 716 717 def wrap(self, expression: exp.Expression | str) -> str: 718 this_sql = ( 719 self.sql(expression) 720 if isinstance(expression, exp.UNWRAPPED_QUERIES) 721 else self.sql(expression, "this") 722 ) 723 if not this_sql: 724 return "()" 725 726 this_sql = self.indent(this_sql, level=1, pad=0) 727 return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}" 728 729 def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str: 730 original = self.identify 731 self.identify = False 732 result = func(*args, **kwargs) 733 self.identify = original 734 return result 735 736 def normalize_func(self, name: str) -> str: 737 if self.normalize_functions == "upper" or self.normalize_functions is True: 738 return name.upper() 739 if self.normalize_functions == "lower": 740 return name.lower() 741 return name 742 743 def indent( 744 self, 745 sql: str, 746 level: int = 0, 747 pad: t.Optional[int] = None, 748 skip_first: bool = False, 749 skip_last: bool = False, 750 ) -> str: 751 if not self.pretty or not sql: 752 return sql 753 754 pad = self.pad if pad is None else pad 755 lines = sql.split("\n") 756 757 return "\n".join( 758 ( 759 line 760 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 761 else f"{' ' * (level * self._indent + pad)}{line}" 762 ) 763 for i, line in enumerate(lines) 764 ) 765 766 def sql( 767 self, 768 expression: t.Optional[str | exp.Expression], 769 key: t.Optional[str] = None, 770 comment: bool = True, 771 ) -> str: 772 if not expression: 773 return "" 774 775 if isinstance(expression, str): 776 return expression 777 778 if key: 779 value = expression.args.get(key) 780 if value: 781 return self.sql(value) 782 return "" 783 784 transform = self.TRANSFORMS.get(expression.__class__) 785 786 if callable(transform): 787 sql = transform(self, expression) 788 elif isinstance(expression, exp.Expression): 789 exp_handler_name = f"{expression.key}_sql" 790 791 if hasattr(self, exp_handler_name): 792 sql = getattr(self, exp_handler_name)(expression) 793 elif isinstance(expression, exp.Func): 794 sql = self.function_fallback_sql(expression) 795 elif isinstance(expression, exp.Property): 796 sql = self.property_sql(expression) 797 else: 798 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 799 else: 800 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 801 802 return self.maybe_comment(sql, expression) if self.comments and comment else sql 803 804 def uncache_sql(self, expression: exp.Uncache) -> str: 805 table = self.sql(expression, "this") 806 exists_sql = " IF EXISTS" if expression.args.get("exists") else "" 807 return f"UNCACHE TABLE{exists_sql} {table}" 808 809 def cache_sql(self, expression: exp.Cache) -> str: 810 lazy = " LAZY" if expression.args.get("lazy") else "" 811 table = self.sql(expression, "this") 812 options = expression.args.get("options") 813 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 814 sql = self.sql(expression, "expression") 815 sql = f" AS{self.sep()}{sql}" if sql else "" 816 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 817 return self.prepend_ctes(expression, sql) 818 819 def characterset_sql(self, expression: exp.CharacterSet) -> str: 820 if isinstance(expression.parent, exp.Cast): 821 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 822 default = "DEFAULT " if expression.args.get("default") else "" 823 return f"{default}CHARACTER SET={self.sql(expression, 'this')}" 824 825 def column_parts(self, expression: exp.Column) -> str: 826 return ".".join( 827 self.sql(part) 828 for part in ( 829 expression.args.get("catalog"), 830 expression.args.get("db"), 831 expression.args.get("table"), 832 expression.args.get("this"), 833 ) 834 if part 835 ) 836 837 def column_sql(self, expression: exp.Column) -> str: 838 join_mark = " (+)" if expression.args.get("join_mark") else "" 839 840 if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS: 841 join_mark = "" 842 self.unsupported("Outer join syntax using the (+) operator is not supported.") 843 844 return f"{self.column_parts(expression)}{join_mark}" 845 846 def columnposition_sql(self, expression: exp.ColumnPosition) -> str: 847 this = self.sql(expression, "this") 848 this = f" {this}" if this else "" 849 position = self.sql(expression, "position") 850 return f"{position}{this}" 851 852 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 853 column = self.sql(expression, "this") 854 kind = self.sql(expression, "kind") 855 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 856 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 857 kind = f"{sep}{kind}" if kind else "" 858 constraints = f" {constraints}" if constraints else "" 859 position = self.sql(expression, "position") 860 position = f" {position}" if position else "" 861 862 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 863 kind = "" 864 865 return f"{exists}{column}{kind}{constraints}{position}" 866 867 def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str: 868 this = self.sql(expression, "this") 869 kind_sql = self.sql(expression, "kind").strip() 870 return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql 871 872 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 873 this = self.sql(expression, "this") 874 if expression.args.get("not_null"): 875 persisted = " PERSISTED NOT NULL" 876 elif expression.args.get("persisted"): 877 persisted = " PERSISTED" 878 else: 879 persisted = "" 880 return f"AS {this}{persisted}" 881 882 def autoincrementcolumnconstraint_sql(self, _) -> str: 883 return self.token_sql(TokenType.AUTO_INCREMENT) 884 885 def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str: 886 if isinstance(expression.this, list): 887 this = self.wrap(self.expressions(expression, key="this", flat=True)) 888 else: 889 this = self.sql(expression, "this") 890 891 return f"COMPRESS {this}" 892 893 def generatedasidentitycolumnconstraint_sql( 894 self, expression: exp.GeneratedAsIdentityColumnConstraint 895 ) -> str: 896 this = "" 897 if expression.this is not None: 898 on_null = " ON NULL" if expression.args.get("on_null") else "" 899 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 900 901 start = expression.args.get("start") 902 start = f"START WITH {start}" if start else "" 903 increment = expression.args.get("increment") 904 increment = f" INCREMENT BY {increment}" if increment else "" 905 minvalue = expression.args.get("minvalue") 906 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 907 maxvalue = expression.args.get("maxvalue") 908 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 909 cycle = expression.args.get("cycle") 910 cycle_sql = "" 911 912 if cycle is not None: 913 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 914 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 915 916 sequence_opts = "" 917 if start or increment or cycle_sql: 918 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 919 sequence_opts = f" ({sequence_opts.strip()})" 920 921 expr = self.sql(expression, "expression") 922 expr = f"({expr})" if expr else "IDENTITY" 923 924 return f"GENERATED{this} AS {expr}{sequence_opts}" 925 926 def generatedasrowcolumnconstraint_sql( 927 self, expression: exp.GeneratedAsRowColumnConstraint 928 ) -> str: 929 start = "START" if expression.args.get("start") else "END" 930 hidden = " HIDDEN" if expression.args.get("hidden") else "" 931 return f"GENERATED ALWAYS AS ROW {start}{hidden}" 932 933 def periodforsystemtimeconstraint_sql( 934 self, expression: exp.PeriodForSystemTimeConstraint 935 ) -> str: 936 return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})" 937 938 def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str: 939 return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL" 940 941 def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str: 942 return f"AS {self.sql(expression, 'this')}" 943 944 def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str: 945 desc = expression.args.get("desc") 946 if desc is not None: 947 return f"PRIMARY KEY{' DESC' if desc else ' ASC'}" 948 return "PRIMARY KEY" 949 950 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 951 this = self.sql(expression, "this") 952 this = f" {this}" if this else "" 953 index_type = expression.args.get("index_type") 954 index_type = f" USING {index_type}" if index_type else "" 955 on_conflict = self.sql(expression, "on_conflict") 956 on_conflict = f" {on_conflict}" if on_conflict else "" 957 nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else "" 958 return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}" 959 960 def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str: 961 return self.sql(expression, "this") 962 963 def create_sql(self, expression: exp.Create) -> str: 964 kind = self.sql(expression, "kind") 965 properties = expression.args.get("properties") 966 properties_locs = self.locate_properties(properties) if properties else defaultdict() 967 968 this = self.createable_sql(expression, properties_locs) 969 970 properties_sql = "" 971 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 972 exp.Properties.Location.POST_WITH 973 ): 974 properties_sql = self.sql( 975 exp.Properties( 976 expressions=[ 977 *properties_locs[exp.Properties.Location.POST_SCHEMA], 978 *properties_locs[exp.Properties.Location.POST_WITH], 979 ] 980 ) 981 ) 982 983 if properties_locs.get(exp.Properties.Location.POST_SCHEMA): 984 properties_sql = self.sep() + properties_sql 985 elif not self.pretty: 986 # Standalone POST_WITH properties need a leading whitespace in non-pretty mode 987 properties_sql = f" {properties_sql}" 988 989 begin = " BEGIN" if expression.args.get("begin") else "" 990 end = " END" if expression.args.get("end") else "" 991 992 expression_sql = self.sql(expression, "expression") 993 if expression_sql: 994 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 995 996 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 997 postalias_props_sql = "" 998 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 999 postalias_props_sql = self.properties( 1000 exp.Properties( 1001 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 1002 ), 1003 wrapped=False, 1004 ) 1005 postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else "" 1006 expression_sql = f" AS{postalias_props_sql}{expression_sql}" 1007 1008 postindex_props_sql = "" 1009 if properties_locs.get(exp.Properties.Location.POST_INDEX): 1010 postindex_props_sql = self.properties( 1011 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 1012 wrapped=False, 1013 prefix=" ", 1014 ) 1015 1016 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 1017 indexes = f" {indexes}" if indexes else "" 1018 index_sql = indexes + postindex_props_sql 1019 1020 replace = " OR REPLACE" if expression.args.get("replace") else "" 1021 unique = " UNIQUE" if expression.args.get("unique") else "" 1022 1023 clustered = expression.args.get("clustered") 1024 if clustered is None: 1025 clustered_sql = "" 1026 elif clustered: 1027 clustered_sql = " CLUSTERED COLUMNSTORE" 1028 else: 1029 clustered_sql = " NONCLUSTERED COLUMNSTORE" 1030 1031 postcreate_props_sql = "" 1032 if properties_locs.get(exp.Properties.Location.POST_CREATE): 1033 postcreate_props_sql = self.properties( 1034 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 1035 sep=" ", 1036 prefix=" ", 1037 wrapped=False, 1038 ) 1039 1040 modifiers = "".join((clustered_sql, replace, unique, postcreate_props_sql)) 1041 1042 postexpression_props_sql = "" 1043 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 1044 postexpression_props_sql = self.properties( 1045 exp.Properties( 1046 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 1047 ), 1048 sep=" ", 1049 prefix=" ", 1050 wrapped=False, 1051 ) 1052 1053 concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else "" 1054 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 1055 no_schema_binding = ( 1056 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 1057 ) 1058 1059 clone = self.sql(expression, "clone") 1060 clone = f" {clone}" if clone else "" 1061 1062 expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 1063 return self.prepend_ctes(expression, expression_sql) 1064 1065 def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str: 1066 start = self.sql(expression, "start") 1067 start = f"START WITH {start}" if start else "" 1068 increment = self.sql(expression, "increment") 1069 increment = f" INCREMENT BY {increment}" if increment else "" 1070 minvalue = self.sql(expression, "minvalue") 1071 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 1072 maxvalue = self.sql(expression, "maxvalue") 1073 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 1074 owned = self.sql(expression, "owned") 1075 owned = f" OWNED BY {owned}" if owned else "" 1076 1077 cache = expression.args.get("cache") 1078 if cache is None: 1079 cache_str = "" 1080 elif cache is True: 1081 cache_str = " CACHE" 1082 else: 1083 cache_str = f" CACHE {cache}" 1084 1085 options = self.expressions(expression, key="options", flat=True, sep=" ") 1086 options = f" {options}" if options else "" 1087 1088 return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip() 1089 1090 def clone_sql(self, expression: exp.Clone) -> str: 1091 this = self.sql(expression, "this") 1092 shallow = "SHALLOW " if expression.args.get("shallow") else "" 1093 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 1094 return f"{shallow}{keyword} {this}" 1095 1096 def describe_sql(self, expression: exp.Describe) -> str: 1097 style = expression.args.get("style") 1098 style = f" {style}" if style else "" 1099 return f"DESCRIBE{style} {self.sql(expression, 'this')}" 1100 1101 def heredoc_sql(self, expression: exp.Heredoc) -> str: 1102 tag = self.sql(expression, "tag") 1103 return f"${tag}${self.sql(expression, 'this')}${tag}$" 1104 1105 def prepend_ctes(self, expression: exp.Expression, sql: str) -> str: 1106 with_ = self.sql(expression, "with") 1107 if with_: 1108 sql = f"{with_}{self.sep()}{sql}" 1109 return sql 1110 1111 def with_sql(self, expression: exp.With) -> str: 1112 sql = self.expressions(expression, flat=True) 1113 recursive = ( 1114 "RECURSIVE " 1115 if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive") 1116 else "" 1117 ) 1118 1119 return f"WITH {recursive}{sql}" 1120 1121 def cte_sql(self, expression: exp.CTE) -> str: 1122 alias = self.sql(expression, "alias") 1123 1124 materialized = expression.args.get("materialized") 1125 if materialized is False: 1126 materialized = "NOT MATERIALIZED " 1127 elif materialized: 1128 materialized = "MATERIALIZED " 1129 1130 return f"{alias} AS {materialized or ''}{self.wrap(expression)}" 1131 1132 def tablealias_sql(self, expression: exp.TableAlias) -> str: 1133 alias = self.sql(expression, "this") 1134 columns = self.expressions(expression, key="columns", flat=True) 1135 columns = f"({columns})" if columns else "" 1136 1137 if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS: 1138 columns = "" 1139 self.unsupported("Named columns are not supported in table alias.") 1140 1141 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 1142 alias = self._next_name() 1143 1144 return f"{alias}{columns}" 1145 1146 def bitstring_sql(self, expression: exp.BitString) -> str: 1147 this = self.sql(expression, "this") 1148 if self.dialect.BIT_START: 1149 return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}" 1150 return f"{int(this, 2)}" 1151 1152 def hexstring_sql(self, expression: exp.HexString) -> str: 1153 this = self.sql(expression, "this") 1154 if self.dialect.HEX_START: 1155 return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}" 1156 return f"{int(this, 16)}" 1157 1158 def bytestring_sql(self, expression: exp.ByteString) -> str: 1159 this = self.sql(expression, "this") 1160 if self.dialect.BYTE_START: 1161 return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}" 1162 return this 1163 1164 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 1165 this = self.sql(expression, "this") 1166 escape = expression.args.get("escape") 1167 1168 if self.dialect.UNICODE_START: 1169 escape_substitute = r"\\\1" 1170 left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END 1171 else: 1172 escape_substitute = r"\\u\1" 1173 left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END 1174 1175 if escape: 1176 escape_pattern = re.compile(rf"{escape.name}(\d+)") 1177 escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else "" 1178 else: 1179 escape_pattern = ESCAPED_UNICODE_RE 1180 escape_sql = "" 1181 1182 if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE): 1183 this = escape_pattern.sub(escape_substitute, this) 1184 1185 return f"{left_quote}{this}{right_quote}{escape_sql}" 1186 1187 def rawstring_sql(self, expression: exp.RawString) -> str: 1188 string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False) 1189 return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}" 1190 1191 def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str: 1192 this = self.sql(expression, "this") 1193 specifier = self.sql(expression, "expression") 1194 specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else "" 1195 return f"{this}{specifier}" 1196 1197 def datatype_sql(self, expression: exp.DataType) -> str: 1198 type_value = expression.this 1199 1200 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 1201 type_sql = self.sql(expression, "kind") 1202 else: 1203 type_sql = ( 1204 self.TYPE_MAPPING.get(type_value, type_value.value) 1205 if isinstance(type_value, exp.DataType.Type) 1206 else type_value 1207 ) 1208 1209 nested = "" 1210 interior = self.expressions(expression, flat=True) 1211 values = "" 1212 1213 if interior: 1214 if expression.args.get("nested"): 1215 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 1216 if expression.args.get("values") is not None: 1217 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 1218 values = self.expressions(expression, key="values", flat=True) 1219 values = f"{delimiters[0]}{values}{delimiters[1]}" 1220 elif type_value == exp.DataType.Type.INTERVAL: 1221 nested = f" {interior}" 1222 else: 1223 nested = f"({interior})" 1224 1225 type_sql = f"{type_sql}{nested}{values}" 1226 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 1227 exp.DataType.Type.TIMETZ, 1228 exp.DataType.Type.TIMESTAMPTZ, 1229 ): 1230 type_sql = f"{type_sql} WITH TIME ZONE" 1231 1232 return type_sql 1233 1234 def directory_sql(self, expression: exp.Directory) -> str: 1235 local = "LOCAL " if expression.args.get("local") else "" 1236 row_format = self.sql(expression, "row_format") 1237 row_format = f" {row_format}" if row_format else "" 1238 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}" 1239 1240 def delete_sql(self, expression: exp.Delete) -> str: 1241 this = self.sql(expression, "this") 1242 this = f" FROM {this}" if this else "" 1243 using = self.sql(expression, "using") 1244 using = f" USING {using}" if using else "" 1245 where = self.sql(expression, "where") 1246 returning = self.sql(expression, "returning") 1247 limit = self.sql(expression, "limit") 1248 tables = self.expressions(expression, key="tables") 1249 tables = f" {tables}" if tables else "" 1250 if self.RETURNING_END: 1251 expression_sql = f"{this}{using}{where}{returning}{limit}" 1252 else: 1253 expression_sql = f"{returning}{this}{using}{where}{limit}" 1254 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}") 1255 1256 def drop_sql(self, expression: exp.Drop) -> str: 1257 this = self.sql(expression, "this") 1258 expressions = self.expressions(expression, flat=True) 1259 expressions = f" ({expressions})" if expressions else "" 1260 kind = expression.args["kind"] 1261 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1262 on_cluster = self.sql(expression, "cluster") 1263 on_cluster = f" {on_cluster}" if on_cluster else "" 1264 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1265 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1266 cascade = " CASCADE" if expression.args.get("cascade") else "" 1267 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1268 purge = " PURGE" if expression.args.get("purge") else "" 1269 return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}" 1270 1271 def except_sql(self, expression: exp.Except) -> str: 1272 return self.set_operations(expression) 1273 1274 def except_op(self, expression: exp.Except) -> str: 1275 return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}" 1276 1277 def fetch_sql(self, expression: exp.Fetch) -> str: 1278 direction = expression.args.get("direction") 1279 direction = f" {direction}" if direction else "" 1280 count = expression.args.get("count") 1281 count = f" {count}" if count else "" 1282 if expression.args.get("percent"): 1283 count = f"{count} PERCENT" 1284 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1285 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}" 1286 1287 def filter_sql(self, expression: exp.Filter) -> str: 1288 if self.AGGREGATE_FILTER_SUPPORTED: 1289 this = self.sql(expression, "this") 1290 where = self.sql(expression, "expression").strip() 1291 return f"{this} FILTER({where})" 1292 1293 agg = expression.this 1294 agg_arg = agg.this 1295 cond = expression.expression.this 1296 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1297 return self.sql(agg) 1298 1299 def hint_sql(self, expression: exp.Hint) -> str: 1300 if not self.QUERY_HINTS: 1301 self.unsupported("Hints are not supported") 1302 return "" 1303 1304 return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */" 1305 1306 def indexparameters_sql(self, expression: exp.IndexParameters) -> str: 1307 using = self.sql(expression, "using") 1308 using = f" USING {using}" if using else "" 1309 columns = self.expressions(expression, key="columns", flat=True) 1310 columns = f"({columns})" if columns else "" 1311 partition_by = self.expressions(expression, key="partition_by", flat=True) 1312 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1313 where = self.sql(expression, "where") 1314 include = self.expressions(expression, key="include", flat=True) 1315 if include: 1316 include = f" INCLUDE ({include})" 1317 with_storage = self.expressions(expression, key="with_storage", flat=True) 1318 with_storage = f" WITH ({with_storage})" if with_storage else "" 1319 tablespace = self.sql(expression, "tablespace") 1320 tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else "" 1321 on = self.sql(expression, "on") 1322 on = f" ON {on}" if on else "" 1323 1324 return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}" 1325 1326 def index_sql(self, expression: exp.Index) -> str: 1327 unique = "UNIQUE " if expression.args.get("unique") else "" 1328 primary = "PRIMARY " if expression.args.get("primary") else "" 1329 amp = "AMP " if expression.args.get("amp") else "" 1330 name = self.sql(expression, "this") 1331 name = f"{name} " if name else "" 1332 table = self.sql(expression, "table") 1333 table = f"{self.INDEX_ON} {table}" if table else "" 1334 1335 index = "INDEX " if not table else "" 1336 1337 params = self.sql(expression, "params") 1338 return f"{unique}{primary}{amp}{index}{name}{table}{params}" 1339 1340 def identifier_sql(self, expression: exp.Identifier) -> str: 1341 text = expression.name 1342 lower = text.lower() 1343 text = lower if self.normalize and not expression.quoted else text 1344 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1345 if ( 1346 expression.quoted 1347 or self.dialect.can_identify(text, self.identify) 1348 or lower in self.RESERVED_KEYWORDS 1349 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1350 ): 1351 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1352 return text 1353 1354 def hex_sql(self, expression: exp.Hex) -> str: 1355 text = self.func(self.HEX_FUNC, self.sql(expression, "this")) 1356 if self.dialect.HEX_LOWERCASE: 1357 text = self.func("LOWER", text) 1358 1359 return text 1360 1361 def lowerhex_sql(self, expression: exp.LowerHex) -> str: 1362 text = self.func(self.HEX_FUNC, self.sql(expression, "this")) 1363 if not self.dialect.HEX_LOWERCASE: 1364 text = self.func("LOWER", text) 1365 return text 1366 1367 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1368 input_format = self.sql(expression, "input_format") 1369 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1370 output_format = self.sql(expression, "output_format") 1371 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1372 return self.sep().join((input_format, output_format)) 1373 1374 def national_sql(self, expression: exp.National, prefix: str = "N") -> str: 1375 string = self.sql(exp.Literal.string(expression.name)) 1376 return f"{prefix}{string}" 1377 1378 def partition_sql(self, expression: exp.Partition) -> str: 1379 return f"PARTITION({self.expressions(expression, flat=True)})" 1380 1381 def properties_sql(self, expression: exp.Properties) -> str: 1382 root_properties = [] 1383 with_properties = [] 1384 1385 for p in expression.expressions: 1386 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1387 if p_loc == exp.Properties.Location.POST_WITH: 1388 with_properties.append(p) 1389 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1390 root_properties.append(p) 1391 1392 root_props = self.root_properties(exp.Properties(expressions=root_properties)) 1393 with_props = self.with_properties(exp.Properties(expressions=with_properties)) 1394 1395 if root_props and with_props and not self.pretty: 1396 with_props = " " + with_props 1397 1398 return root_props + with_props 1399 1400 def root_properties(self, properties: exp.Properties) -> str: 1401 if properties.expressions: 1402 return self.expressions(properties, indent=False, sep=" ") 1403 return "" 1404 1405 def properties( 1406 self, 1407 properties: exp.Properties, 1408 prefix: str = "", 1409 sep: str = ", ", 1410 suffix: str = "", 1411 wrapped: bool = True, 1412 ) -> str: 1413 if properties.expressions: 1414 expressions = self.expressions(properties, sep=sep, indent=False) 1415 if expressions: 1416 expressions = self.wrap(expressions) if wrapped else expressions 1417 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1418 return "" 1419 1420 def with_properties(self, properties: exp.Properties) -> str: 1421 return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep="")) 1422 1423 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1424 properties_locs = defaultdict(list) 1425 for p in properties.expressions: 1426 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1427 if p_loc != exp.Properties.Location.UNSUPPORTED: 1428 properties_locs[p_loc].append(p) 1429 else: 1430 self.unsupported(f"Unsupported property {p.key}") 1431 1432 return properties_locs 1433 1434 def property_name(self, expression: exp.Property, string_key: bool = False) -> str: 1435 if isinstance(expression.this, exp.Dot): 1436 return self.sql(expression, "this") 1437 return f"'{expression.name}'" if string_key else expression.name 1438 1439 def property_sql(self, expression: exp.Property) -> str: 1440 property_cls = expression.__class__ 1441 if property_cls == exp.Property: 1442 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1443 1444 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1445 if not property_name: 1446 self.unsupported(f"Unsupported property {expression.key}") 1447 1448 return f"{property_name}={self.sql(expression, 'this')}" 1449 1450 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1451 if self.SUPPORTS_CREATE_TABLE_LIKE: 1452 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1453 options = f" {options}" if options else "" 1454 1455 like = f"LIKE {self.sql(expression, 'this')}{options}" 1456 if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema): 1457 like = f"({like})" 1458 1459 return like 1460 1461 if expression.expressions: 1462 self.unsupported("Transpilation of LIKE property options is unsupported") 1463 1464 select = exp.select("*").from_(expression.this).limit(0) 1465 return f"AS {self.sql(select)}" 1466 1467 def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str: 1468 no = "NO " if expression.args.get("no") else "" 1469 protection = " PROTECTION" if expression.args.get("protection") else "" 1470 return f"{no}FALLBACK{protection}" 1471 1472 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1473 no = "NO " if expression.args.get("no") else "" 1474 local = expression.args.get("local") 1475 local = f"{local} " if local else "" 1476 dual = "DUAL " if expression.args.get("dual") else "" 1477 before = "BEFORE " if expression.args.get("before") else "" 1478 after = "AFTER " if expression.args.get("after") else "" 1479 return f"{no}{local}{dual}{before}{after}JOURNAL" 1480 1481 def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str: 1482 freespace = self.sql(expression, "this") 1483 percent = " PERCENT" if expression.args.get("percent") else "" 1484 return f"FREESPACE={freespace}{percent}" 1485 1486 def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str: 1487 if expression.args.get("default"): 1488 property = "DEFAULT" 1489 elif expression.args.get("on"): 1490 property = "ON" 1491 else: 1492 property = "OFF" 1493 return f"CHECKSUM={property}" 1494 1495 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1496 if expression.args.get("no"): 1497 return "NO MERGEBLOCKRATIO" 1498 if expression.args.get("default"): 1499 return "DEFAULT MERGEBLOCKRATIO" 1500 1501 percent = " PERCENT" if expression.args.get("percent") else "" 1502 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}" 1503 1504 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1505 default = expression.args.get("default") 1506 minimum = expression.args.get("minimum") 1507 maximum = expression.args.get("maximum") 1508 if default or minimum or maximum: 1509 if default: 1510 prop = "DEFAULT" 1511 elif minimum: 1512 prop = "MINIMUM" 1513 else: 1514 prop = "MAXIMUM" 1515 return f"{prop} DATABLOCKSIZE" 1516 units = expression.args.get("units") 1517 units = f" {units}" if units else "" 1518 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}" 1519 1520 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1521 autotemp = expression.args.get("autotemp") 1522 always = expression.args.get("always") 1523 default = expression.args.get("default") 1524 manual = expression.args.get("manual") 1525 never = expression.args.get("never") 1526 1527 if autotemp is not None: 1528 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1529 elif always: 1530 prop = "ALWAYS" 1531 elif default: 1532 prop = "DEFAULT" 1533 elif manual: 1534 prop = "MANUAL" 1535 elif never: 1536 prop = "NEVER" 1537 return f"BLOCKCOMPRESSION={prop}" 1538 1539 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1540 no = expression.args.get("no") 1541 no = " NO" if no else "" 1542 concurrent = expression.args.get("concurrent") 1543 concurrent = " CONCURRENT" if concurrent else "" 1544 target = self.sql(expression, "target") 1545 target = f" {target}" if target else "" 1546 return f"WITH{no}{concurrent} ISOLATED LOADING{target}" 1547 1548 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1549 if isinstance(expression.this, list): 1550 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1551 if expression.this: 1552 modulus = self.sql(expression, "this") 1553 remainder = self.sql(expression, "expression") 1554 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1555 1556 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1557 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1558 return f"FROM ({from_expressions}) TO ({to_expressions})" 1559 1560 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1561 this = self.sql(expression, "this") 1562 1563 for_values_or_default = expression.expression 1564 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1565 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1566 else: 1567 for_values_or_default = " DEFAULT" 1568 1569 return f"PARTITION OF {this}{for_values_or_default}" 1570 1571 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1572 kind = expression.args.get("kind") 1573 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1574 for_or_in = expression.args.get("for_or_in") 1575 for_or_in = f" {for_or_in}" if for_or_in else "" 1576 lock_type = expression.args.get("lock_type") 1577 override = " OVERRIDE" if expression.args.get("override") else "" 1578 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}" 1579 1580 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1581 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1582 statistics = expression.args.get("statistics") 1583 statistics_sql = "" 1584 if statistics is not None: 1585 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1586 return f"{data_sql}{statistics_sql}" 1587 1588 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1589 this = self.sql(expression, "this") 1590 this = f"HISTORY_TABLE={this}" if this else "" 1591 data_consistency: t.Optional[str] = self.sql(expression, "data_consistency") 1592 data_consistency = ( 1593 f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None 1594 ) 1595 retention_period: t.Optional[str] = self.sql(expression, "retention_period") 1596 retention_period = ( 1597 f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None 1598 ) 1599 1600 if this: 1601 on_sql = self.func("ON", this, data_consistency, retention_period) 1602 else: 1603 on_sql = "ON" if expression.args.get("on") else "OFF" 1604 1605 sql = f"SYSTEM_VERSIONING={on_sql}" 1606 1607 return f"WITH({sql})" if expression.args.get("with") else sql 1608 1609 def insert_sql(self, expression: exp.Insert) -> str: 1610 hint = self.sql(expression, "hint") 1611 overwrite = expression.args.get("overwrite") 1612 1613 if isinstance(expression.this, exp.Directory): 1614 this = " OVERWRITE" if overwrite else " INTO" 1615 else: 1616 this = self.INSERT_OVERWRITE if overwrite else " INTO" 1617 1618 stored = self.sql(expression, "stored") 1619 stored = f" {stored}" if stored else "" 1620 alternative = expression.args.get("alternative") 1621 alternative = f" OR {alternative}" if alternative else "" 1622 ignore = " IGNORE" if expression.args.get("ignore") else "" 1623 is_function = expression.args.get("is_function") 1624 if is_function: 1625 this = f"{this} FUNCTION" 1626 this = f"{this} {self.sql(expression, 'this')}" 1627 1628 exists = " IF EXISTS" if expression.args.get("exists") else "" 1629 where = self.sql(expression, "where") 1630 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1631 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1632 on_conflict = self.sql(expression, "conflict") 1633 on_conflict = f" {on_conflict}" if on_conflict else "" 1634 by_name = " BY NAME" if expression.args.get("by_name") else "" 1635 returning = self.sql(expression, "returning") 1636 1637 if self.RETURNING_END: 1638 expression_sql = f"{expression_sql}{on_conflict}{returning}" 1639 else: 1640 expression_sql = f"{returning}{expression_sql}{on_conflict}" 1641 1642 sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{where}{expression_sql}" 1643 return self.prepend_ctes(expression, sql) 1644 1645 def intersect_sql(self, expression: exp.Intersect) -> str: 1646 return self.set_operations(expression) 1647 1648 def intersect_op(self, expression: exp.Intersect) -> str: 1649 return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}" 1650 1651 def introducer_sql(self, expression: exp.Introducer) -> str: 1652 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 1653 1654 def kill_sql(self, expression: exp.Kill) -> str: 1655 kind = self.sql(expression, "kind") 1656 kind = f" {kind}" if kind else "" 1657 this = self.sql(expression, "this") 1658 this = f" {this}" if this else "" 1659 return f"KILL{kind}{this}" 1660 1661 def pseudotype_sql(self, expression: exp.PseudoType) -> str: 1662 return expression.name 1663 1664 def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str: 1665 return expression.name 1666 1667 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1668 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1669 1670 constraint = self.sql(expression, "constraint") 1671 constraint = f" ON CONSTRAINT {constraint}" if constraint else "" 1672 1673 conflict_keys = self.expressions(expression, key="conflict_keys", flat=True) 1674 conflict_keys = f"({conflict_keys}) " if conflict_keys else " " 1675 action = self.sql(expression, "action") 1676 1677 expressions = self.expressions(expression, flat=True) 1678 if expressions: 1679 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1680 expressions = f" {set_keyword}{expressions}" 1681 1682 return f"{conflict}{constraint}{conflict_keys}{action}{expressions}" 1683 1684 def returning_sql(self, expression: exp.Returning) -> str: 1685 return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}" 1686 1687 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1688 fields = self.sql(expression, "fields") 1689 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1690 escaped = self.sql(expression, "escaped") 1691 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1692 items = self.sql(expression, "collection_items") 1693 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1694 keys = self.sql(expression, "map_keys") 1695 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1696 lines = self.sql(expression, "lines") 1697 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1698 null = self.sql(expression, "null") 1699 null = f" NULL DEFINED AS {null}" if null else "" 1700 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}" 1701 1702 def withtablehint_sql(self, expression: exp.WithTableHint) -> str: 1703 return f"WITH ({self.expressions(expression, flat=True)})" 1704 1705 def indextablehint_sql(self, expression: exp.IndexTableHint) -> str: 1706 this = f"{self.sql(expression, 'this')} INDEX" 1707 target = self.sql(expression, "target") 1708 target = f" FOR {target}" if target else "" 1709 return f"{this}{target} ({self.expressions(expression, flat=True)})" 1710 1711 def historicaldata_sql(self, expression: exp.HistoricalData) -> str: 1712 this = self.sql(expression, "this") 1713 kind = self.sql(expression, "kind") 1714 expr = self.sql(expression, "expression") 1715 return f"{this} ({kind} => {expr})" 1716 1717 def table_parts(self, expression: exp.Table) -> str: 1718 return ".".join( 1719 self.sql(part) 1720 for part in ( 1721 expression.args.get("catalog"), 1722 expression.args.get("db"), 1723 expression.args.get("this"), 1724 ) 1725 if part is not None 1726 ) 1727 1728 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1729 table = self.table_parts(expression) 1730 only = "ONLY " if expression.args.get("only") else "" 1731 partition = self.sql(expression, "partition") 1732 partition = f" {partition}" if partition else "" 1733 version = self.sql(expression, "version") 1734 version = f" {version}" if version else "" 1735 alias = self.sql(expression, "alias") 1736 alias = f"{sep}{alias}" if alias else "" 1737 hints = self.expressions(expression, key="hints", sep=" ") 1738 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1739 pivots = self.expressions(expression, key="pivots", sep="", flat=True) 1740 joins = self.indent( 1741 self.expressions(expression, key="joins", sep="", flat=True), skip_first=True 1742 ) 1743 laterals = self.expressions(expression, key="laterals", sep="") 1744 1745 file_format = self.sql(expression, "format") 1746 if file_format: 1747 pattern = self.sql(expression, "pattern") 1748 pattern = f", PATTERN => {pattern}" if pattern else "" 1749 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1750 1751 ordinality = expression.args.get("ordinality") or "" 1752 if ordinality: 1753 ordinality = f" WITH ORDINALITY{alias}" 1754 alias = "" 1755 1756 when = self.sql(expression, "when") 1757 if when: 1758 table = f"{table} {when}" 1759 1760 changes = self.sql(expression, "changes") 1761 changes = f" {changes}" if changes else "" 1762 1763 rows_from = self.expressions(expression, key="rows_from") 1764 if rows_from: 1765 table = f"ROWS FROM {self.wrap(rows_from)}" 1766 1767 return f"{only}{table}{changes}{partition}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}" 1768 1769 def tablesample_sql( 1770 self, 1771 expression: exp.TableSample, 1772 sep: str = " AS ", 1773 tablesample_keyword: t.Optional[str] = None, 1774 ) -> str: 1775 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1776 table = expression.this.copy() 1777 table.set("alias", None) 1778 this = self.sql(table) 1779 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1780 else: 1781 this = self.sql(expression, "this") 1782 alias = "" 1783 1784 method = self.sql(expression, "method") 1785 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1786 numerator = self.sql(expression, "bucket_numerator") 1787 denominator = self.sql(expression, "bucket_denominator") 1788 field = self.sql(expression, "bucket_field") 1789 field = f" ON {field}" if field else "" 1790 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1791 seed = self.sql(expression, "seed") 1792 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1793 1794 size = self.sql(expression, "size") 1795 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1796 size = f"{size} ROWS" 1797 1798 percent = self.sql(expression, "percent") 1799 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1800 percent = f"{percent} PERCENT" 1801 1802 expr = f"{bucket}{percent}{size}" 1803 if self.TABLESAMPLE_REQUIRES_PARENS: 1804 expr = f"({expr})" 1805 1806 return ( 1807 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1808 ) 1809 1810 def pivot_sql(self, expression: exp.Pivot) -> str: 1811 expressions = self.expressions(expression, flat=True) 1812 1813 if expression.this: 1814 this = self.sql(expression, "this") 1815 if not expressions: 1816 return f"UNPIVOT {this}" 1817 1818 on = f"{self.seg('ON')} {expressions}" 1819 using = self.expressions(expression, key="using", flat=True) 1820 using = f"{self.seg('USING')} {using}" if using else "" 1821 group = self.sql(expression, "group") 1822 return f"PIVOT {this}{on}{using}{group}" 1823 1824 alias = self.sql(expression, "alias") 1825 alias = f" AS {alias}" if alias else "" 1826 direction = self.seg("UNPIVOT" if expression.unpivot else "PIVOT") 1827 field = self.sql(expression, "field") 1828 include_nulls = expression.args.get("include_nulls") 1829 if include_nulls is not None: 1830 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1831 else: 1832 nulls = "" 1833 return f"{direction}{nulls}({expressions} FOR {field}){alias}" 1834 1835 def version_sql(self, expression: exp.Version) -> str: 1836 this = f"FOR {expression.name}" 1837 kind = expression.text("kind") 1838 expr = self.sql(expression, "expression") 1839 return f"{this} {kind} {expr}" 1840 1841 def tuple_sql(self, expression: exp.Tuple) -> str: 1842 return f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})" 1843 1844 def update_sql(self, expression: exp.Update) -> str: 1845 this = self.sql(expression, "this") 1846 set_sql = self.expressions(expression, flat=True) 1847 from_sql = self.sql(expression, "from") 1848 where_sql = self.sql(expression, "where") 1849 returning = self.sql(expression, "returning") 1850 order = self.sql(expression, "order") 1851 limit = self.sql(expression, "limit") 1852 if self.RETURNING_END: 1853 expression_sql = f"{from_sql}{where_sql}{returning}" 1854 else: 1855 expression_sql = f"{returning}{from_sql}{where_sql}" 1856 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1857 return self.prepend_ctes(expression, sql) 1858 1859 def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str: 1860 values_as_table = values_as_table and self.VALUES_AS_TABLE 1861 1862 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1863 if values_as_table or not expression.find_ancestor(exp.From, exp.Join): 1864 args = self.expressions(expression) 1865 alias = self.sql(expression, "alias") 1866 values = f"VALUES{self.seg('')}{args}" 1867 values = ( 1868 f"({values})" 1869 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1870 else values 1871 ) 1872 return f"{values} AS {alias}" if alias else values 1873 1874 # Converts `VALUES...` expression into a series of select unions. 1875 alias_node = expression.args.get("alias") 1876 column_names = alias_node and alias_node.columns 1877 1878 selects: t.List[exp.Query] = [] 1879 1880 for i, tup in enumerate(expression.expressions): 1881 row = tup.expressions 1882 1883 if i == 0 and column_names: 1884 row = [ 1885 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1886 ] 1887 1888 selects.append(exp.Select(expressions=row)) 1889 1890 if self.pretty: 1891 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1892 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1893 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1894 query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1895 return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False)) 1896 1897 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1898 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1899 return f"({unions}){alias}" 1900 1901 def var_sql(self, expression: exp.Var) -> str: 1902 return self.sql(expression, "this") 1903 1904 def into_sql(self, expression: exp.Into) -> str: 1905 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1906 unlogged = " UNLOGGED" if expression.args.get("unlogged") else "" 1907 return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}" 1908 1909 def from_sql(self, expression: exp.From) -> str: 1910 return f"{self.seg('FROM')} {self.sql(expression, 'this')}" 1911 1912 def group_sql(self, expression: exp.Group) -> str: 1913 group_by_all = expression.args.get("all") 1914 if group_by_all is True: 1915 modifier = " ALL" 1916 elif group_by_all is False: 1917 modifier = " DISTINCT" 1918 else: 1919 modifier = "" 1920 1921 group_by = self.op_expressions(f"GROUP BY{modifier}", expression) 1922 1923 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1924 grouping_sets = ( 1925 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1926 ) 1927 1928 cube = expression.args.get("cube", []) 1929 if seq_get(cube, 0) is True: 1930 return f"{group_by}{self.seg('WITH CUBE')}" 1931 else: 1932 cube_sql = self.expressions(expression, key="cube", indent=False) 1933 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1934 1935 rollup = expression.args.get("rollup", []) 1936 if seq_get(rollup, 0) is True: 1937 return f"{group_by}{self.seg('WITH ROLLUP')}" 1938 else: 1939 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1940 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1941 1942 groupings = csv( 1943 grouping_sets, 1944 cube_sql, 1945 rollup_sql, 1946 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1947 sep=self.GROUPINGS_SEP, 1948 ) 1949 1950 if expression.args.get("expressions") and groupings: 1951 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1952 1953 return f"{group_by}{groupings}" 1954 1955 def having_sql(self, expression: exp.Having) -> str: 1956 this = self.indent(self.sql(expression, "this")) 1957 return f"{self.seg('HAVING')}{self.sep()}{this}" 1958 1959 def connect_sql(self, expression: exp.Connect) -> str: 1960 start = self.sql(expression, "start") 1961 start = self.seg(f"START WITH {start}") if start else "" 1962 nocycle = " NOCYCLE" if expression.args.get("nocycle") else "" 1963 connect = self.sql(expression, "connect") 1964 connect = self.seg(f"CONNECT BY{nocycle} {connect}") 1965 return start + connect 1966 1967 def prior_sql(self, expression: exp.Prior) -> str: 1968 return f"PRIOR {self.sql(expression, 'this')}" 1969 1970 def join_sql(self, expression: exp.Join) -> str: 1971 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1972 side = None 1973 else: 1974 side = expression.side 1975 1976 op_sql = " ".join( 1977 op 1978 for op in ( 1979 expression.method, 1980 "GLOBAL" if expression.args.get("global") else None, 1981 side, 1982 expression.kind, 1983 expression.hint if self.JOIN_HINTS else None, 1984 ) 1985 if op 1986 ) 1987 match_cond = self.sql(expression, "match_condition") 1988 match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else "" 1989 on_sql = self.sql(expression, "on") 1990 using = expression.args.get("using") 1991 1992 if not on_sql and using: 1993 on_sql = csv(*(self.sql(column) for column in using)) 1994 1995 this = expression.this 1996 this_sql = self.sql(this) 1997 1998 if on_sql: 1999 on_sql = self.indent(on_sql, skip_first=True) 2000 space = self.seg(" " * self.pad) if self.pretty else " " 2001 if using: 2002 on_sql = f"{space}USING ({on_sql})" 2003 else: 2004 on_sql = f"{space}ON {on_sql}" 2005 elif not op_sql: 2006 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 2007 return f" {this_sql}" 2008 2009 return f", {this_sql}" 2010 2011 if op_sql != "STRAIGHT_JOIN": 2012 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 2013 2014 return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}" 2015 2016 def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str: 2017 args = self.expressions(expression, flat=True) 2018 args = f"({args})" if len(args.split(",")) > 1 else args 2019 return f"{args} {arrow_sep} {self.sql(expression, 'this')}" 2020 2021 def lateral_op(self, expression: exp.Lateral) -> str: 2022 cross_apply = expression.args.get("cross_apply") 2023 2024 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 2025 if cross_apply is True: 2026 op = "INNER JOIN " 2027 elif cross_apply is False: 2028 op = "LEFT JOIN " 2029 else: 2030 op = "" 2031 2032 return f"{op}LATERAL" 2033 2034 def lateral_sql(self, expression: exp.Lateral) -> str: 2035 this = self.sql(expression, "this") 2036 2037 if expression.args.get("view"): 2038 alias = expression.args["alias"] 2039 columns = self.expressions(alias, key="columns", flat=True) 2040 table = f" {alias.name}" if alias.name else "" 2041 columns = f" AS {columns}" if columns else "" 2042 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 2043 return f"{op_sql}{self.sep()}{this}{table}{columns}" 2044 2045 alias = self.sql(expression, "alias") 2046 alias = f" AS {alias}" if alias else "" 2047 return f"{self.lateral_op(expression)} {this}{alias}" 2048 2049 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 2050 this = self.sql(expression, "this") 2051 2052 args = [ 2053 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 2054 for e in (expression.args.get(k) for k in ("offset", "expression")) 2055 if e 2056 ] 2057 2058 args_sql = ", ".join(self.sql(e) for e in args) 2059 args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql 2060 expressions = self.expressions(expression, flat=True) 2061 expressions = f" BY {expressions}" if expressions else "" 2062 2063 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}" 2064 2065 def offset_sql(self, expression: exp.Offset) -> str: 2066 this = self.sql(expression, "this") 2067 value = expression.expression 2068 value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value 2069 expressions = self.expressions(expression, flat=True) 2070 expressions = f" BY {expressions}" if expressions else "" 2071 return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}" 2072 2073 def setitem_sql(self, expression: exp.SetItem) -> str: 2074 kind = self.sql(expression, "kind") 2075 kind = f"{kind} " if kind else "" 2076 this = self.sql(expression, "this") 2077 expressions = self.expressions(expression) 2078 collate = self.sql(expression, "collate") 2079 collate = f" COLLATE {collate}" if collate else "" 2080 global_ = "GLOBAL " if expression.args.get("global") else "" 2081 return f"{global_}{kind}{this}{expressions}{collate}" 2082 2083 def set_sql(self, expression: exp.Set) -> str: 2084 expressions = ( 2085 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 2086 ) 2087 tag = " TAG" if expression.args.get("tag") else "" 2088 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}" 2089 2090 def pragma_sql(self, expression: exp.Pragma) -> str: 2091 return f"PRAGMA {self.sql(expression, 'this')}" 2092 2093 def lock_sql(self, expression: exp.Lock) -> str: 2094 if not self.LOCKING_READS_SUPPORTED: 2095 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 2096 return "" 2097 2098 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 2099 expressions = self.expressions(expression, flat=True) 2100 expressions = f" OF {expressions}" if expressions else "" 2101 wait = expression.args.get("wait") 2102 2103 if wait is not None: 2104 if isinstance(wait, exp.Literal): 2105 wait = f" WAIT {self.sql(wait)}" 2106 else: 2107 wait = " NOWAIT" if wait else " SKIP LOCKED" 2108 2109 return f"{lock_type}{expressions}{wait or ''}" 2110 2111 def literal_sql(self, expression: exp.Literal) -> str: 2112 text = expression.this or "" 2113 if expression.is_string: 2114 text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}" 2115 return text 2116 2117 def escape_str(self, text: str, escape_backslash: bool = True) -> str: 2118 if self.dialect.ESCAPED_SEQUENCES: 2119 to_escaped = self.dialect.ESCAPED_SEQUENCES 2120 text = "".join( 2121 to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text 2122 ) 2123 2124 return self._replace_line_breaks(text).replace( 2125 self.dialect.QUOTE_END, self._escaped_quote_end 2126 ) 2127 2128 def loaddata_sql(self, expression: exp.LoadData) -> str: 2129 local = " LOCAL" if expression.args.get("local") else "" 2130 inpath = f" INPATH {self.sql(expression, 'inpath')}" 2131 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 2132 this = f" INTO TABLE {self.sql(expression, 'this')}" 2133 partition = self.sql(expression, "partition") 2134 partition = f" {partition}" if partition else "" 2135 input_format = self.sql(expression, "input_format") 2136 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 2137 serde = self.sql(expression, "serde") 2138 serde = f" SERDE {serde}" if serde else "" 2139 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}" 2140 2141 def null_sql(self, *_) -> str: 2142 return "NULL" 2143 2144 def boolean_sql(self, expression: exp.Boolean) -> str: 2145 return "TRUE" if expression.this else "FALSE" 2146 2147 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 2148 this = self.sql(expression, "this") 2149 this = f"{this} " if this else this 2150 siblings = "SIBLINGS " if expression.args.get("siblings") else "" 2151 order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore 2152 interpolated_values = [ 2153 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 2154 for named_expression in expression.args.get("interpolate") or [] 2155 ] 2156 interpolate = ( 2157 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 2158 ) 2159 return f"{order}{interpolate}" 2160 2161 def withfill_sql(self, expression: exp.WithFill) -> str: 2162 from_sql = self.sql(expression, "from") 2163 from_sql = f" FROM {from_sql}" if from_sql else "" 2164 to_sql = self.sql(expression, "to") 2165 to_sql = f" TO {to_sql}" if to_sql else "" 2166 step_sql = self.sql(expression, "step") 2167 step_sql = f" STEP {step_sql}" if step_sql else "" 2168 return f"WITH FILL{from_sql}{to_sql}{step_sql}" 2169 2170 def cluster_sql(self, expression: exp.Cluster) -> str: 2171 return self.op_expressions("CLUSTER BY", expression) 2172 2173 def distribute_sql(self, expression: exp.Distribute) -> str: 2174 return self.op_expressions("DISTRIBUTE BY", expression) 2175 2176 def sort_sql(self, expression: exp.Sort) -> str: 2177 return self.op_expressions("SORT BY", expression) 2178 2179 def ordered_sql(self, expression: exp.Ordered) -> str: 2180 desc = expression.args.get("desc") 2181 asc = not desc 2182 2183 nulls_first = expression.args.get("nulls_first") 2184 nulls_last = not nulls_first 2185 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 2186 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 2187 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 2188 2189 this = self.sql(expression, "this") 2190 2191 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 2192 nulls_sort_change = "" 2193 if nulls_first and ( 2194 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 2195 ): 2196 nulls_sort_change = " NULLS FIRST" 2197 elif ( 2198 nulls_last 2199 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 2200 and not nulls_are_last 2201 ): 2202 nulls_sort_change = " NULLS LAST" 2203 2204 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 2205 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 2206 window = expression.find_ancestor(exp.Window, exp.Select) 2207 if isinstance(window, exp.Window) and window.args.get("spec"): 2208 self.unsupported( 2209 f"'{nulls_sort_change.strip()}' translation not supported in window functions" 2210 ) 2211 nulls_sort_change = "" 2212 elif self.NULL_ORDERING_SUPPORTED is None: 2213 if expression.this.is_int: 2214 self.unsupported( 2215 f"'{nulls_sort_change.strip()}' translation not supported with positional ordering" 2216 ) 2217 elif not isinstance(expression.this, exp.Rand): 2218 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 2219 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 2220 nulls_sort_change = "" 2221 2222 with_fill = self.sql(expression, "with_fill") 2223 with_fill = f" {with_fill}" if with_fill else "" 2224 2225 return f"{this}{sort_order}{nulls_sort_change}{with_fill}" 2226 2227 def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str: 2228 window_frame = self.sql(expression, "window_frame") 2229 window_frame = f"{window_frame} " if window_frame else "" 2230 2231 this = self.sql(expression, "this") 2232 2233 return f"{window_frame}{this}" 2234 2235 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 2236 partition = self.partition_by_sql(expression) 2237 order = self.sql(expression, "order") 2238 measures = self.expressions(expression, key="measures") 2239 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 2240 rows = self.sql(expression, "rows") 2241 rows = self.seg(rows) if rows else "" 2242 after = self.sql(expression, "after") 2243 after = self.seg(after) if after else "" 2244 pattern = self.sql(expression, "pattern") 2245 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 2246 definition_sqls = [ 2247 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 2248 for definition in expression.args.get("define", []) 2249 ] 2250 definitions = self.expressions(sqls=definition_sqls) 2251 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 2252 body = "".join( 2253 ( 2254 partition, 2255 order, 2256 measures, 2257 rows, 2258 after, 2259 pattern, 2260 define, 2261 ) 2262 ) 2263 alias = self.sql(expression, "alias") 2264 alias = f" {alias}" if alias else "" 2265 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}" 2266 2267 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 2268 limit = expression.args.get("limit") 2269 2270 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 2271 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 2272 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 2273 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 2274 2275 return csv( 2276 *sqls, 2277 *[self.sql(join) for join in expression.args.get("joins") or []], 2278 self.sql(expression, "connect"), 2279 self.sql(expression, "match"), 2280 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 2281 self.sql(expression, "prewhere"), 2282 self.sql(expression, "where"), 2283 self.sql(expression, "group"), 2284 self.sql(expression, "having"), 2285 *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()], 2286 self.sql(expression, "order"), 2287 *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit), 2288 *self.after_limit_modifiers(expression), 2289 self.options_modifier(expression), 2290 sep="", 2291 ) 2292 2293 def options_modifier(self, expression: exp.Expression) -> str: 2294 options = self.expressions(expression, key="options") 2295 return f" {options}" if options else "" 2296 2297 def queryoption_sql(self, expression: exp.QueryOption) -> str: 2298 return "" 2299 2300 def offset_limit_modifiers( 2301 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 2302 ) -> t.List[str]: 2303 return [ 2304 self.sql(expression, "offset") if fetch else self.sql(limit), 2305 self.sql(limit) if fetch else self.sql(expression, "offset"), 2306 ] 2307 2308 def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]: 2309 locks = self.expressions(expression, key="locks", sep=" ") 2310 locks = f" {locks}" if locks else "" 2311 return [locks, self.sql(expression, "sample")] 2312 2313 def select_sql(self, expression: exp.Select) -> str: 2314 into = expression.args.get("into") 2315 if not self.SUPPORTS_SELECT_INTO and into: 2316 into.pop() 2317 2318 hint = self.sql(expression, "hint") 2319 distinct = self.sql(expression, "distinct") 2320 distinct = f" {distinct}" if distinct else "" 2321 kind = self.sql(expression, "kind") 2322 2323 limit = expression.args.get("limit") 2324 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP: 2325 top = self.limit_sql(limit, top=True) 2326 limit.pop() 2327 else: 2328 top = "" 2329 2330 expressions = self.expressions(expression) 2331 2332 if kind: 2333 if kind in self.SELECT_KINDS: 2334 kind = f" AS {kind}" 2335 else: 2336 if kind == "STRUCT": 2337 expressions = self.expressions( 2338 sqls=[ 2339 self.sql( 2340 exp.Struct( 2341 expressions=[ 2342 exp.PropertyEQ(this=e.args.get("alias"), expression=e.this) 2343 if isinstance(e, exp.Alias) 2344 else e 2345 for e in expression.expressions 2346 ] 2347 ) 2348 ) 2349 ] 2350 ) 2351 kind = "" 2352 2353 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2354 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2355 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2356 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2357 sql = self.query_modifiers( 2358 expression, 2359 f"SELECT{top_distinct}{kind}{expressions}", 2360 self.sql(expression, "into", comment=False), 2361 self.sql(expression, "from", comment=False), 2362 ) 2363 2364 sql = self.prepend_ctes(expression, sql) 2365 2366 if not self.SUPPORTS_SELECT_INTO and into: 2367 if into.args.get("temporary"): 2368 table_kind = " TEMPORARY" 2369 elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"): 2370 table_kind = " UNLOGGED" 2371 else: 2372 table_kind = "" 2373 sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}" 2374 2375 return sql 2376 2377 def schema_sql(self, expression: exp.Schema) -> str: 2378 this = self.sql(expression, "this") 2379 sql = self.schema_columns_sql(expression) 2380 return f"{this} {sql}" if this and sql else this or sql 2381 2382 def schema_columns_sql(self, expression: exp.Schema) -> str: 2383 if expression.expressions: 2384 return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}" 2385 return "" 2386 2387 def star_sql(self, expression: exp.Star) -> str: 2388 except_ = self.expressions(expression, key="except", flat=True) 2389 except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else "" 2390 replace = self.expressions(expression, key="replace", flat=True) 2391 replace = f"{self.seg('REPLACE')} ({replace})" if replace else "" 2392 rename = self.expressions(expression, key="rename", flat=True) 2393 rename = f"{self.seg('RENAME')} ({rename})" if rename else "" 2394 return f"*{except_}{replace}{rename}" 2395 2396 def parameter_sql(self, expression: exp.Parameter) -> str: 2397 this = self.sql(expression, "this") 2398 return f"{self.PARAMETER_TOKEN}{this}" 2399 2400 def sessionparameter_sql(self, expression: exp.SessionParameter) -> str: 2401 this = self.sql(expression, "this") 2402 kind = expression.text("kind") 2403 if kind: 2404 kind = f"{kind}." 2405 return f"@@{kind}{this}" 2406 2407 def placeholder_sql(self, expression: exp.Placeholder) -> str: 2408 return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?" 2409 2410 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2411 alias = self.sql(expression, "alias") 2412 alias = f"{sep}{alias}" if alias else "" 2413 pivots = self.expressions(expression, key="pivots", sep="", flat=True) 2414 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2415 return self.prepend_ctes(expression, sql) 2416 2417 def qualify_sql(self, expression: exp.Qualify) -> str: 2418 this = self.indent(self.sql(expression, "this")) 2419 return f"{self.seg('QUALIFY')}{self.sep()}{this}" 2420 2421 def set_operations(self, expression: exp.SetOperation) -> str: 2422 if not self.SET_OP_MODIFIERS: 2423 limit = expression.args.get("limit") 2424 order = expression.args.get("order") 2425 2426 if limit or order: 2427 select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False) 2428 2429 if limit: 2430 select = select.limit(limit.pop(), copy=False) 2431 if order: 2432 select = select.order_by(order.pop(), copy=False) 2433 return self.sql(select) 2434 2435 sqls: t.List[str] = [] 2436 stack: t.List[t.Union[str, exp.Expression]] = [expression] 2437 2438 while stack: 2439 node = stack.pop() 2440 2441 if isinstance(node, exp.SetOperation): 2442 stack.append(node.expression) 2443 stack.append( 2444 self.maybe_comment( 2445 getattr(self, f"{node.key}_op")(node), 2446 comments=node.comments, 2447 separated=True, 2448 ) 2449 ) 2450 stack.append(node.this) 2451 else: 2452 sqls.append(self.sql(node)) 2453 2454 this = self.sep().join(sqls) 2455 this = self.query_modifiers(expression, this) 2456 return self.prepend_ctes(expression, this) 2457 2458 def union_sql(self, expression: exp.Union) -> str: 2459 return self.set_operations(expression) 2460 2461 def union_op(self, expression: exp.SetOperation) -> str: 2462 kind = " DISTINCT" if self.EXPLICIT_SET_OP else "" 2463 kind = kind if expression.args.get("distinct") else " ALL" 2464 by_name = " BY NAME" if expression.args.get("by_name") else "" 2465 return f"UNION{kind}{by_name}" 2466 2467 def unnest_sql(self, expression: exp.Unnest) -> str: 2468 args = self.expressions(expression, flat=True) 2469 2470 alias = expression.args.get("alias") 2471 offset = expression.args.get("offset") 2472 2473 if self.UNNEST_WITH_ORDINALITY: 2474 if alias and isinstance(offset, exp.Expression): 2475 alias.append("columns", offset) 2476 2477 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2478 columns = alias.columns 2479 alias = self.sql(columns[0]) if columns else "" 2480 else: 2481 alias = self.sql(alias) 2482 2483 alias = f" AS {alias}" if alias else alias 2484 if self.UNNEST_WITH_ORDINALITY: 2485 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2486 else: 2487 if isinstance(offset, exp.Expression): 2488 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2489 elif offset: 2490 suffix = f"{alias} WITH OFFSET" 2491 else: 2492 suffix = alias 2493 2494 return f"UNNEST({args}){suffix}" 2495 2496 def prewhere_sql(self, expression: exp.PreWhere) -> str: 2497 return "" 2498 2499 def where_sql(self, expression: exp.Where) -> str: 2500 this = self.indent(self.sql(expression, "this")) 2501 return f"{self.seg('WHERE')}{self.sep()}{this}" 2502 2503 def window_sql(self, expression: exp.Window) -> str: 2504 this = self.sql(expression, "this") 2505 partition = self.partition_by_sql(expression) 2506 order = expression.args.get("order") 2507 order = self.order_sql(order, flat=True) if order else "" 2508 spec = self.sql(expression, "spec") 2509 alias = self.sql(expression, "alias") 2510 over = self.sql(expression, "over") or "OVER" 2511 2512 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2513 2514 first = expression.args.get("first") 2515 if first is None: 2516 first = "" 2517 else: 2518 first = "FIRST" if first else "LAST" 2519 2520 if not partition and not order and not spec and alias: 2521 return f"{this} {alias}" 2522 2523 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2524 return f"{this} ({args})" 2525 2526 def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str: 2527 partition = self.expressions(expression, key="partition_by", flat=True) 2528 return f"PARTITION BY {partition}" if partition else "" 2529 2530 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2531 kind = self.sql(expression, "kind") 2532 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2533 end = ( 2534 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2535 or "CURRENT ROW" 2536 ) 2537 return f"{kind} BETWEEN {start} AND {end}" 2538 2539 def withingroup_sql(self, expression: exp.WithinGroup) -> str: 2540 this = self.sql(expression, "this") 2541 expression_sql = self.sql(expression, "expression")[1:] # order has a leading space 2542 return f"{this} WITHIN GROUP ({expression_sql})" 2543 2544 def between_sql(self, expression: exp.Between) -> str: 2545 this = self.sql(expression, "this") 2546 low = self.sql(expression, "low") 2547 high = self.sql(expression, "high") 2548 return f"{this} BETWEEN {low} AND {high}" 2549 2550 def bracket_offset_expressions(self, expression: exp.Bracket) -> t.List[exp.Expression]: 2551 return apply_index_offset( 2552 expression.this, 2553 expression.expressions, 2554 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2555 ) 2556 2557 def bracket_sql(self, expression: exp.Bracket) -> str: 2558 expressions = self.bracket_offset_expressions(expression) 2559 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2560 return f"{self.sql(expression, 'this')}[{expressions_sql}]" 2561 2562 def all_sql(self, expression: exp.All) -> str: 2563 return f"ALL {self.wrap(expression)}" 2564 2565 def any_sql(self, expression: exp.Any) -> str: 2566 this = self.sql(expression, "this") 2567 if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)): 2568 if isinstance(expression.this, exp.UNWRAPPED_QUERIES): 2569 this = self.wrap(this) 2570 return f"ANY{this}" 2571 return f"ANY {this}" 2572 2573 def exists_sql(self, expression: exp.Exists) -> str: 2574 return f"EXISTS{self.wrap(expression)}" 2575 2576 def case_sql(self, expression: exp.Case) -> str: 2577 this = self.sql(expression, "this") 2578 statements = [f"CASE {this}" if this else "CASE"] 2579 2580 for e in expression.args["ifs"]: 2581 statements.append(f"WHEN {self.sql(e, 'this')}") 2582 statements.append(f"THEN {self.sql(e, 'true')}") 2583 2584 default = self.sql(expression, "default") 2585 2586 if default: 2587 statements.append(f"ELSE {default}") 2588 2589 statements.append("END") 2590 2591 if self.pretty and self.too_wide(statements): 2592 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2593 2594 return " ".join(statements) 2595 2596 def constraint_sql(self, expression: exp.Constraint) -> str: 2597 this = self.sql(expression, "this") 2598 expressions = self.expressions(expression, flat=True) 2599 return f"CONSTRAINT {this} {expressions}" 2600 2601 def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str: 2602 order = expression.args.get("order") 2603 order = f" OVER ({self.order_sql(order, flat=True)})" if order else "" 2604 return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}" 2605 2606 def extract_sql(self, expression: exp.Extract) -> str: 2607 this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name 2608 expression_sql = self.sql(expression, "expression") 2609 return f"EXTRACT({this} FROM {expression_sql})" 2610 2611 def trim_sql(self, expression: exp.Trim) -> str: 2612 trim_type = self.sql(expression, "position") 2613 2614 if trim_type == "LEADING": 2615 return self.func("LTRIM", expression.this) 2616 elif trim_type == "TRAILING": 2617 return self.func("RTRIM", expression.this) 2618 else: 2619 return self.func("TRIM", expression.this, expression.expression) 2620 2621 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2622 args = expression.expressions 2623 if isinstance(expression, exp.ConcatWs): 2624 args = args[1:] # Skip the delimiter 2625 2626 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2627 args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args] 2628 2629 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2630 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2631 2632 return args 2633 2634 def concat_sql(self, expression: exp.Concat) -> str: 2635 expressions = self.convert_concat_args(expression) 2636 2637 # Some dialects don't allow a single-argument CONCAT call 2638 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2639 return self.sql(expressions[0]) 2640 2641 return self.func("CONCAT", *expressions) 2642 2643 def concatws_sql(self, expression: exp.ConcatWs) -> str: 2644 return self.func( 2645 "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression) 2646 ) 2647 2648 def check_sql(self, expression: exp.Check) -> str: 2649 this = self.sql(expression, key="this") 2650 return f"CHECK ({this})" 2651 2652 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2653 expressions = self.expressions(expression, flat=True) 2654 reference = self.sql(expression, "reference") 2655 reference = f" {reference}" if reference else "" 2656 delete = self.sql(expression, "delete") 2657 delete = f" ON DELETE {delete}" if delete else "" 2658 update = self.sql(expression, "update") 2659 update = f" ON UPDATE {update}" if update else "" 2660 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}" 2661 2662 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2663 expressions = self.expressions(expression, flat=True) 2664 options = self.expressions(expression, key="options", flat=True, sep=" ") 2665 options = f" {options}" if options else "" 2666 return f"PRIMARY KEY ({expressions}){options}" 2667 2668 def if_sql(self, expression: exp.If) -> str: 2669 return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false"))) 2670 2671 def matchagainst_sql(self, expression: exp.MatchAgainst) -> str: 2672 modifier = expression.args.get("modifier") 2673 modifier = f" {modifier}" if modifier else "" 2674 return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})" 2675 2676 def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str: 2677 return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}" 2678 2679 def jsonpath_sql(self, expression: exp.JSONPath) -> str: 2680 path = self.expressions(expression, sep="", flat=True).lstrip(".") 2681 if self.QUOTE_JSON_PATH: 2682 path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}" 2683 2684 return path 2685 2686 def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str: 2687 if isinstance(expression, exp.JSONPathPart): 2688 transform = self.TRANSFORMS.get(expression.__class__) 2689 if not callable(transform): 2690 self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}") 2691 return "" 2692 2693 return transform(self, expression) 2694 2695 if isinstance(expression, int): 2696 return str(expression) 2697 2698 if self.JSON_PATH_SINGLE_QUOTE_ESCAPE: 2699 escaped = expression.replace("'", "\\'") 2700 escaped = f"\\'{expression}\\'" 2701 else: 2702 escaped = expression.replace('"', '\\"') 2703 escaped = f'"{escaped}"' 2704 2705 return escaped 2706 2707 def formatjson_sql(self, expression: exp.FormatJson) -> str: 2708 return f"{self.sql(expression, 'this')} FORMAT JSON" 2709 2710 def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str: 2711 null_handling = expression.args.get("null_handling") 2712 null_handling = f" {null_handling}" if null_handling else "" 2713 2714 unique_keys = expression.args.get("unique_keys") 2715 if unique_keys is not None: 2716 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2717 else: 2718 unique_keys = "" 2719 2720 return_type = self.sql(expression, "return_type") 2721 return_type = f" RETURNING {return_type}" if return_type else "" 2722 encoding = self.sql(expression, "encoding") 2723 encoding = f" ENCODING {encoding}" if encoding else "" 2724 2725 return self.func( 2726 "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG", 2727 *expression.expressions, 2728 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2729 ) 2730 2731 def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str: 2732 return self.jsonobject_sql(expression) 2733 2734 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2735 null_handling = expression.args.get("null_handling") 2736 null_handling = f" {null_handling}" if null_handling else "" 2737 return_type = self.sql(expression, "return_type") 2738 return_type = f" RETURNING {return_type}" if return_type else "" 2739 strict = " STRICT" if expression.args.get("strict") else "" 2740 return self.func( 2741 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2742 ) 2743 2744 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2745 this = self.sql(expression, "this") 2746 order = self.sql(expression, "order") 2747 null_handling = expression.args.get("null_handling") 2748 null_handling = f" {null_handling}" if null_handling else "" 2749 return_type = self.sql(expression, "return_type") 2750 return_type = f" RETURNING {return_type}" if return_type else "" 2751 strict = " STRICT" if expression.args.get("strict") else "" 2752 return self.func( 2753 "JSON_ARRAYAGG", 2754 this, 2755 suffix=f"{order}{null_handling}{return_type}{strict})", 2756 ) 2757 2758 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2759 path = self.sql(expression, "path") 2760 path = f" PATH {path}" if path else "" 2761 nested_schema = self.sql(expression, "nested_schema") 2762 2763 if nested_schema: 2764 return f"NESTED{path} {nested_schema}" 2765 2766 this = self.sql(expression, "this") 2767 kind = self.sql(expression, "kind") 2768 kind = f" {kind}" if kind else "" 2769 return f"{this}{kind}{path}" 2770 2771 def jsonschema_sql(self, expression: exp.JSONSchema) -> str: 2772 return self.func("COLUMNS", *expression.expressions) 2773 2774 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2775 this = self.sql(expression, "this") 2776 path = self.sql(expression, "path") 2777 path = f", {path}" if path else "" 2778 error_handling = expression.args.get("error_handling") 2779 error_handling = f" {error_handling}" if error_handling else "" 2780 empty_handling = expression.args.get("empty_handling") 2781 empty_handling = f" {empty_handling}" if empty_handling else "" 2782 schema = self.sql(expression, "schema") 2783 return self.func( 2784 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2785 ) 2786 2787 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2788 this = self.sql(expression, "this") 2789 kind = self.sql(expression, "kind") 2790 path = self.sql(expression, "path") 2791 path = f" {path}" if path else "" 2792 as_json = " AS JSON" if expression.args.get("as_json") else "" 2793 return f"{this} {kind}{path}{as_json}" 2794 2795 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2796 this = self.sql(expression, "this") 2797 path = self.sql(expression, "path") 2798 path = f", {path}" if path else "" 2799 expressions = self.expressions(expression) 2800 with_ = ( 2801 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2802 if expressions 2803 else "" 2804 ) 2805 return f"OPENJSON({this}{path}){with_}" 2806 2807 def in_sql(self, expression: exp.In) -> str: 2808 query = expression.args.get("query") 2809 unnest = expression.args.get("unnest") 2810 field = expression.args.get("field") 2811 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2812 2813 if query: 2814 in_sql = self.sql(query) 2815 elif unnest: 2816 in_sql = self.in_unnest_op(unnest) 2817 elif field: 2818 in_sql = self.sql(field) 2819 else: 2820 in_sql = f"({self.expressions(expression, flat=True)})" 2821 2822 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}" 2823 2824 def in_unnest_op(self, unnest: exp.Unnest) -> str: 2825 return f"(SELECT {self.sql(unnest)})" 2826 2827 def interval_sql(self, expression: exp.Interval) -> str: 2828 unit = self.sql(expression, "unit") 2829 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2830 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2831 unit = f" {unit}" if unit else "" 2832 2833 if self.SINGLE_STRING_INTERVAL: 2834 this = expression.this.name if expression.this else "" 2835 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2836 2837 this = self.sql(expression, "this") 2838 if this: 2839 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2840 this = f" {this}" if unwrapped else f" ({this})" 2841 2842 return f"INTERVAL{this}{unit}" 2843 2844 def return_sql(self, expression: exp.Return) -> str: 2845 return f"RETURN {self.sql(expression, 'this')}" 2846 2847 def reference_sql(self, expression: exp.Reference) -> str: 2848 this = self.sql(expression, "this") 2849 expressions = self.expressions(expression, flat=True) 2850 expressions = f"({expressions})" if expressions else "" 2851 options = self.expressions(expression, key="options", flat=True, sep=" ") 2852 options = f" {options}" if options else "" 2853 return f"REFERENCES {this}{expressions}{options}" 2854 2855 def anonymous_sql(self, expression: exp.Anonymous) -> str: 2856 return self.func(self.sql(expression, "this"), *expression.expressions) 2857 2858 def paren_sql(self, expression: exp.Paren) -> str: 2859 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2860 return f"({sql}{self.seg(')', sep='')}" 2861 2862 def neg_sql(self, expression: exp.Neg) -> str: 2863 # This makes sure we don't convert "- - 5" to "--5", which is a comment 2864 this_sql = self.sql(expression, "this") 2865 sep = " " if this_sql[0] == "-" else "" 2866 return f"-{sep}{this_sql}" 2867 2868 def not_sql(self, expression: exp.Not) -> str: 2869 return f"NOT {self.sql(expression, 'this')}" 2870 2871 def alias_sql(self, expression: exp.Alias) -> str: 2872 alias = self.sql(expression, "alias") 2873 alias = f" AS {alias}" if alias else "" 2874 return f"{self.sql(expression, 'this')}{alias}" 2875 2876 def pivotalias_sql(self, expression: exp.PivotAlias) -> str: 2877 alias = expression.args["alias"] 2878 identifier_alias = isinstance(alias, exp.Identifier) 2879 2880 if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2881 alias.replace(exp.Literal.string(alias.output_name)) 2882 elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2883 alias.replace(exp.to_identifier(alias.output_name)) 2884 2885 return self.alias_sql(expression) 2886 2887 def aliases_sql(self, expression: exp.Aliases) -> str: 2888 return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})" 2889 2890 def atindex_sql(self, expression: exp.AtTimeZone) -> str: 2891 this = self.sql(expression, "this") 2892 index = self.sql(expression, "expression") 2893 return f"{this} AT {index}" 2894 2895 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 2896 this = self.sql(expression, "this") 2897 zone = self.sql(expression, "zone") 2898 return f"{this} AT TIME ZONE {zone}" 2899 2900 def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str: 2901 this = self.sql(expression, "this") 2902 zone = self.sql(expression, "zone") 2903 return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'" 2904 2905 def add_sql(self, expression: exp.Add) -> str: 2906 return self.binary(expression, "+") 2907 2908 def and_sql( 2909 self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None 2910 ) -> str: 2911 return self.connector_sql(expression, "AND", stack) 2912 2913 def or_sql( 2914 self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None 2915 ) -> str: 2916 return self.connector_sql(expression, "OR", stack) 2917 2918 def xor_sql( 2919 self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None 2920 ) -> str: 2921 return self.connector_sql(expression, "XOR", stack) 2922 2923 def connector_sql( 2924 self, 2925 expression: exp.Connector, 2926 op: str, 2927 stack: t.Optional[t.List[str | exp.Expression]] = None, 2928 ) -> str: 2929 if stack is not None: 2930 if expression.expressions: 2931 stack.append(self.expressions(expression, sep=f" {op} ")) 2932 else: 2933 stack.append(expression.right) 2934 if expression.comments and self.comments: 2935 for comment in expression.comments: 2936 if comment: 2937 op += f" /*{self.pad_comment(comment)}*/" 2938 stack.extend((op, expression.left)) 2939 return op 2940 2941 stack = [expression] 2942 sqls: t.List[str] = [] 2943 ops = set() 2944 2945 while stack: 2946 node = stack.pop() 2947 if isinstance(node, exp.Connector): 2948 ops.add(getattr(self, f"{node.key}_sql")(node, stack)) 2949 else: 2950 sql = self.sql(node) 2951 if sqls and sqls[-1] in ops: 2952 sqls[-1] += f" {sql}" 2953 else: 2954 sqls.append(sql) 2955 2956 sep = "\n" if self.pretty and self.too_wide(sqls) else " " 2957 return sep.join(sqls) 2958 2959 def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str: 2960 return self.binary(expression, "&") 2961 2962 def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str: 2963 return self.binary(expression, "<<") 2964 2965 def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str: 2966 return f"~{self.sql(expression, 'this')}" 2967 2968 def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str: 2969 return self.binary(expression, "|") 2970 2971 def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str: 2972 return self.binary(expression, ">>") 2973 2974 def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str: 2975 return self.binary(expression, "^") 2976 2977 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2978 format_sql = self.sql(expression, "format") 2979 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2980 to_sql = self.sql(expression, "to") 2981 to_sql = f" {to_sql}" if to_sql else "" 2982 action = self.sql(expression, "action") 2983 action = f" {action}" if action else "" 2984 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql}{action})" 2985 2986 def currentdate_sql(self, expression: exp.CurrentDate) -> str: 2987 zone = self.sql(expression, "this") 2988 return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE" 2989 2990 def collate_sql(self, expression: exp.Collate) -> str: 2991 if self.COLLATE_IS_FUNC: 2992 return self.function_fallback_sql(expression) 2993 return self.binary(expression, "COLLATE") 2994 2995 def command_sql(self, expression: exp.Command) -> str: 2996 return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}" 2997 2998 def comment_sql(self, expression: exp.Comment) -> str: 2999 this = self.sql(expression, "this") 3000 kind = expression.args["kind"] 3001 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 3002 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 3003 expression_sql = self.sql(expression, "expression") 3004 return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}" 3005 3006 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 3007 this = self.sql(expression, "this") 3008 delete = " DELETE" if expression.args.get("delete") else "" 3009 recompress = self.sql(expression, "recompress") 3010 recompress = f" RECOMPRESS {recompress}" if recompress else "" 3011 to_disk = self.sql(expression, "to_disk") 3012 to_disk = f" TO DISK {to_disk}" if to_disk else "" 3013 to_volume = self.sql(expression, "to_volume") 3014 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 3015 return f"{this}{delete}{recompress}{to_disk}{to_volume}" 3016 3017 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 3018 where = self.sql(expression, "where") 3019 group = self.sql(expression, "group") 3020 aggregates = self.expressions(expression, key="aggregates") 3021 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 3022 3023 if not (where or group or aggregates) and len(expression.expressions) == 1: 3024 return f"TTL {self.expressions(expression, flat=True)}" 3025 3026 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}" 3027 3028 def transaction_sql(self, expression: exp.Transaction) -> str: 3029 return "BEGIN" 3030 3031 def commit_sql(self, expression: exp.Commit) -> str: 3032 chain = expression.args.get("chain") 3033 if chain is not None: 3034 chain = " AND CHAIN" if chain else " AND NO CHAIN" 3035 3036 return f"COMMIT{chain or ''}" 3037 3038 def rollback_sql(self, expression: exp.Rollback) -> str: 3039 savepoint = expression.args.get("savepoint") 3040 savepoint = f" TO {savepoint}" if savepoint else "" 3041 return f"ROLLBACK{savepoint}" 3042 3043 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 3044 this = self.sql(expression, "this") 3045 3046 dtype = self.sql(expression, "dtype") 3047 if dtype: 3048 collate = self.sql(expression, "collate") 3049 collate = f" COLLATE {collate}" if collate else "" 3050 using = self.sql(expression, "using") 3051 using = f" USING {using}" if using else "" 3052 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 3053 3054 default = self.sql(expression, "default") 3055 if default: 3056 return f"ALTER COLUMN {this} SET DEFAULT {default}" 3057 3058 comment = self.sql(expression, "comment") 3059 if comment: 3060 return f"ALTER COLUMN {this} COMMENT {comment}" 3061 3062 allow_null = expression.args.get("allow_null") 3063 drop = expression.args.get("drop") 3064 3065 if not drop and not allow_null: 3066 self.unsupported("Unsupported ALTER COLUMN syntax") 3067 3068 if allow_null is not None: 3069 keyword = "DROP" if drop else "SET" 3070 return f"ALTER COLUMN {this} {keyword} NOT NULL" 3071 3072 return f"ALTER COLUMN {this} DROP DEFAULT" 3073 3074 def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str: 3075 this = self.sql(expression, "this") 3076 if not isinstance(expression.this, exp.Var): 3077 this = f"KEY DISTKEY {this}" 3078 return f"ALTER DISTSTYLE {this}" 3079 3080 def altersortkey_sql(self, expression: exp.AlterSortKey) -> str: 3081 compound = " COMPOUND" if expression.args.get("compound") else "" 3082 this = self.sql(expression, "this") 3083 expressions = self.expressions(expression, flat=True) 3084 expressions = f"({expressions})" if expressions else "" 3085 return f"ALTER{compound} SORTKEY {this or expressions}" 3086 3087 def renametable_sql(self, expression: exp.RenameTable) -> str: 3088 if not self.RENAME_TABLE_WITH_DB: 3089 # Remove db from tables 3090 expression = expression.transform( 3091 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 3092 ).assert_is(exp.RenameTable) 3093 this = self.sql(expression, "this") 3094 return f"RENAME TO {this}" 3095 3096 def renamecolumn_sql(self, expression: exp.RenameColumn) -> str: 3097 exists = " IF EXISTS" if expression.args.get("exists") else "" 3098 old_column = self.sql(expression, "this") 3099 new_column = self.sql(expression, "to") 3100 return f"RENAME COLUMN{exists} {old_column} TO {new_column}" 3101 3102 def alterset_sql(self, expression: exp.AlterSet) -> str: 3103 exprs = self.expressions(expression, flat=True) 3104 return f"SET {exprs}" 3105 3106 def altertable_sql(self, expression: exp.AlterTable) -> str: 3107 actions = expression.args["actions"] 3108 3109 if isinstance(actions[0], exp.ColumnDef): 3110 actions = self.add_column_sql(expression) 3111 elif isinstance(actions[0], exp.Schema): 3112 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 3113 elif isinstance(actions[0], exp.Delete): 3114 actions = self.expressions(expression, key="actions", flat=True) 3115 else: 3116 actions = self.expressions(expression, key="actions", flat=True) 3117 3118 exists = " IF EXISTS" if expression.args.get("exists") else "" 3119 on_cluster = self.sql(expression, "cluster") 3120 on_cluster = f" {on_cluster}" if on_cluster else "" 3121 only = " ONLY" if expression.args.get("only") else "" 3122 options = self.expressions(expression, key="options") 3123 options = f", {options}" if options else "" 3124 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{options}" 3125 3126 def add_column_sql(self, expression: exp.AlterTable) -> str: 3127 if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD: 3128 return self.expressions( 3129 expression, 3130 key="actions", 3131 prefix="ADD COLUMN ", 3132 skip_first=True, 3133 ) 3134 return f"ADD {self.expressions(expression, key='actions', flat=True)}" 3135 3136 def droppartition_sql(self, expression: exp.DropPartition) -> str: 3137 expressions = self.expressions(expression) 3138 exists = " IF EXISTS " if expression.args.get("exists") else " " 3139 return f"DROP{exists}{expressions}" 3140 3141 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 3142 return f"ADD {self.expressions(expression)}" 3143 3144 def distinct_sql(self, expression: exp.Distinct) -> str: 3145 this = self.expressions(expression, flat=True) 3146 3147 if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1: 3148 case = exp.case() 3149 for arg in expression.expressions: 3150 case = case.when(arg.is_(exp.null()), exp.null()) 3151 this = self.sql(case.else_(f"({this})")) 3152 3153 this = f" {this}" if this else "" 3154 3155 on = self.sql(expression, "on") 3156 on = f" ON {on}" if on else "" 3157 return f"DISTINCT{this}{on}" 3158 3159 def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: 3160 return self._embed_ignore_nulls(expression, "IGNORE NULLS") 3161 3162 def respectnulls_sql(self, expression: exp.RespectNulls) -> str: 3163 return self._embed_ignore_nulls(expression, "RESPECT NULLS") 3164 3165 def havingmax_sql(self, expression: exp.HavingMax) -> str: 3166 this_sql = self.sql(expression, "this") 3167 expression_sql = self.sql(expression, "expression") 3168 kind = "MAX" if expression.args.get("max") else "MIN" 3169 return f"{this_sql} HAVING {kind} {expression_sql}" 3170 3171 def intdiv_sql(self, expression: exp.IntDiv) -> str: 3172 return self.sql( 3173 exp.Cast( 3174 this=exp.Div(this=expression.this, expression=expression.expression), 3175 to=exp.DataType(this=exp.DataType.Type.INT), 3176 ) 3177 ) 3178 3179 def dpipe_sql(self, expression: exp.DPipe) -> str: 3180 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 3181 return self.func( 3182 "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten()) 3183 ) 3184 return self.binary(expression, "||") 3185 3186 def div_sql(self, expression: exp.Div) -> str: 3187 l, r = expression.left, expression.right 3188 3189 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 3190 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 3191 3192 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 3193 if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES): 3194 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 3195 3196 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 3197 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 3198 return self.sql( 3199 exp.cast( 3200 l / r, 3201 to=exp.DataType.Type.BIGINT, 3202 ) 3203 ) 3204 3205 return self.binary(expression, "/") 3206 3207 def overlaps_sql(self, expression: exp.Overlaps) -> str: 3208 return self.binary(expression, "OVERLAPS") 3209 3210 def distance_sql(self, expression: exp.Distance) -> str: 3211 return self.binary(expression, "<->") 3212 3213 def dot_sql(self, expression: exp.Dot) -> str: 3214 return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}" 3215 3216 def eq_sql(self, expression: exp.EQ) -> str: 3217 return self.binary(expression, "=") 3218 3219 def propertyeq_sql(self, expression: exp.PropertyEQ) -> str: 3220 return self.binary(expression, ":=") 3221 3222 def escape_sql(self, expression: exp.Escape) -> str: 3223 return self.binary(expression, "ESCAPE") 3224 3225 def glob_sql(self, expression: exp.Glob) -> str: 3226 return self.binary(expression, "GLOB") 3227 3228 def gt_sql(self, expression: exp.GT) -> str: 3229 return self.binary(expression, ">") 3230 3231 def gte_sql(self, expression: exp.GTE) -> str: 3232 return self.binary(expression, ">=") 3233 3234 def ilike_sql(self, expression: exp.ILike) -> str: 3235 return self.binary(expression, "ILIKE") 3236 3237 def ilikeany_sql(self, expression: exp.ILikeAny) -> str: 3238 return self.binary(expression, "ILIKE ANY") 3239 3240 def is_sql(self, expression: exp.Is) -> str: 3241 if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean): 3242 return self.sql( 3243 expression.this if expression.expression.this else exp.not_(expression.this) 3244 ) 3245 return self.binary(expression, "IS") 3246 3247 def like_sql(self, expression: exp.Like) -> str: 3248 return self.binary(expression, "LIKE") 3249 3250 def likeany_sql(self, expression: exp.LikeAny) -> str: 3251 return self.binary(expression, "LIKE ANY") 3252 3253 def similarto_sql(self, expression: exp.SimilarTo) -> str: 3254 return self.binary(expression, "SIMILAR TO") 3255 3256 def lt_sql(self, expression: exp.LT) -> str: 3257 return self.binary(expression, "<") 3258 3259 def lte_sql(self, expression: exp.LTE) -> str: 3260 return self.binary(expression, "<=") 3261 3262 def mod_sql(self, expression: exp.Mod) -> str: 3263 return self.binary(expression, "%") 3264 3265 def mul_sql(self, expression: exp.Mul) -> str: 3266 return self.binary(expression, "*") 3267 3268 def neq_sql(self, expression: exp.NEQ) -> str: 3269 return self.binary(expression, "<>") 3270 3271 def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str: 3272 return self.binary(expression, "IS NOT DISTINCT FROM") 3273 3274 def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str: 3275 return self.binary(expression, "IS DISTINCT FROM") 3276 3277 def slice_sql(self, expression: exp.Slice) -> str: 3278 return self.binary(expression, ":") 3279 3280 def sub_sql(self, expression: exp.Sub) -> str: 3281 return self.binary(expression, "-") 3282 3283 def trycast_sql(self, expression: exp.TryCast) -> str: 3284 return self.cast_sql(expression, safe_prefix="TRY_") 3285 3286 def try_sql(self, expression: exp.Try) -> str: 3287 if not self.TRY_SUPPORTED: 3288 self.unsupported("Unsupported TRY function") 3289 return self.sql(expression, "this") 3290 3291 return self.func("TRY", expression.this) 3292 3293 def log_sql(self, expression: exp.Log) -> str: 3294 this = expression.this 3295 expr = expression.expression 3296 3297 if self.dialect.LOG_BASE_FIRST is False: 3298 this, expr = expr, this 3299 elif self.dialect.LOG_BASE_FIRST is None and expr: 3300 if this.name in ("2", "10"): 3301 return self.func(f"LOG{this.name}", expr) 3302 3303 self.unsupported(f"Unsupported logarithm with base {self.sql(this)}") 3304 3305 return self.func("LOG", this, expr) 3306 3307 def use_sql(self, expression: exp.Use) -> str: 3308 kind = self.sql(expression, "kind") 3309 kind = f" {kind}" if kind else "" 3310 this = self.sql(expression, "this") 3311 this = f" {this}" if this else "" 3312 return f"USE{kind}{this}" 3313 3314 def binary(self, expression: exp.Binary, op: str) -> str: 3315 op = self.maybe_comment(op, comments=expression.comments) 3316 return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}" 3317 3318 def function_fallback_sql(self, expression: exp.Func) -> str: 3319 args = [] 3320 3321 for key in expression.arg_types: 3322 arg_value = expression.args.get(key) 3323 3324 if isinstance(arg_value, list): 3325 for value in arg_value: 3326 args.append(value) 3327 elif arg_value is not None: 3328 args.append(arg_value) 3329 3330 if self.normalize_functions: 3331 name = expression.sql_name() 3332 else: 3333 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 3334 3335 return self.func(name, *args) 3336 3337 def func( 3338 self, 3339 name: str, 3340 *args: t.Optional[exp.Expression | str], 3341 prefix: str = "(", 3342 suffix: str = ")", 3343 ) -> str: 3344 return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}" 3345 3346 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 3347 arg_sqls = tuple( 3348 self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool) 3349 ) 3350 if self.pretty and self.too_wide(arg_sqls): 3351 return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 3352 return ", ".join(arg_sqls) 3353 3354 def too_wide(self, args: t.Iterable) -> bool: 3355 return sum(len(arg) for arg in args) > self.max_text_width 3356 3357 def format_time( 3358 self, 3359 expression: exp.Expression, 3360 inverse_time_mapping: t.Optional[t.Dict[str, str]] = None, 3361 inverse_time_trie: t.Optional[t.Dict] = None, 3362 ) -> t.Optional[str]: 3363 return format_time( 3364 self.sql(expression, "format"), 3365 inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING, 3366 inverse_time_trie or self.dialect.INVERSE_TIME_TRIE, 3367 ) 3368 3369 def expressions( 3370 self, 3371 expression: t.Optional[exp.Expression] = None, 3372 key: t.Optional[str] = None, 3373 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 3374 flat: bool = False, 3375 indent: bool = True, 3376 skip_first: bool = False, 3377 skip_last: bool = False, 3378 sep: str = ", ", 3379 prefix: str = "", 3380 dynamic: bool = False, 3381 new_line: bool = False, 3382 ) -> str: 3383 expressions = expression.args.get(key or "expressions") if expression else sqls 3384 3385 if not expressions: 3386 return "" 3387 3388 if flat: 3389 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 3390 3391 num_sqls = len(expressions) 3392 result_sqls = [] 3393 3394 for i, e in enumerate(expressions): 3395 sql = self.sql(e, comment=False) 3396 if not sql: 3397 continue 3398 3399 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 3400 3401 if self.pretty: 3402 if self.leading_comma: 3403 result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}") 3404 else: 3405 result_sqls.append( 3406 f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}" 3407 ) 3408 else: 3409 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 3410 3411 if self.pretty and (not dynamic or self.too_wide(result_sqls)): 3412 if new_line: 3413 result_sqls.insert(0, "") 3414 result_sqls.append("") 3415 result_sql = "\n".join(s.rstrip() for s in result_sqls) 3416 else: 3417 result_sql = "".join(result_sqls) 3418 return ( 3419 self.indent(result_sql, skip_first=skip_first, skip_last=skip_last) 3420 if indent 3421 else result_sql 3422 ) 3423 3424 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 3425 flat = flat or isinstance(expression.parent, exp.Properties) 3426 expressions_sql = self.expressions(expression, flat=flat) 3427 if flat: 3428 return f"{op} {expressions_sql}" 3429 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}" 3430 3431 def naked_property(self, expression: exp.Property) -> str: 3432 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 3433 if not property_name: 3434 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 3435 return f"{property_name} {self.sql(expression, 'this')}" 3436 3437 def tag_sql(self, expression: exp.Tag) -> str: 3438 return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}" 3439 3440 def token_sql(self, token_type: TokenType) -> str: 3441 return self.TOKEN_MAPPING.get(token_type, token_type.name) 3442 3443 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 3444 this = self.sql(expression, "this") 3445 expressions = self.no_identify(self.expressions, expression) 3446 expressions = ( 3447 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 3448 ) 3449 return f"{this}{expressions}" 3450 3451 def joinhint_sql(self, expression: exp.JoinHint) -> str: 3452 this = self.sql(expression, "this") 3453 expressions = self.expressions(expression, flat=True) 3454 return f"{this}({expressions})" 3455 3456 def kwarg_sql(self, expression: exp.Kwarg) -> str: 3457 return self.binary(expression, "=>") 3458 3459 def when_sql(self, expression: exp.When) -> str: 3460 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 3461 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 3462 condition = self.sql(expression, "condition") 3463 condition = f" AND {condition}" if condition else "" 3464 3465 then_expression = expression.args.get("then") 3466 if isinstance(then_expression, exp.Insert): 3467 this = self.sql(then_expression, "this") 3468 this = f"INSERT {this}" if this else "INSERT" 3469 then = self.sql(then_expression, "expression") 3470 then = f"{this} VALUES {then}" if then else this 3471 elif isinstance(then_expression, exp.Update): 3472 if isinstance(then_expression.args.get("expressions"), exp.Star): 3473 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 3474 else: 3475 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 3476 else: 3477 then = self.sql(then_expression) 3478 return f"WHEN {matched}{source}{condition} THEN {then}" 3479 3480 def merge_sql(self, expression: exp.Merge) -> str: 3481 table = expression.this 3482 table_alias = "" 3483 3484 hints = table.args.get("hints") 3485 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 3486 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 3487 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 3488 3489 this = self.sql(table) 3490 using = f"USING {self.sql(expression, 'using')}" 3491 on = f"ON {self.sql(expression, 'on')}" 3492 expressions = self.expressions(expression, sep=" ", indent=False) 3493 sep = self.sep() 3494 3495 return self.prepend_ctes( 3496 expression, f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{expressions}" 3497 ) 3498 3499 def tochar_sql(self, expression: exp.ToChar) -> str: 3500 if expression.args.get("format"): 3501 self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function") 3502 3503 return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT)) 3504 3505 def tonumber_sql(self, expression: exp.ToNumber) -> str: 3506 if not self.SUPPORTS_TO_NUMBER: 3507 self.unsupported("Unsupported TO_NUMBER function") 3508 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3509 3510 fmt = expression.args.get("format") 3511 if not fmt: 3512 self.unsupported("Conversion format is required for TO_NUMBER") 3513 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3514 3515 return self.func("TO_NUMBER", expression.this, fmt) 3516 3517 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 3518 this = self.sql(expression, "this") 3519 kind = self.sql(expression, "kind") 3520 settings_sql = self.expressions(expression, key="settings", sep=" ") 3521 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 3522 return f"{this}({kind}{args})" 3523 3524 def dictrange_sql(self, expression: exp.DictRange) -> str: 3525 this = self.sql(expression, "this") 3526 max = self.sql(expression, "max") 3527 min = self.sql(expression, "min") 3528 return f"{this}(MIN {min} MAX {max})" 3529 3530 def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str: 3531 return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}" 3532 3533 def oncluster_sql(self, expression: exp.OnCluster) -> str: 3534 return "" 3535 3536 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3537 expressions = self.expressions(expression, key="expressions", flat=True) 3538 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3539 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3540 buckets = self.sql(expression, "buckets") 3541 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS" 3542 3543 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3544 this = self.sql(expression, "this") 3545 having = self.sql(expression, "having") 3546 3547 if having: 3548 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3549 3550 return self.func("ANY_VALUE", this) 3551 3552 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3553 transform = self.func("TRANSFORM", *expression.expressions) 3554 row_format_before = self.sql(expression, "row_format_before") 3555 row_format_before = f" {row_format_before}" if row_format_before else "" 3556 record_writer = self.sql(expression, "record_writer") 3557 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3558 using = f" USING {self.sql(expression, 'command_script')}" 3559 schema = self.sql(expression, "schema") 3560 schema = f" AS {schema}" if schema else "" 3561 row_format_after = self.sql(expression, "row_format_after") 3562 row_format_after = f" {row_format_after}" if row_format_after else "" 3563 record_reader = self.sql(expression, "record_reader") 3564 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3565 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}" 3566 3567 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3568 key_block_size = self.sql(expression, "key_block_size") 3569 if key_block_size: 3570 return f"KEY_BLOCK_SIZE = {key_block_size}" 3571 3572 using = self.sql(expression, "using") 3573 if using: 3574 return f"USING {using}" 3575 3576 parser = self.sql(expression, "parser") 3577 if parser: 3578 return f"WITH PARSER {parser}" 3579 3580 comment = self.sql(expression, "comment") 3581 if comment: 3582 return f"COMMENT {comment}" 3583 3584 visible = expression.args.get("visible") 3585 if visible is not None: 3586 return "VISIBLE" if visible else "INVISIBLE" 3587 3588 engine_attr = self.sql(expression, "engine_attr") 3589 if engine_attr: 3590 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3591 3592 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3593 if secondary_engine_attr: 3594 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3595 3596 self.unsupported("Unsupported index constraint option.") 3597 return "" 3598 3599 def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str: 3600 enforced = " ENFORCED" if expression.args.get("enforced") else "" 3601 return f"CHECK ({self.sql(expression, 'this')}){enforced}" 3602 3603 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3604 kind = self.sql(expression, "kind") 3605 kind = f"{kind} INDEX" if kind else "INDEX" 3606 this = self.sql(expression, "this") 3607 this = f" {this}" if this else "" 3608 index_type = self.sql(expression, "index_type") 3609 index_type = f" USING {index_type}" if index_type else "" 3610 expressions = self.expressions(expression, flat=True) 3611 expressions = f" ({expressions})" if expressions else "" 3612 options = self.expressions(expression, key="options", sep=" ") 3613 options = f" {options}" if options else "" 3614 return f"{kind}{this}{index_type}{expressions}{options}" 3615 3616 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3617 if self.NVL2_SUPPORTED: 3618 return self.function_fallback_sql(expression) 3619 3620 case = exp.Case().when( 3621 expression.this.is_(exp.null()).not_(copy=False), 3622 expression.args["true"], 3623 copy=False, 3624 ) 3625 else_cond = expression.args.get("false") 3626 if else_cond: 3627 case.else_(else_cond, copy=False) 3628 3629 return self.sql(case) 3630 3631 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3632 this = self.sql(expression, "this") 3633 expr = self.sql(expression, "expression") 3634 iterator = self.sql(expression, "iterator") 3635 condition = self.sql(expression, "condition") 3636 condition = f" IF {condition}" if condition else "" 3637 return f"{this} FOR {expr} IN {iterator}{condition}" 3638 3639 def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str: 3640 return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})" 3641 3642 def opclass_sql(self, expression: exp.Opclass) -> str: 3643 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 3644 3645 def predict_sql(self, expression: exp.Predict) -> str: 3646 model = self.sql(expression, "this") 3647 model = f"MODEL {model}" 3648 table = self.sql(expression, "expression") 3649 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3650 parameters = self.sql(expression, "params_struct") 3651 return self.func("PREDICT", model, table, parameters or None) 3652 3653 def forin_sql(self, expression: exp.ForIn) -> str: 3654 this = self.sql(expression, "this") 3655 expression_sql = self.sql(expression, "expression") 3656 return f"FOR {this} DO {expression_sql}" 3657 3658 def refresh_sql(self, expression: exp.Refresh) -> str: 3659 this = self.sql(expression, "this") 3660 table = "" if isinstance(expression.this, exp.Literal) else "TABLE " 3661 return f"REFRESH {table}{this}" 3662 3663 def operator_sql(self, expression: exp.Operator) -> str: 3664 return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})") 3665 3666 def toarray_sql(self, expression: exp.ToArray) -> str: 3667 arg = expression.this 3668 if not arg.type: 3669 from sqlglot.optimizer.annotate_types import annotate_types 3670 3671 arg = annotate_types(arg) 3672 3673 if arg.is_type(exp.DataType.Type.ARRAY): 3674 return self.sql(arg) 3675 3676 cond_for_null = arg.is_(exp.null()) 3677 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False))) 3678 3679 def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str: 3680 this = expression.this 3681 if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME): 3682 return self.sql(this) 3683 3684 return self.sql(exp.cast(this, exp.DataType.Type.TIME)) 3685 3686 def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str: 3687 this = expression.this 3688 if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP): 3689 return self.sql(this) 3690 3691 return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP)) 3692 3693 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3694 this = expression.this 3695 time_format = self.format_time(expression) 3696 3697 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3698 return self.sql( 3699 exp.cast( 3700 exp.StrToTime(this=this, format=expression.args["format"]), 3701 exp.DataType.Type.DATE, 3702 ) 3703 ) 3704 3705 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3706 return self.sql(this) 3707 3708 return self.sql(exp.cast(this, exp.DataType.Type.DATE)) 3709 3710 def unixdate_sql(self, expression: exp.UnixDate) -> str: 3711 return self.sql( 3712 exp.func( 3713 "DATEDIFF", 3714 expression.this, 3715 exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE), 3716 "day", 3717 ) 3718 ) 3719 3720 def lastday_sql(self, expression: exp.LastDay) -> str: 3721 if self.LAST_DAY_SUPPORTS_DATE_PART: 3722 return self.function_fallback_sql(expression) 3723 3724 unit = expression.text("unit") 3725 if unit and unit != "MONTH": 3726 self.unsupported("Date parts are not supported in LAST_DAY.") 3727 3728 return self.func("LAST_DAY", expression.this) 3729 3730 def dateadd_sql(self, expression: exp.DateAdd) -> str: 3731 from sqlglot.dialects.dialect import unit_to_str 3732 3733 return self.func( 3734 "DATE_ADD", expression.this, expression.expression, unit_to_str(expression) 3735 ) 3736 3737 def arrayany_sql(self, expression: exp.ArrayAny) -> str: 3738 if self.CAN_IMPLEMENT_ARRAY_ANY: 3739 filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression) 3740 filtered_not_empty = exp.ArraySize(this=filtered).neq(0) 3741 original_is_empty = exp.ArraySize(this=expression.this).eq(0) 3742 return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty))) 3743 3744 from sqlglot.dialects import Dialect 3745 3746 # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect 3747 if self.dialect.__class__ != Dialect: 3748 self.unsupported("ARRAY_ANY is unsupported") 3749 3750 return self.function_fallback_sql(expression) 3751 3752 def struct_sql(self, expression: exp.Struct) -> str: 3753 expression.set( 3754 "expressions", 3755 [ 3756 exp.alias_(e.expression, e.name if e.this.is_string else e.this) 3757 if isinstance(e, exp.PropertyEQ) 3758 else e 3759 for e in expression.expressions 3760 ], 3761 ) 3762 3763 return self.function_fallback_sql(expression) 3764 3765 def partitionrange_sql(self, expression: exp.PartitionRange) -> str: 3766 low = self.sql(expression, "this") 3767 high = self.sql(expression, "expression") 3768 3769 return f"{low} TO {high}" 3770 3771 def truncatetable_sql(self, expression: exp.TruncateTable) -> str: 3772 target = "DATABASE" if expression.args.get("is_database") else "TABLE" 3773 tables = f" {self.expressions(expression)}" 3774 3775 exists = " IF EXISTS" if expression.args.get("exists") else "" 3776 3777 on_cluster = self.sql(expression, "cluster") 3778 on_cluster = f" {on_cluster}" if on_cluster else "" 3779 3780 identity = self.sql(expression, "identity") 3781 identity = f" {identity} IDENTITY" if identity else "" 3782 3783 option = self.sql(expression, "option") 3784 option = f" {option}" if option else "" 3785 3786 partition = self.sql(expression, "partition") 3787 partition = f" {partition}" if partition else "" 3788 3789 return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}" 3790 3791 # This transpiles T-SQL's CONVERT function 3792 # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16 3793 def convert_sql(self, expression: exp.Convert) -> str: 3794 to = expression.this 3795 value = expression.expression 3796 style = expression.args.get("style") 3797 safe = expression.args.get("safe") 3798 strict = expression.args.get("strict") 3799 3800 if not to or not value: 3801 return "" 3802 3803 # Retrieve length of datatype and override to default if not specified 3804 if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3805 to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False) 3806 3807 transformed: t.Optional[exp.Expression] = None 3808 cast = exp.Cast if strict else exp.TryCast 3809 3810 # Check whether a conversion with format (T-SQL calls this 'style') is applicable 3811 if isinstance(style, exp.Literal) and style.is_int: 3812 from sqlglot.dialects.tsql import TSQL 3813 3814 style_value = style.name 3815 converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value) 3816 if not converted_style: 3817 self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}") 3818 3819 fmt = exp.Literal.string(converted_style) 3820 3821 if to.this == exp.DataType.Type.DATE: 3822 transformed = exp.StrToDate(this=value, format=fmt) 3823 elif to.this == exp.DataType.Type.DATETIME: 3824 transformed = exp.StrToTime(this=value, format=fmt) 3825 elif to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3826 transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe) 3827 elif to.this == exp.DataType.Type.TEXT: 3828 transformed = exp.TimeToStr(this=value, format=fmt) 3829 3830 if not transformed: 3831 transformed = cast(this=value, to=to, safe=safe) 3832 3833 return self.sql(transformed) 3834 3835 def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str: 3836 this = expression.this 3837 if isinstance(this, exp.JSONPathWildcard): 3838 this = self.json_path_part(this) 3839 return f".{this}" if this else "" 3840 3841 if exp.SAFE_IDENTIFIER_RE.match(this): 3842 return f".{this}" 3843 3844 this = self.json_path_part(this) 3845 return f"[{this}]" if self.JSON_PATH_BRACKETED_KEY_SUPPORTED else f".{this}" 3846 3847 def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str: 3848 this = self.json_path_part(expression.this) 3849 return f"[{this}]" if this else "" 3850 3851 def _simplify_unless_literal(self, expression: E) -> E: 3852 if not isinstance(expression, exp.Literal): 3853 from sqlglot.optimizer.simplify import simplify 3854 3855 expression = simplify(expression, dialect=self.dialect) 3856 3857 return expression 3858 3859 def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str: 3860 if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"): 3861 # The first modifier here will be the one closest to the AggFunc's arg 3862 mods = sorted( 3863 expression.find_all(exp.HavingMax, exp.Order, exp.Limit), 3864 key=lambda x: 0 3865 if isinstance(x, exp.HavingMax) 3866 else (1 if isinstance(x, exp.Order) else 2), 3867 ) 3868 3869 if mods: 3870 mod = mods[0] 3871 this = expression.__class__(this=mod.this.copy()) 3872 this.meta["inline"] = True 3873 mod.this.replace(this) 3874 return self.sql(expression.this) 3875 3876 agg_func = expression.find(exp.AggFunc) 3877 3878 if agg_func: 3879 return self.sql(agg_func)[:-1] + f" {text})" 3880 3881 return f"{self.sql(expression, 'this')} {text}" 3882 3883 def _replace_line_breaks(self, string: str) -> str: 3884 """We don't want to extra indent line breaks so we temporarily replace them with sentinels.""" 3885 if self.pretty: 3886 return string.replace("\n", self.SENTINEL_LINE_BREAK) 3887 return string 3888 3889 def copyparameter_sql(self, expression: exp.CopyParameter) -> str: 3890 option = self.sql(expression, "this") 3891 3892 if expression.expressions: 3893 upper = option.upper() 3894 3895 # Snowflake FILE_FORMAT options are separated by whitespace 3896 sep = " " if upper == "FILE_FORMAT" else ", " 3897 3898 # Databricks copy/format options do not set their list of values with EQ 3899 op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = " 3900 values = self.expressions(expression, flat=True, sep=sep) 3901 return f"{option}{op}({values})" 3902 3903 value = self.sql(expression, "expression") 3904 3905 if not value: 3906 return option 3907 3908 op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " " 3909 3910 return f"{option}{op}{value}" 3911 3912 def credentials_sql(self, expression: exp.Credentials) -> str: 3913 cred_expr = expression.args.get("credentials") 3914 if isinstance(cred_expr, exp.Literal): 3915 # Redshift case: CREDENTIALS <string> 3916 credentials = self.sql(expression, "credentials") 3917 credentials = f"CREDENTIALS {credentials}" if credentials else "" 3918 else: 3919 # Snowflake case: CREDENTIALS = (...) 3920 credentials = self.expressions(expression, key="credentials", flat=True, sep=" ") 3921 credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else "" 3922 3923 storage = self.sql(expression, "storage") 3924 storage = f"STORAGE_INTEGRATION = {storage}" if storage else "" 3925 3926 encryption = self.expressions(expression, key="encryption", flat=True, sep=" ") 3927 encryption = f" ENCRYPTION = ({encryption})" if encryption else "" 3928 3929 iam_role = self.sql(expression, "iam_role") 3930 iam_role = f"IAM_ROLE {iam_role}" if iam_role else "" 3931 3932 region = self.sql(expression, "region") 3933 region = f" REGION {region}" if region else "" 3934 3935 return f"{credentials}{storage}{encryption}{iam_role}{region}" 3936 3937 def copy_sql(self, expression: exp.Copy) -> str: 3938 this = self.sql(expression, "this") 3939 this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}" 3940 3941 credentials = self.sql(expression, "credentials") 3942 credentials = self.seg(credentials) if credentials else "" 3943 kind = self.seg("FROM" if expression.args.get("kind") else "TO") 3944 files = self.expressions(expression, key="files", flat=True) 3945 3946 sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " " 3947 params = self.expressions( 3948 expression, 3949 key="params", 3950 sep=sep, 3951 new_line=True, 3952 skip_last=True, 3953 skip_first=True, 3954 indent=self.COPY_PARAMS_ARE_WRAPPED, 3955 ) 3956 3957 if params: 3958 if self.COPY_PARAMS_ARE_WRAPPED: 3959 params = f" WITH ({params})" 3960 elif not self.pretty: 3961 params = f" {params}" 3962 3963 return f"COPY{this}{kind} {files}{credentials}{params}" 3964 3965 def semicolon_sql(self, expression: exp.Semicolon) -> str: 3966 return "" 3967 3968 def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str: 3969 on_sql = "ON" if expression.args.get("on") else "OFF" 3970 filter_col: t.Optional[str] = self.sql(expression, "filter_column") 3971 filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None 3972 retention_period: t.Optional[str] = self.sql(expression, "retention_period") 3973 retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None 3974 3975 if filter_col or retention_period: 3976 on_sql = self.func("ON", filter_col, retention_period) 3977 3978 return f"DATA_DELETION={on_sql}" 3979 3980 def maskingpolicycolumnconstraint_sql( 3981 self, expression: exp.MaskingPolicyColumnConstraint 3982 ) -> str: 3983 this = self.sql(expression, "this") 3984 expressions = self.expressions(expression, flat=True) 3985 expressions = f" USING ({expressions})" if expressions else "" 3986 return f"MASKING POLICY {this}{expressions}" 3987 3988 def gapfill_sql(self, expression: exp.GapFill) -> str: 3989 this = self.sql(expression, "this") 3990 this = f"TABLE {this}" 3991 return self.func("GAP_FILL", this, *[v for k, v in expression.args.items() if k != "this"]) 3992 3993 def scope_resolution(self, rhs: str, scope_name: str) -> str: 3994 return self.func("SCOPE_RESOLUTION", scope_name or None, rhs) 3995 3996 def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str: 3997 this = self.sql(expression, "this") 3998 expr = expression.expression 3999 4000 if isinstance(expr, exp.Func): 4001 # T-SQL's CLR functions are case sensitive 4002 expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})" 4003 else: 4004 expr = self.sql(expression, "expression") 4005 4006 return self.scope_resolution(expr, this) 4007 4008 def parsejson_sql(self, expression: exp.ParseJSON) -> str: 4009 if self.PARSE_JSON_NAME is None: 4010 return self.sql(expression.this) 4011 4012 return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression) 4013 4014 def rand_sql(self, expression: exp.Rand) -> str: 4015 lower = self.sql(expression, "lower") 4016 upper = self.sql(expression, "upper") 4017 4018 if lower and upper: 4019 return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}" 4020 return self.func("RAND", expression.this) 4021 4022 def changes_sql(self, expression: exp.Changes) -> str: 4023 information = self.sql(expression, "information") 4024 information = f"INFORMATION => {information}" 4025 at_before = self.sql(expression, "at_before") 4026 at_before = f"{self.seg('')}{at_before}" if at_before else "" 4027 end = self.sql(expression, "end") 4028 end = f"{self.seg('')}{end}" if end else "" 4029 4030 return f"CHANGES ({information}){at_before}{end}" 4031 4032 def pad_sql(self, expression: exp.Pad) -> str: 4033 prefix = "L" if expression.args.get("is_left") else "R" 4034 4035 fill_pattern = self.sql(expression, "fill_pattern") or None 4036 if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED: 4037 fill_pattern = "' '" 4038 4039 return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern) 4040 4041 def summarize_sql(self, expression: exp.Summarize) -> str: 4042 table = " TABLE" if expression.args.get("table") else "" 4043 return f"SUMMARIZE{table} {self.sql(expression.this)}"
logger =
<Logger sqlglot (WARNING)>
ESCAPED_UNICODE_RE =
re.compile('\\\\(\\d+)')
class
Generator:
37class Generator(metaclass=_Generator): 38 """ 39 Generator converts a given syntax tree to the corresponding SQL string. 40 41 Args: 42 pretty: Whether to format the produced SQL string. 43 Default: False. 44 identify: Determines when an identifier should be quoted. Possible values are: 45 False (default): Never quote, except in cases where it's mandatory by the dialect. 46 True or 'always': Always quote. 47 'safe': Only quote identifiers that are case insensitive. 48 normalize: Whether to normalize identifiers to lowercase. 49 Default: False. 50 pad: The pad size in a formatted string. For example, this affects the indentation of 51 a projection in a query, relative to its nesting level. 52 Default: 2. 53 indent: The indentation size in a formatted string. For example, this affects the 54 indentation of subqueries and filters under a `WHERE` clause. 55 Default: 2. 56 normalize_functions: How to normalize function names. Possible values are: 57 "upper" or True (default): Convert names to uppercase. 58 "lower": Convert names to lowercase. 59 False: Disables function name normalization. 60 unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. 61 Default ErrorLevel.WARN. 62 max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. 63 This is only relevant if unsupported_level is ErrorLevel.RAISE. 64 Default: 3 65 leading_comma: Whether the comma is leading or trailing in select expressions. 66 This is only relevant when generating in pretty mode. 67 Default: False 68 max_text_width: The max number of characters in a segment before creating new lines in pretty mode. 69 The default is on the smaller end because the length only represents a segment and not the true 70 line length. 71 Default: 80 72 comments: Whether to preserve comments in the output SQL code. 73 Default: True 74 """ 75 76 TRANSFORMS: t.Dict[t.Type[exp.Expression], t.Callable[..., str]] = { 77 **JSON_PATH_PART_TRANSFORMS, 78 exp.AllowedValuesProperty: lambda self, 79 e: f"ALLOWED_VALUES {self.expressions(e, flat=True)}", 80 exp.AutoRefreshProperty: lambda self, e: f"AUTO REFRESH {self.sql(e, 'this')}", 81 exp.BackupProperty: lambda self, e: f"BACKUP {self.sql(e, 'this')}", 82 exp.CaseSpecificColumnConstraint: lambda _, 83 e: f"{'NOT ' if e.args.get('not_') else ''}CASESPECIFIC", 84 exp.CharacterSetColumnConstraint: lambda self, e: f"CHARACTER SET {self.sql(e, 'this')}", 85 exp.CharacterSetProperty: lambda self, 86 e: f"{'DEFAULT ' if e.args.get('default') else ''}CHARACTER SET={self.sql(e, 'this')}", 87 exp.ClusteredColumnConstraint: lambda self, 88 e: f"CLUSTERED ({self.expressions(e, 'this', indent=False)})", 89 exp.CollateColumnConstraint: lambda self, e: f"COLLATE {self.sql(e, 'this')}", 90 exp.CommentColumnConstraint: lambda self, e: f"COMMENT {self.sql(e, 'this')}", 91 exp.ConnectByRoot: lambda self, e: f"CONNECT_BY_ROOT {self.sql(e, 'this')}", 92 exp.CopyGrantsProperty: lambda *_: "COPY GRANTS", 93 exp.DateFormatColumnConstraint: lambda self, e: f"FORMAT {self.sql(e, 'this')}", 94 exp.DefaultColumnConstraint: lambda self, e: f"DEFAULT {self.sql(e, 'this')}", 95 exp.DynamicProperty: lambda *_: "DYNAMIC", 96 exp.EncodeColumnConstraint: lambda self, e: f"ENCODE {self.sql(e, 'this')}", 97 exp.EphemeralColumnConstraint: lambda self, 98 e: f"EPHEMERAL{(' ' + self.sql(e, 'this')) if e.this else ''}", 99 exp.ExcludeColumnConstraint: lambda self, e: f"EXCLUDE {self.sql(e, 'this').lstrip()}", 100 exp.ExecuteAsProperty: lambda self, e: self.naked_property(e), 101 exp.ExternalProperty: lambda *_: "EXTERNAL", 102 exp.GlobalProperty: lambda *_: "GLOBAL", 103 exp.HeapProperty: lambda *_: "HEAP", 104 exp.IcebergProperty: lambda *_: "ICEBERG", 105 exp.InheritsProperty: lambda self, e: f"INHERITS ({self.expressions(e, flat=True)})", 106 exp.InlineLengthColumnConstraint: lambda self, e: f"INLINE LENGTH {self.sql(e, 'this')}", 107 exp.InputModelProperty: lambda self, e: f"INPUT{self.sql(e, 'this')}", 108 exp.IntervalSpan: lambda self, e: f"{self.sql(e, 'this')} TO {self.sql(e, 'expression')}", 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.ProjectionPolicyColumnConstraint: lambda self, 124 e: f"PROJECTION POLICY {self.sql(e, 'this')}", 125 exp.RemoteWithConnectionModelProperty: lambda self, 126 e: f"REMOTE WITH CONNECTION {self.sql(e, 'this')}", 127 exp.ReturnsProperty: lambda self, e: ( 128 "RETURNS NULL ON NULL INPUT" if e.args.get("null") else self.naked_property(e) 129 ), 130 exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}", 131 exp.SecureProperty: lambda *_: "SECURE", 132 exp.SetConfigProperty: lambda self, e: self.sql(e, "this"), 133 exp.SetProperty: lambda _, e: f"{'MULTI' if e.args.get('multi') else ''}SET", 134 exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}", 135 exp.SharingProperty: lambda self, e: f"SHARING={self.sql(e, 'this')}", 136 exp.SqlReadWriteProperty: lambda _, e: e.name, 137 exp.SqlSecurityProperty: lambda _, 138 e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}", 139 exp.StabilityProperty: lambda _, e: e.name, 140 exp.StrictProperty: lambda *_: "STRICT", 141 exp.TemporaryProperty: lambda *_: "TEMPORARY", 142 exp.TagColumnConstraint: lambda self, e: f"TAG ({self.expressions(e, flat=True)})", 143 exp.TitleColumnConstraint: lambda self, e: f"TITLE {self.sql(e, 'this')}", 144 exp.ToMap: lambda self, e: f"MAP {self.sql(e, 'this')}", 145 exp.ToTableProperty: lambda self, e: f"TO {self.sql(e.this)}", 146 exp.TransformModelProperty: lambda self, e: self.func("TRANSFORM", *e.expressions), 147 exp.TransientProperty: lambda *_: "TRANSIENT", 148 exp.UppercaseColumnConstraint: lambda *_: "UPPERCASE", 149 exp.UnloggedProperty: lambda *_: "UNLOGGED", 150 exp.VarMap: lambda self, e: self.func("MAP", e.args["keys"], e.args["values"]), 151 exp.ViewAttributeProperty: lambda self, e: f"WITH {self.sql(e, 'this')}", 152 exp.VolatileProperty: lambda *_: "VOLATILE", 153 exp.WithJournalTableProperty: lambda self, e: f"WITH JOURNAL TABLE={self.sql(e, 'this')}", 154 exp.WithSchemaBindingProperty: lambda self, e: f"WITH SCHEMA {self.sql(e, 'this')}", 155 exp.WithOperator: lambda self, e: f"{self.sql(e, 'this')} WITH {self.sql(e, 'op')}", 156 } 157 158 # Whether null ordering is supported in order by 159 # True: Full Support, None: No support, False: No support in window specifications 160 NULL_ORDERING_SUPPORTED: t.Optional[bool] = True 161 162 # Whether ignore nulls is inside the agg or outside. 163 # FIRST(x IGNORE NULLS) OVER vs FIRST (x) IGNORE NULLS OVER 164 IGNORE_NULLS_IN_FUNC = False 165 166 # Whether locking reads (i.e. SELECT ... FOR UPDATE/SHARE) are supported 167 LOCKING_READS_SUPPORTED = False 168 169 # Always do <set op> distinct or <set op> all 170 EXPLICIT_SET_OP = False 171 172 # Wrap derived values in parens, usually standard but spark doesn't support it 173 WRAP_DERIVED_VALUES = True 174 175 # Whether create function uses an AS before the RETURN 176 CREATE_FUNCTION_RETURN_AS = True 177 178 # Whether MERGE ... WHEN MATCHED BY SOURCE is allowed 179 MATCHED_BY_SOURCE = True 180 181 # Whether the INTERVAL expression works only with values like '1 day' 182 SINGLE_STRING_INTERVAL = False 183 184 # Whether the plural form of date parts like day (i.e. "days") is supported in INTERVALs 185 INTERVAL_ALLOWS_PLURAL_FORM = True 186 187 # Whether limit and fetch are supported (possible values: "ALL", "LIMIT", "FETCH") 188 LIMIT_FETCH = "ALL" 189 190 # Whether limit and fetch allows expresions or just limits 191 LIMIT_ONLY_LITERALS = False 192 193 # Whether a table is allowed to be renamed with a db 194 RENAME_TABLE_WITH_DB = True 195 196 # The separator for grouping sets and rollups 197 GROUPINGS_SEP = "," 198 199 # The string used for creating an index on a table 200 INDEX_ON = "ON" 201 202 # Whether join hints should be generated 203 JOIN_HINTS = True 204 205 # Whether table hints should be generated 206 TABLE_HINTS = True 207 208 # Whether query hints should be generated 209 QUERY_HINTS = True 210 211 # What kind of separator to use for query hints 212 QUERY_HINT_SEP = ", " 213 214 # Whether comparing against booleans (e.g. x IS TRUE) is supported 215 IS_BOOL_ALLOWED = True 216 217 # Whether to include the "SET" keyword in the "INSERT ... ON DUPLICATE KEY UPDATE" statement 218 DUPLICATE_KEY_UPDATE_WITH_SET = True 219 220 # Whether to generate the limit as TOP <value> instead of LIMIT <value> 221 LIMIT_IS_TOP = False 222 223 # Whether to generate INSERT INTO ... RETURNING or INSERT INTO RETURNING ... 224 RETURNING_END = True 225 226 # Whether to generate an unquoted value for EXTRACT's date part argument 227 EXTRACT_ALLOWS_QUOTES = True 228 229 # Whether TIMETZ / TIMESTAMPTZ will be generated using the "WITH TIME ZONE" syntax 230 TZ_TO_WITH_TIME_ZONE = False 231 232 # Whether the NVL2 function is supported 233 NVL2_SUPPORTED = True 234 235 # https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax 236 SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE") 237 238 # Whether VALUES statements can be used as derived tables. 239 # MySQL 5 and Redshift do not allow this, so when False, it will convert 240 # SELECT * VALUES into SELECT UNION 241 VALUES_AS_TABLE = True 242 243 # Whether the word COLUMN is included when adding a column with ALTER TABLE 244 ALTER_TABLE_INCLUDE_COLUMN_KEYWORD = True 245 246 # UNNEST WITH ORDINALITY (presto) instead of UNNEST WITH OFFSET (bigquery) 247 UNNEST_WITH_ORDINALITY = True 248 249 # Whether FILTER (WHERE cond) can be used for conditional aggregation 250 AGGREGATE_FILTER_SUPPORTED = True 251 252 # Whether JOIN sides (LEFT, RIGHT) are supported in conjunction with SEMI/ANTI join kinds 253 SEMI_ANTI_JOIN_WITH_SIDE = True 254 255 # Whether to include the type of a computed column in the CREATE DDL 256 COMPUTED_COLUMN_WITH_TYPE = True 257 258 # Whether CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY 259 SUPPORTS_TABLE_COPY = True 260 261 # Whether parentheses are required around the table sample's expression 262 TABLESAMPLE_REQUIRES_PARENS = True 263 264 # Whether a table sample clause's size needs to be followed by the ROWS keyword 265 TABLESAMPLE_SIZE_IS_ROWS = True 266 267 # The keyword(s) to use when generating a sample clause 268 TABLESAMPLE_KEYWORDS = "TABLESAMPLE" 269 270 # Whether the TABLESAMPLE clause supports a method name, like BERNOULLI 271 TABLESAMPLE_WITH_METHOD = True 272 273 # The keyword to use when specifying the seed of a sample clause 274 TABLESAMPLE_SEED_KEYWORD = "SEED" 275 276 # Whether COLLATE is a function instead of a binary operator 277 COLLATE_IS_FUNC = False 278 279 # Whether data types support additional specifiers like e.g. CHAR or BYTE (oracle) 280 DATA_TYPE_SPECIFIERS_ALLOWED = False 281 282 # Whether conditions require booleans WHERE x = 0 vs WHERE x 283 ENSURE_BOOLS = False 284 285 # Whether the "RECURSIVE" keyword is required when defining recursive CTEs 286 CTE_RECURSIVE_KEYWORD_REQUIRED = True 287 288 # Whether CONCAT requires >1 arguments 289 SUPPORTS_SINGLE_ARG_CONCAT = True 290 291 # Whether LAST_DAY function supports a date part argument 292 LAST_DAY_SUPPORTS_DATE_PART = True 293 294 # Whether named columns are allowed in table aliases 295 SUPPORTS_TABLE_ALIAS_COLUMNS = True 296 297 # Whether UNPIVOT aliases are Identifiers (False means they're Literals) 298 UNPIVOT_ALIASES_ARE_IDENTIFIERS = True 299 300 # What delimiter to use for separating JSON key/value pairs 301 JSON_KEY_VALUE_PAIR_SEP = ":" 302 303 # INSERT OVERWRITE TABLE x override 304 INSERT_OVERWRITE = " OVERWRITE TABLE" 305 306 # Whether the SELECT .. INTO syntax is used instead of CTAS 307 SUPPORTS_SELECT_INTO = False 308 309 # Whether UNLOGGED tables can be created 310 SUPPORTS_UNLOGGED_TABLES = False 311 312 # Whether the CREATE TABLE LIKE statement is supported 313 SUPPORTS_CREATE_TABLE_LIKE = True 314 315 # Whether the LikeProperty needs to be specified inside of the schema clause 316 LIKE_PROPERTY_INSIDE_SCHEMA = False 317 318 # Whether DISTINCT can be followed by multiple args in an AggFunc. If not, it will be 319 # transpiled into a series of CASE-WHEN-ELSE, ultimately using a tuple conseisting of the args 320 MULTI_ARG_DISTINCT = True 321 322 # Whether the JSON extraction operators expect a value of type JSON 323 JSON_TYPE_REQUIRED_FOR_EXTRACTION = False 324 325 # Whether bracketed keys like ["foo"] are supported in JSON paths 326 JSON_PATH_BRACKETED_KEY_SUPPORTED = True 327 328 # Whether to escape keys using single quotes in JSON paths 329 JSON_PATH_SINGLE_QUOTE_ESCAPE = False 330 331 # The JSONPathPart expressions supported by this dialect 332 SUPPORTED_JSON_PATH_PARTS = ALL_JSON_PATH_PARTS.copy() 333 334 # Whether any(f(x) for x in array) can be implemented by this dialect 335 CAN_IMPLEMENT_ARRAY_ANY = False 336 337 # Whether the function TO_NUMBER is supported 338 SUPPORTS_TO_NUMBER = True 339 340 # Whether or not set op modifiers apply to the outer set op or select. 341 # SELECT * FROM x UNION SELECT * FROM y LIMIT 1 342 # True means limit 1 happens after the set op, False means it it happens on y. 343 SET_OP_MODIFIERS = True 344 345 # Whether parameters from COPY statement are wrapped in parentheses 346 COPY_PARAMS_ARE_WRAPPED = True 347 348 # Whether values of params are set with "=" token or empty space 349 COPY_PARAMS_EQ_REQUIRED = False 350 351 # Whether COPY statement has INTO keyword 352 COPY_HAS_INTO_KEYWORD = True 353 354 # Whether the conditional TRY(expression) function is supported 355 TRY_SUPPORTED = True 356 357 # Whether the UESCAPE syntax in unicode strings is supported 358 SUPPORTS_UESCAPE = True 359 360 # The keyword to use when generating a star projection with excluded columns 361 STAR_EXCEPT = "EXCEPT" 362 363 # The HEX function name 364 HEX_FUNC = "HEX" 365 366 # The keywords to use when prefixing & separating WITH based properties 367 WITH_PROPERTIES_PREFIX = "WITH" 368 369 # Whether to quote the generated expression of exp.JsonPath 370 QUOTE_JSON_PATH = True 371 372 # Whether the text pattern/fill (3rd) parameter of RPAD()/LPAD() is optional (defaults to space) 373 PAD_FILL_PATTERN_IS_REQUIRED = False 374 375 # The name to generate for the JSONPath expression. If `None`, only `this` will be generated 376 PARSE_JSON_NAME: t.Optional[str] = "PARSE_JSON" 377 378 TYPE_MAPPING = { 379 exp.DataType.Type.NCHAR: "CHAR", 380 exp.DataType.Type.NVARCHAR: "VARCHAR", 381 exp.DataType.Type.MEDIUMTEXT: "TEXT", 382 exp.DataType.Type.LONGTEXT: "TEXT", 383 exp.DataType.Type.TINYTEXT: "TEXT", 384 exp.DataType.Type.MEDIUMBLOB: "BLOB", 385 exp.DataType.Type.LONGBLOB: "BLOB", 386 exp.DataType.Type.TINYBLOB: "BLOB", 387 exp.DataType.Type.INET: "INET", 388 exp.DataType.Type.ROWVERSION: "VARBINARY", 389 } 390 391 TIME_PART_SINGULARS = { 392 "MICROSECONDS": "MICROSECOND", 393 "SECONDS": "SECOND", 394 "MINUTES": "MINUTE", 395 "HOURS": "HOUR", 396 "DAYS": "DAY", 397 "WEEKS": "WEEK", 398 "MONTHS": "MONTH", 399 "QUARTERS": "QUARTER", 400 "YEARS": "YEAR", 401 } 402 403 AFTER_HAVING_MODIFIER_TRANSFORMS = { 404 "cluster": lambda self, e: self.sql(e, "cluster"), 405 "distribute": lambda self, e: self.sql(e, "distribute"), 406 "sort": lambda self, e: self.sql(e, "sort"), 407 "windows": lambda self, e: ( 408 self.seg("WINDOW ") + self.expressions(e, key="windows", flat=True) 409 if e.args.get("windows") 410 else "" 411 ), 412 "qualify": lambda self, e: self.sql(e, "qualify"), 413 } 414 415 TOKEN_MAPPING: t.Dict[TokenType, str] = {} 416 417 STRUCT_DELIMITER = ("<", ">") 418 419 PARAMETER_TOKEN = "@" 420 NAMED_PLACEHOLDER_TOKEN = ":" 421 422 PROPERTIES_LOCATION = { 423 exp.AllowedValuesProperty: exp.Properties.Location.POST_SCHEMA, 424 exp.AlgorithmProperty: exp.Properties.Location.POST_CREATE, 425 exp.AutoIncrementProperty: exp.Properties.Location.POST_SCHEMA, 426 exp.AutoRefreshProperty: exp.Properties.Location.POST_SCHEMA, 427 exp.BackupProperty: exp.Properties.Location.POST_SCHEMA, 428 exp.BlockCompressionProperty: exp.Properties.Location.POST_NAME, 429 exp.CharacterSetProperty: exp.Properties.Location.POST_SCHEMA, 430 exp.ChecksumProperty: exp.Properties.Location.POST_NAME, 431 exp.CollateProperty: exp.Properties.Location.POST_SCHEMA, 432 exp.CopyGrantsProperty: exp.Properties.Location.POST_SCHEMA, 433 exp.Cluster: exp.Properties.Location.POST_SCHEMA, 434 exp.ClusteredByProperty: exp.Properties.Location.POST_SCHEMA, 435 exp.DataBlocksizeProperty: exp.Properties.Location.POST_NAME, 436 exp.DataDeletionProperty: exp.Properties.Location.POST_SCHEMA, 437 exp.DefinerProperty: exp.Properties.Location.POST_CREATE, 438 exp.DictRange: exp.Properties.Location.POST_SCHEMA, 439 exp.DictProperty: exp.Properties.Location.POST_SCHEMA, 440 exp.DynamicProperty: exp.Properties.Location.POST_CREATE, 441 exp.DistKeyProperty: exp.Properties.Location.POST_SCHEMA, 442 exp.DistStyleProperty: exp.Properties.Location.POST_SCHEMA, 443 exp.EngineProperty: exp.Properties.Location.POST_SCHEMA, 444 exp.ExecuteAsProperty: exp.Properties.Location.POST_SCHEMA, 445 exp.ExternalProperty: exp.Properties.Location.POST_CREATE, 446 exp.FallbackProperty: exp.Properties.Location.POST_NAME, 447 exp.FileFormatProperty: exp.Properties.Location.POST_WITH, 448 exp.FreespaceProperty: exp.Properties.Location.POST_NAME, 449 exp.GlobalProperty: exp.Properties.Location.POST_CREATE, 450 exp.HeapProperty: exp.Properties.Location.POST_WITH, 451 exp.InheritsProperty: exp.Properties.Location.POST_SCHEMA, 452 exp.IcebergProperty: exp.Properties.Location.POST_CREATE, 453 exp.InputModelProperty: exp.Properties.Location.POST_SCHEMA, 454 exp.IsolatedLoadingProperty: exp.Properties.Location.POST_NAME, 455 exp.JournalProperty: exp.Properties.Location.POST_NAME, 456 exp.LanguageProperty: exp.Properties.Location.POST_SCHEMA, 457 exp.LikeProperty: exp.Properties.Location.POST_SCHEMA, 458 exp.LocationProperty: exp.Properties.Location.POST_SCHEMA, 459 exp.LockProperty: exp.Properties.Location.POST_SCHEMA, 460 exp.LockingProperty: exp.Properties.Location.POST_ALIAS, 461 exp.LogProperty: exp.Properties.Location.POST_NAME, 462 exp.MaterializedProperty: exp.Properties.Location.POST_CREATE, 463 exp.MergeBlockRatioProperty: exp.Properties.Location.POST_NAME, 464 exp.NoPrimaryIndexProperty: exp.Properties.Location.POST_EXPRESSION, 465 exp.OnProperty: exp.Properties.Location.POST_SCHEMA, 466 exp.OnCommitProperty: exp.Properties.Location.POST_EXPRESSION, 467 exp.Order: exp.Properties.Location.POST_SCHEMA, 468 exp.OutputModelProperty: exp.Properties.Location.POST_SCHEMA, 469 exp.PartitionedByProperty: exp.Properties.Location.POST_WITH, 470 exp.PartitionedOfProperty: exp.Properties.Location.POST_SCHEMA, 471 exp.PrimaryKey: exp.Properties.Location.POST_SCHEMA, 472 exp.Property: exp.Properties.Location.POST_WITH, 473 exp.RemoteWithConnectionModelProperty: exp.Properties.Location.POST_SCHEMA, 474 exp.ReturnsProperty: exp.Properties.Location.POST_SCHEMA, 475 exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA, 476 exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA, 477 exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA, 478 exp.SampleProperty: exp.Properties.Location.POST_SCHEMA, 479 exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA, 480 exp.SecureProperty: exp.Properties.Location.POST_CREATE, 481 exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA, 482 exp.Set: exp.Properties.Location.POST_SCHEMA, 483 exp.SettingsProperty: exp.Properties.Location.POST_SCHEMA, 484 exp.SetProperty: exp.Properties.Location.POST_CREATE, 485 exp.SetConfigProperty: exp.Properties.Location.POST_SCHEMA, 486 exp.SharingProperty: exp.Properties.Location.POST_EXPRESSION, 487 exp.SequenceProperties: exp.Properties.Location.POST_EXPRESSION, 488 exp.SortKeyProperty: exp.Properties.Location.POST_SCHEMA, 489 exp.SqlReadWriteProperty: exp.Properties.Location.POST_SCHEMA, 490 exp.SqlSecurityProperty: exp.Properties.Location.POST_CREATE, 491 exp.StabilityProperty: exp.Properties.Location.POST_SCHEMA, 492 exp.StrictProperty: exp.Properties.Location.POST_SCHEMA, 493 exp.TemporaryProperty: exp.Properties.Location.POST_CREATE, 494 exp.ToTableProperty: exp.Properties.Location.POST_SCHEMA, 495 exp.TransientProperty: exp.Properties.Location.POST_CREATE, 496 exp.TransformModelProperty: exp.Properties.Location.POST_SCHEMA, 497 exp.MergeTreeTTL: exp.Properties.Location.POST_SCHEMA, 498 exp.UnloggedProperty: exp.Properties.Location.POST_CREATE, 499 exp.ViewAttributeProperty: exp.Properties.Location.POST_SCHEMA, 500 exp.VolatileProperty: exp.Properties.Location.POST_CREATE, 501 exp.WithDataProperty: exp.Properties.Location.POST_EXPRESSION, 502 exp.WithJournalTableProperty: exp.Properties.Location.POST_NAME, 503 exp.WithSchemaBindingProperty: exp.Properties.Location.POST_SCHEMA, 504 exp.WithSystemVersioningProperty: exp.Properties.Location.POST_SCHEMA, 505 } 506 507 # Keywords that can't be used as unquoted identifier names 508 RESERVED_KEYWORDS: t.Set[str] = set() 509 510 # Expressions whose comments are separated from them for better formatting 511 WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 512 exp.Command, 513 exp.Create, 514 exp.Delete, 515 exp.Drop, 516 exp.From, 517 exp.Insert, 518 exp.Join, 519 exp.Select, 520 exp.SetOperation, 521 exp.Update, 522 exp.Where, 523 exp.With, 524 ) 525 526 # Expressions that should not have their comments generated in maybe_comment 527 EXCLUDE_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = ( 528 exp.Binary, 529 exp.SetOperation, 530 ) 531 532 # Expressions that can remain unwrapped when appearing in the context of an INTERVAL 533 UNWRAPPED_INTERVAL_VALUES: t.Tuple[t.Type[exp.Expression], ...] = ( 534 exp.Column, 535 exp.Literal, 536 exp.Neg, 537 exp.Paren, 538 ) 539 540 PARAMETERIZABLE_TEXT_TYPES = { 541 exp.DataType.Type.NVARCHAR, 542 exp.DataType.Type.VARCHAR, 543 exp.DataType.Type.CHAR, 544 exp.DataType.Type.NCHAR, 545 } 546 547 # Expressions that need to have all CTEs under them bubbled up to them 548 EXPRESSIONS_WITHOUT_NESTED_CTES: t.Set[t.Type[exp.Expression]] = set() 549 550 SENTINEL_LINE_BREAK = "__SQLGLOT__LB__" 551 552 __slots__ = ( 553 "pretty", 554 "identify", 555 "normalize", 556 "pad", 557 "_indent", 558 "normalize_functions", 559 "unsupported_level", 560 "max_unsupported", 561 "leading_comma", 562 "max_text_width", 563 "comments", 564 "dialect", 565 "unsupported_messages", 566 "_escaped_quote_end", 567 "_escaped_identifier_end", 568 "_next_name", 569 ) 570 571 def __init__( 572 self, 573 pretty: t.Optional[bool] = None, 574 identify: str | bool = False, 575 normalize: bool = False, 576 pad: int = 2, 577 indent: int = 2, 578 normalize_functions: t.Optional[str | bool] = None, 579 unsupported_level: ErrorLevel = ErrorLevel.WARN, 580 max_unsupported: int = 3, 581 leading_comma: bool = False, 582 max_text_width: int = 80, 583 comments: bool = True, 584 dialect: DialectType = None, 585 ): 586 import sqlglot 587 from sqlglot.dialects import Dialect 588 589 self.pretty = pretty if pretty is not None else sqlglot.pretty 590 self.identify = identify 591 self.normalize = normalize 592 self.pad = pad 593 self._indent = indent 594 self.unsupported_level = unsupported_level 595 self.max_unsupported = max_unsupported 596 self.leading_comma = leading_comma 597 self.max_text_width = max_text_width 598 self.comments = comments 599 self.dialect = Dialect.get_or_raise(dialect) 600 601 # This is both a Dialect property and a Generator argument, so we prioritize the latter 602 self.normalize_functions = ( 603 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 604 ) 605 606 self.unsupported_messages: t.List[str] = [] 607 self._escaped_quote_end: str = ( 608 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 609 ) 610 self._escaped_identifier_end: str = ( 611 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 612 ) 613 614 self._next_name = name_sequence("_t") 615 616 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 617 """ 618 Generates the SQL string corresponding to the given syntax tree. 619 620 Args: 621 expression: The syntax tree. 622 copy: Whether to copy the expression. The generator performs mutations so 623 it is safer to copy. 624 625 Returns: 626 The SQL string corresponding to `expression`. 627 """ 628 if copy: 629 expression = expression.copy() 630 631 expression = self.preprocess(expression) 632 633 self.unsupported_messages = [] 634 sql = self.sql(expression).strip() 635 636 if self.pretty: 637 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 638 639 if self.unsupported_level == ErrorLevel.IGNORE: 640 return sql 641 642 if self.unsupported_level == ErrorLevel.WARN: 643 for msg in self.unsupported_messages: 644 logger.warning(msg) 645 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 646 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 647 648 return sql 649 650 def preprocess(self, expression: exp.Expression) -> exp.Expression: 651 """Apply generic preprocessing transformations to a given expression.""" 652 if ( 653 not expression.parent 654 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 655 and any(node.parent is not expression for node in expression.find_all(exp.With)) 656 ): 657 from sqlglot.transforms import move_ctes_to_top_level 658 659 expression = move_ctes_to_top_level(expression) 660 661 if self.ENSURE_BOOLS: 662 from sqlglot.transforms import ensure_bools 663 664 expression = ensure_bools(expression) 665 666 return expression 667 668 def unsupported(self, message: str) -> None: 669 if self.unsupported_level == ErrorLevel.IMMEDIATE: 670 raise UnsupportedError(message) 671 self.unsupported_messages.append(message) 672 673 def sep(self, sep: str = " ") -> str: 674 return f"{sep.strip()}\n" if self.pretty else sep 675 676 def seg(self, sql: str, sep: str = " ") -> str: 677 return f"{self.sep(sep)}{sql}" 678 679 def pad_comment(self, comment: str) -> str: 680 comment = " " + comment if comment[0].strip() else comment 681 comment = comment + " " if comment[-1].strip() else comment 682 return comment 683 684 def maybe_comment( 685 self, 686 sql: str, 687 expression: t.Optional[exp.Expression] = None, 688 comments: t.Optional[t.List[str]] = None, 689 separated: bool = False, 690 ) -> str: 691 comments = ( 692 ((expression and expression.comments) if comments is None else comments) # type: ignore 693 if self.comments 694 else None 695 ) 696 697 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 698 return sql 699 700 comments_sql = " ".join( 701 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 702 ) 703 704 if not comments_sql: 705 return sql 706 707 comments_sql = self._replace_line_breaks(comments_sql) 708 709 if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS): 710 return ( 711 f"{self.sep()}{comments_sql}{sql}" 712 if not sql or sql[0].isspace() 713 else f"{comments_sql}{self.sep()}{sql}" 714 ) 715 716 return f"{sql} {comments_sql}" 717 718 def wrap(self, expression: exp.Expression | str) -> str: 719 this_sql = ( 720 self.sql(expression) 721 if isinstance(expression, exp.UNWRAPPED_QUERIES) 722 else self.sql(expression, "this") 723 ) 724 if not this_sql: 725 return "()" 726 727 this_sql = self.indent(this_sql, level=1, pad=0) 728 return f"({self.sep('')}{this_sql}{self.seg(')', sep='')}" 729 730 def no_identify(self, func: t.Callable[..., str], *args, **kwargs) -> str: 731 original = self.identify 732 self.identify = False 733 result = func(*args, **kwargs) 734 self.identify = original 735 return result 736 737 def normalize_func(self, name: str) -> str: 738 if self.normalize_functions == "upper" or self.normalize_functions is True: 739 return name.upper() 740 if self.normalize_functions == "lower": 741 return name.lower() 742 return name 743 744 def indent( 745 self, 746 sql: str, 747 level: int = 0, 748 pad: t.Optional[int] = None, 749 skip_first: bool = False, 750 skip_last: bool = False, 751 ) -> str: 752 if not self.pretty or not sql: 753 return sql 754 755 pad = self.pad if pad is None else pad 756 lines = sql.split("\n") 757 758 return "\n".join( 759 ( 760 line 761 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 762 else f"{' ' * (level * self._indent + pad)}{line}" 763 ) 764 for i, line in enumerate(lines) 765 ) 766 767 def sql( 768 self, 769 expression: t.Optional[str | exp.Expression], 770 key: t.Optional[str] = None, 771 comment: bool = True, 772 ) -> str: 773 if not expression: 774 return "" 775 776 if isinstance(expression, str): 777 return expression 778 779 if key: 780 value = expression.args.get(key) 781 if value: 782 return self.sql(value) 783 return "" 784 785 transform = self.TRANSFORMS.get(expression.__class__) 786 787 if callable(transform): 788 sql = transform(self, expression) 789 elif isinstance(expression, exp.Expression): 790 exp_handler_name = f"{expression.key}_sql" 791 792 if hasattr(self, exp_handler_name): 793 sql = getattr(self, exp_handler_name)(expression) 794 elif isinstance(expression, exp.Func): 795 sql = self.function_fallback_sql(expression) 796 elif isinstance(expression, exp.Property): 797 sql = self.property_sql(expression) 798 else: 799 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 800 else: 801 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 802 803 return self.maybe_comment(sql, expression) if self.comments and comment else sql 804 805 def uncache_sql(self, expression: exp.Uncache) -> str: 806 table = self.sql(expression, "this") 807 exists_sql = " IF EXISTS" if expression.args.get("exists") else "" 808 return f"UNCACHE TABLE{exists_sql} {table}" 809 810 def cache_sql(self, expression: exp.Cache) -> str: 811 lazy = " LAZY" if expression.args.get("lazy") else "" 812 table = self.sql(expression, "this") 813 options = expression.args.get("options") 814 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 815 sql = self.sql(expression, "expression") 816 sql = f" AS{self.sep()}{sql}" if sql else "" 817 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 818 return self.prepend_ctes(expression, sql) 819 820 def characterset_sql(self, expression: exp.CharacterSet) -> str: 821 if isinstance(expression.parent, exp.Cast): 822 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 823 default = "DEFAULT " if expression.args.get("default") else "" 824 return f"{default}CHARACTER SET={self.sql(expression, 'this')}" 825 826 def column_parts(self, expression: exp.Column) -> str: 827 return ".".join( 828 self.sql(part) 829 for part in ( 830 expression.args.get("catalog"), 831 expression.args.get("db"), 832 expression.args.get("table"), 833 expression.args.get("this"), 834 ) 835 if part 836 ) 837 838 def column_sql(self, expression: exp.Column) -> str: 839 join_mark = " (+)" if expression.args.get("join_mark") else "" 840 841 if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS: 842 join_mark = "" 843 self.unsupported("Outer join syntax using the (+) operator is not supported.") 844 845 return f"{self.column_parts(expression)}{join_mark}" 846 847 def columnposition_sql(self, expression: exp.ColumnPosition) -> str: 848 this = self.sql(expression, "this") 849 this = f" {this}" if this else "" 850 position = self.sql(expression, "position") 851 return f"{position}{this}" 852 853 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 854 column = self.sql(expression, "this") 855 kind = self.sql(expression, "kind") 856 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 857 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 858 kind = f"{sep}{kind}" if kind else "" 859 constraints = f" {constraints}" if constraints else "" 860 position = self.sql(expression, "position") 861 position = f" {position}" if position else "" 862 863 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 864 kind = "" 865 866 return f"{exists}{column}{kind}{constraints}{position}" 867 868 def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str: 869 this = self.sql(expression, "this") 870 kind_sql = self.sql(expression, "kind").strip() 871 return f"CONSTRAINT {this} {kind_sql}" if this else kind_sql 872 873 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 874 this = self.sql(expression, "this") 875 if expression.args.get("not_null"): 876 persisted = " PERSISTED NOT NULL" 877 elif expression.args.get("persisted"): 878 persisted = " PERSISTED" 879 else: 880 persisted = "" 881 return f"AS {this}{persisted}" 882 883 def autoincrementcolumnconstraint_sql(self, _) -> str: 884 return self.token_sql(TokenType.AUTO_INCREMENT) 885 886 def compresscolumnconstraint_sql(self, expression: exp.CompressColumnConstraint) -> str: 887 if isinstance(expression.this, list): 888 this = self.wrap(self.expressions(expression, key="this", flat=True)) 889 else: 890 this = self.sql(expression, "this") 891 892 return f"COMPRESS {this}" 893 894 def generatedasidentitycolumnconstraint_sql( 895 self, expression: exp.GeneratedAsIdentityColumnConstraint 896 ) -> str: 897 this = "" 898 if expression.this is not None: 899 on_null = " ON NULL" if expression.args.get("on_null") else "" 900 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 901 902 start = expression.args.get("start") 903 start = f"START WITH {start}" if start else "" 904 increment = expression.args.get("increment") 905 increment = f" INCREMENT BY {increment}" if increment else "" 906 minvalue = expression.args.get("minvalue") 907 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 908 maxvalue = expression.args.get("maxvalue") 909 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 910 cycle = expression.args.get("cycle") 911 cycle_sql = "" 912 913 if cycle is not None: 914 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 915 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 916 917 sequence_opts = "" 918 if start or increment or cycle_sql: 919 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 920 sequence_opts = f" ({sequence_opts.strip()})" 921 922 expr = self.sql(expression, "expression") 923 expr = f"({expr})" if expr else "IDENTITY" 924 925 return f"GENERATED{this} AS {expr}{sequence_opts}" 926 927 def generatedasrowcolumnconstraint_sql( 928 self, expression: exp.GeneratedAsRowColumnConstraint 929 ) -> str: 930 start = "START" if expression.args.get("start") else "END" 931 hidden = " HIDDEN" if expression.args.get("hidden") else "" 932 return f"GENERATED ALWAYS AS ROW {start}{hidden}" 933 934 def periodforsystemtimeconstraint_sql( 935 self, expression: exp.PeriodForSystemTimeConstraint 936 ) -> str: 937 return f"PERIOD FOR SYSTEM_TIME ({self.sql(expression, 'this')}, {self.sql(expression, 'expression')})" 938 939 def notnullcolumnconstraint_sql(self, expression: exp.NotNullColumnConstraint) -> str: 940 return f"{'' if expression.args.get('allow_null') else 'NOT '}NULL" 941 942 def transformcolumnconstraint_sql(self, expression: exp.TransformColumnConstraint) -> str: 943 return f"AS {self.sql(expression, 'this')}" 944 945 def primarykeycolumnconstraint_sql(self, expression: exp.PrimaryKeyColumnConstraint) -> str: 946 desc = expression.args.get("desc") 947 if desc is not None: 948 return f"PRIMARY KEY{' DESC' if desc else ' ASC'}" 949 return "PRIMARY KEY" 950 951 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 952 this = self.sql(expression, "this") 953 this = f" {this}" if this else "" 954 index_type = expression.args.get("index_type") 955 index_type = f" USING {index_type}" if index_type else "" 956 on_conflict = self.sql(expression, "on_conflict") 957 on_conflict = f" {on_conflict}" if on_conflict else "" 958 nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else "" 959 return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}" 960 961 def createable_sql(self, expression: exp.Create, locations: t.DefaultDict) -> str: 962 return self.sql(expression, "this") 963 964 def create_sql(self, expression: exp.Create) -> str: 965 kind = self.sql(expression, "kind") 966 properties = expression.args.get("properties") 967 properties_locs = self.locate_properties(properties) if properties else defaultdict() 968 969 this = self.createable_sql(expression, properties_locs) 970 971 properties_sql = "" 972 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 973 exp.Properties.Location.POST_WITH 974 ): 975 properties_sql = self.sql( 976 exp.Properties( 977 expressions=[ 978 *properties_locs[exp.Properties.Location.POST_SCHEMA], 979 *properties_locs[exp.Properties.Location.POST_WITH], 980 ] 981 ) 982 ) 983 984 if properties_locs.get(exp.Properties.Location.POST_SCHEMA): 985 properties_sql = self.sep() + properties_sql 986 elif not self.pretty: 987 # Standalone POST_WITH properties need a leading whitespace in non-pretty mode 988 properties_sql = f" {properties_sql}" 989 990 begin = " BEGIN" if expression.args.get("begin") else "" 991 end = " END" if expression.args.get("end") else "" 992 993 expression_sql = self.sql(expression, "expression") 994 if expression_sql: 995 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 996 997 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 998 postalias_props_sql = "" 999 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 1000 postalias_props_sql = self.properties( 1001 exp.Properties( 1002 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 1003 ), 1004 wrapped=False, 1005 ) 1006 postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else "" 1007 expression_sql = f" AS{postalias_props_sql}{expression_sql}" 1008 1009 postindex_props_sql = "" 1010 if properties_locs.get(exp.Properties.Location.POST_INDEX): 1011 postindex_props_sql = self.properties( 1012 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 1013 wrapped=False, 1014 prefix=" ", 1015 ) 1016 1017 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 1018 indexes = f" {indexes}" if indexes else "" 1019 index_sql = indexes + postindex_props_sql 1020 1021 replace = " OR REPLACE" if expression.args.get("replace") else "" 1022 unique = " UNIQUE" if expression.args.get("unique") else "" 1023 1024 clustered = expression.args.get("clustered") 1025 if clustered is None: 1026 clustered_sql = "" 1027 elif clustered: 1028 clustered_sql = " CLUSTERED COLUMNSTORE" 1029 else: 1030 clustered_sql = " NONCLUSTERED COLUMNSTORE" 1031 1032 postcreate_props_sql = "" 1033 if properties_locs.get(exp.Properties.Location.POST_CREATE): 1034 postcreate_props_sql = self.properties( 1035 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 1036 sep=" ", 1037 prefix=" ", 1038 wrapped=False, 1039 ) 1040 1041 modifiers = "".join((clustered_sql, replace, unique, postcreate_props_sql)) 1042 1043 postexpression_props_sql = "" 1044 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 1045 postexpression_props_sql = self.properties( 1046 exp.Properties( 1047 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 1048 ), 1049 sep=" ", 1050 prefix=" ", 1051 wrapped=False, 1052 ) 1053 1054 concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else "" 1055 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 1056 no_schema_binding = ( 1057 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 1058 ) 1059 1060 clone = self.sql(expression, "clone") 1061 clone = f" {clone}" if clone else "" 1062 1063 expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 1064 return self.prepend_ctes(expression, expression_sql) 1065 1066 def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str: 1067 start = self.sql(expression, "start") 1068 start = f"START WITH {start}" if start else "" 1069 increment = self.sql(expression, "increment") 1070 increment = f" INCREMENT BY {increment}" if increment else "" 1071 minvalue = self.sql(expression, "minvalue") 1072 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 1073 maxvalue = self.sql(expression, "maxvalue") 1074 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 1075 owned = self.sql(expression, "owned") 1076 owned = f" OWNED BY {owned}" if owned else "" 1077 1078 cache = expression.args.get("cache") 1079 if cache is None: 1080 cache_str = "" 1081 elif cache is True: 1082 cache_str = " CACHE" 1083 else: 1084 cache_str = f" CACHE {cache}" 1085 1086 options = self.expressions(expression, key="options", flat=True, sep=" ") 1087 options = f" {options}" if options else "" 1088 1089 return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip() 1090 1091 def clone_sql(self, expression: exp.Clone) -> str: 1092 this = self.sql(expression, "this") 1093 shallow = "SHALLOW " if expression.args.get("shallow") else "" 1094 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 1095 return f"{shallow}{keyword} {this}" 1096 1097 def describe_sql(self, expression: exp.Describe) -> str: 1098 style = expression.args.get("style") 1099 style = f" {style}" if style else "" 1100 return f"DESCRIBE{style} {self.sql(expression, 'this')}" 1101 1102 def heredoc_sql(self, expression: exp.Heredoc) -> str: 1103 tag = self.sql(expression, "tag") 1104 return f"${tag}${self.sql(expression, 'this')}${tag}$" 1105 1106 def prepend_ctes(self, expression: exp.Expression, sql: str) -> str: 1107 with_ = self.sql(expression, "with") 1108 if with_: 1109 sql = f"{with_}{self.sep()}{sql}" 1110 return sql 1111 1112 def with_sql(self, expression: exp.With) -> str: 1113 sql = self.expressions(expression, flat=True) 1114 recursive = ( 1115 "RECURSIVE " 1116 if self.CTE_RECURSIVE_KEYWORD_REQUIRED and expression.args.get("recursive") 1117 else "" 1118 ) 1119 1120 return f"WITH {recursive}{sql}" 1121 1122 def cte_sql(self, expression: exp.CTE) -> str: 1123 alias = self.sql(expression, "alias") 1124 1125 materialized = expression.args.get("materialized") 1126 if materialized is False: 1127 materialized = "NOT MATERIALIZED " 1128 elif materialized: 1129 materialized = "MATERIALIZED " 1130 1131 return f"{alias} AS {materialized or ''}{self.wrap(expression)}" 1132 1133 def tablealias_sql(self, expression: exp.TableAlias) -> str: 1134 alias = self.sql(expression, "this") 1135 columns = self.expressions(expression, key="columns", flat=True) 1136 columns = f"({columns})" if columns else "" 1137 1138 if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS: 1139 columns = "" 1140 self.unsupported("Named columns are not supported in table alias.") 1141 1142 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 1143 alias = self._next_name() 1144 1145 return f"{alias}{columns}" 1146 1147 def bitstring_sql(self, expression: exp.BitString) -> str: 1148 this = self.sql(expression, "this") 1149 if self.dialect.BIT_START: 1150 return f"{self.dialect.BIT_START}{this}{self.dialect.BIT_END}" 1151 return f"{int(this, 2)}" 1152 1153 def hexstring_sql(self, expression: exp.HexString) -> str: 1154 this = self.sql(expression, "this") 1155 if self.dialect.HEX_START: 1156 return f"{self.dialect.HEX_START}{this}{self.dialect.HEX_END}" 1157 return f"{int(this, 16)}" 1158 1159 def bytestring_sql(self, expression: exp.ByteString) -> str: 1160 this = self.sql(expression, "this") 1161 if self.dialect.BYTE_START: 1162 return f"{self.dialect.BYTE_START}{this}{self.dialect.BYTE_END}" 1163 return this 1164 1165 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 1166 this = self.sql(expression, "this") 1167 escape = expression.args.get("escape") 1168 1169 if self.dialect.UNICODE_START: 1170 escape_substitute = r"\\\1" 1171 left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END 1172 else: 1173 escape_substitute = r"\\u\1" 1174 left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END 1175 1176 if escape: 1177 escape_pattern = re.compile(rf"{escape.name}(\d+)") 1178 escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else "" 1179 else: 1180 escape_pattern = ESCAPED_UNICODE_RE 1181 escape_sql = "" 1182 1183 if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE): 1184 this = escape_pattern.sub(escape_substitute, this) 1185 1186 return f"{left_quote}{this}{right_quote}{escape_sql}" 1187 1188 def rawstring_sql(self, expression: exp.RawString) -> str: 1189 string = self.escape_str(expression.this.replace("\\", "\\\\"), escape_backslash=False) 1190 return f"{self.dialect.QUOTE_START}{string}{self.dialect.QUOTE_END}" 1191 1192 def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str: 1193 this = self.sql(expression, "this") 1194 specifier = self.sql(expression, "expression") 1195 specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else "" 1196 return f"{this}{specifier}" 1197 1198 def datatype_sql(self, expression: exp.DataType) -> str: 1199 type_value = expression.this 1200 1201 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 1202 type_sql = self.sql(expression, "kind") 1203 else: 1204 type_sql = ( 1205 self.TYPE_MAPPING.get(type_value, type_value.value) 1206 if isinstance(type_value, exp.DataType.Type) 1207 else type_value 1208 ) 1209 1210 nested = "" 1211 interior = self.expressions(expression, flat=True) 1212 values = "" 1213 1214 if interior: 1215 if expression.args.get("nested"): 1216 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 1217 if expression.args.get("values") is not None: 1218 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 1219 values = self.expressions(expression, key="values", flat=True) 1220 values = f"{delimiters[0]}{values}{delimiters[1]}" 1221 elif type_value == exp.DataType.Type.INTERVAL: 1222 nested = f" {interior}" 1223 else: 1224 nested = f"({interior})" 1225 1226 type_sql = f"{type_sql}{nested}{values}" 1227 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 1228 exp.DataType.Type.TIMETZ, 1229 exp.DataType.Type.TIMESTAMPTZ, 1230 ): 1231 type_sql = f"{type_sql} WITH TIME ZONE" 1232 1233 return type_sql 1234 1235 def directory_sql(self, expression: exp.Directory) -> str: 1236 local = "LOCAL " if expression.args.get("local") else "" 1237 row_format = self.sql(expression, "row_format") 1238 row_format = f" {row_format}" if row_format else "" 1239 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}" 1240 1241 def delete_sql(self, expression: exp.Delete) -> str: 1242 this = self.sql(expression, "this") 1243 this = f" FROM {this}" if this else "" 1244 using = self.sql(expression, "using") 1245 using = f" USING {using}" if using else "" 1246 where = self.sql(expression, "where") 1247 returning = self.sql(expression, "returning") 1248 limit = self.sql(expression, "limit") 1249 tables = self.expressions(expression, key="tables") 1250 tables = f" {tables}" if tables else "" 1251 if self.RETURNING_END: 1252 expression_sql = f"{this}{using}{where}{returning}{limit}" 1253 else: 1254 expression_sql = f"{returning}{this}{using}{where}{limit}" 1255 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}") 1256 1257 def drop_sql(self, expression: exp.Drop) -> str: 1258 this = self.sql(expression, "this") 1259 expressions = self.expressions(expression, flat=True) 1260 expressions = f" ({expressions})" if expressions else "" 1261 kind = expression.args["kind"] 1262 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1263 on_cluster = self.sql(expression, "cluster") 1264 on_cluster = f" {on_cluster}" if on_cluster else "" 1265 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1266 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1267 cascade = " CASCADE" if expression.args.get("cascade") else "" 1268 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1269 purge = " PURGE" if expression.args.get("purge") else "" 1270 return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}" 1271 1272 def except_sql(self, expression: exp.Except) -> str: 1273 return self.set_operations(expression) 1274 1275 def except_op(self, expression: exp.Except) -> str: 1276 return f"EXCEPT{'' if expression.args.get('distinct') else ' ALL'}" 1277 1278 def fetch_sql(self, expression: exp.Fetch) -> str: 1279 direction = expression.args.get("direction") 1280 direction = f" {direction}" if direction else "" 1281 count = expression.args.get("count") 1282 count = f" {count}" if count else "" 1283 if expression.args.get("percent"): 1284 count = f"{count} PERCENT" 1285 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1286 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}" 1287 1288 def filter_sql(self, expression: exp.Filter) -> str: 1289 if self.AGGREGATE_FILTER_SUPPORTED: 1290 this = self.sql(expression, "this") 1291 where = self.sql(expression, "expression").strip() 1292 return f"{this} FILTER({where})" 1293 1294 agg = expression.this 1295 agg_arg = agg.this 1296 cond = expression.expression.this 1297 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1298 return self.sql(agg) 1299 1300 def hint_sql(self, expression: exp.Hint) -> str: 1301 if not self.QUERY_HINTS: 1302 self.unsupported("Hints are not supported") 1303 return "" 1304 1305 return f" /*+ {self.expressions(expression, sep=self.QUERY_HINT_SEP).strip()} */" 1306 1307 def indexparameters_sql(self, expression: exp.IndexParameters) -> str: 1308 using = self.sql(expression, "using") 1309 using = f" USING {using}" if using else "" 1310 columns = self.expressions(expression, key="columns", flat=True) 1311 columns = f"({columns})" if columns else "" 1312 partition_by = self.expressions(expression, key="partition_by", flat=True) 1313 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1314 where = self.sql(expression, "where") 1315 include = self.expressions(expression, key="include", flat=True) 1316 if include: 1317 include = f" INCLUDE ({include})" 1318 with_storage = self.expressions(expression, key="with_storage", flat=True) 1319 with_storage = f" WITH ({with_storage})" if with_storage else "" 1320 tablespace = self.sql(expression, "tablespace") 1321 tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else "" 1322 on = self.sql(expression, "on") 1323 on = f" ON {on}" if on else "" 1324 1325 return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}" 1326 1327 def index_sql(self, expression: exp.Index) -> str: 1328 unique = "UNIQUE " if expression.args.get("unique") else "" 1329 primary = "PRIMARY " if expression.args.get("primary") else "" 1330 amp = "AMP " if expression.args.get("amp") else "" 1331 name = self.sql(expression, "this") 1332 name = f"{name} " if name else "" 1333 table = self.sql(expression, "table") 1334 table = f"{self.INDEX_ON} {table}" if table else "" 1335 1336 index = "INDEX " if not table else "" 1337 1338 params = self.sql(expression, "params") 1339 return f"{unique}{primary}{amp}{index}{name}{table}{params}" 1340 1341 def identifier_sql(self, expression: exp.Identifier) -> str: 1342 text = expression.name 1343 lower = text.lower() 1344 text = lower if self.normalize and not expression.quoted else text 1345 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1346 if ( 1347 expression.quoted 1348 or self.dialect.can_identify(text, self.identify) 1349 or lower in self.RESERVED_KEYWORDS 1350 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1351 ): 1352 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1353 return text 1354 1355 def hex_sql(self, expression: exp.Hex) -> str: 1356 text = self.func(self.HEX_FUNC, self.sql(expression, "this")) 1357 if self.dialect.HEX_LOWERCASE: 1358 text = self.func("LOWER", text) 1359 1360 return text 1361 1362 def lowerhex_sql(self, expression: exp.LowerHex) -> str: 1363 text = self.func(self.HEX_FUNC, self.sql(expression, "this")) 1364 if not self.dialect.HEX_LOWERCASE: 1365 text = self.func("LOWER", text) 1366 return text 1367 1368 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1369 input_format = self.sql(expression, "input_format") 1370 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1371 output_format = self.sql(expression, "output_format") 1372 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1373 return self.sep().join((input_format, output_format)) 1374 1375 def national_sql(self, expression: exp.National, prefix: str = "N") -> str: 1376 string = self.sql(exp.Literal.string(expression.name)) 1377 return f"{prefix}{string}" 1378 1379 def partition_sql(self, expression: exp.Partition) -> str: 1380 return f"PARTITION({self.expressions(expression, flat=True)})" 1381 1382 def properties_sql(self, expression: exp.Properties) -> str: 1383 root_properties = [] 1384 with_properties = [] 1385 1386 for p in expression.expressions: 1387 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1388 if p_loc == exp.Properties.Location.POST_WITH: 1389 with_properties.append(p) 1390 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1391 root_properties.append(p) 1392 1393 root_props = self.root_properties(exp.Properties(expressions=root_properties)) 1394 with_props = self.with_properties(exp.Properties(expressions=with_properties)) 1395 1396 if root_props and with_props and not self.pretty: 1397 with_props = " " + with_props 1398 1399 return root_props + with_props 1400 1401 def root_properties(self, properties: exp.Properties) -> str: 1402 if properties.expressions: 1403 return self.expressions(properties, indent=False, sep=" ") 1404 return "" 1405 1406 def properties( 1407 self, 1408 properties: exp.Properties, 1409 prefix: str = "", 1410 sep: str = ", ", 1411 suffix: str = "", 1412 wrapped: bool = True, 1413 ) -> str: 1414 if properties.expressions: 1415 expressions = self.expressions(properties, sep=sep, indent=False) 1416 if expressions: 1417 expressions = self.wrap(expressions) if wrapped else expressions 1418 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1419 return "" 1420 1421 def with_properties(self, properties: exp.Properties) -> str: 1422 return self.properties(properties, prefix=self.seg(self.WITH_PROPERTIES_PREFIX, sep="")) 1423 1424 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1425 properties_locs = defaultdict(list) 1426 for p in properties.expressions: 1427 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1428 if p_loc != exp.Properties.Location.UNSUPPORTED: 1429 properties_locs[p_loc].append(p) 1430 else: 1431 self.unsupported(f"Unsupported property {p.key}") 1432 1433 return properties_locs 1434 1435 def property_name(self, expression: exp.Property, string_key: bool = False) -> str: 1436 if isinstance(expression.this, exp.Dot): 1437 return self.sql(expression, "this") 1438 return f"'{expression.name}'" if string_key else expression.name 1439 1440 def property_sql(self, expression: exp.Property) -> str: 1441 property_cls = expression.__class__ 1442 if property_cls == exp.Property: 1443 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1444 1445 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1446 if not property_name: 1447 self.unsupported(f"Unsupported property {expression.key}") 1448 1449 return f"{property_name}={self.sql(expression, 'this')}" 1450 1451 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1452 if self.SUPPORTS_CREATE_TABLE_LIKE: 1453 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1454 options = f" {options}" if options else "" 1455 1456 like = f"LIKE {self.sql(expression, 'this')}{options}" 1457 if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema): 1458 like = f"({like})" 1459 1460 return like 1461 1462 if expression.expressions: 1463 self.unsupported("Transpilation of LIKE property options is unsupported") 1464 1465 select = exp.select("*").from_(expression.this).limit(0) 1466 return f"AS {self.sql(select)}" 1467 1468 def fallbackproperty_sql(self, expression: exp.FallbackProperty) -> str: 1469 no = "NO " if expression.args.get("no") else "" 1470 protection = " PROTECTION" if expression.args.get("protection") else "" 1471 return f"{no}FALLBACK{protection}" 1472 1473 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1474 no = "NO " if expression.args.get("no") else "" 1475 local = expression.args.get("local") 1476 local = f"{local} " if local else "" 1477 dual = "DUAL " if expression.args.get("dual") else "" 1478 before = "BEFORE " if expression.args.get("before") else "" 1479 after = "AFTER " if expression.args.get("after") else "" 1480 return f"{no}{local}{dual}{before}{after}JOURNAL" 1481 1482 def freespaceproperty_sql(self, expression: exp.FreespaceProperty) -> str: 1483 freespace = self.sql(expression, "this") 1484 percent = " PERCENT" if expression.args.get("percent") else "" 1485 return f"FREESPACE={freespace}{percent}" 1486 1487 def checksumproperty_sql(self, expression: exp.ChecksumProperty) -> str: 1488 if expression.args.get("default"): 1489 property = "DEFAULT" 1490 elif expression.args.get("on"): 1491 property = "ON" 1492 else: 1493 property = "OFF" 1494 return f"CHECKSUM={property}" 1495 1496 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1497 if expression.args.get("no"): 1498 return "NO MERGEBLOCKRATIO" 1499 if expression.args.get("default"): 1500 return "DEFAULT MERGEBLOCKRATIO" 1501 1502 percent = " PERCENT" if expression.args.get("percent") else "" 1503 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}" 1504 1505 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1506 default = expression.args.get("default") 1507 minimum = expression.args.get("minimum") 1508 maximum = expression.args.get("maximum") 1509 if default or minimum or maximum: 1510 if default: 1511 prop = "DEFAULT" 1512 elif minimum: 1513 prop = "MINIMUM" 1514 else: 1515 prop = "MAXIMUM" 1516 return f"{prop} DATABLOCKSIZE" 1517 units = expression.args.get("units") 1518 units = f" {units}" if units else "" 1519 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}" 1520 1521 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1522 autotemp = expression.args.get("autotemp") 1523 always = expression.args.get("always") 1524 default = expression.args.get("default") 1525 manual = expression.args.get("manual") 1526 never = expression.args.get("never") 1527 1528 if autotemp is not None: 1529 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1530 elif always: 1531 prop = "ALWAYS" 1532 elif default: 1533 prop = "DEFAULT" 1534 elif manual: 1535 prop = "MANUAL" 1536 elif never: 1537 prop = "NEVER" 1538 return f"BLOCKCOMPRESSION={prop}" 1539 1540 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1541 no = expression.args.get("no") 1542 no = " NO" if no else "" 1543 concurrent = expression.args.get("concurrent") 1544 concurrent = " CONCURRENT" if concurrent else "" 1545 target = self.sql(expression, "target") 1546 target = f" {target}" if target else "" 1547 return f"WITH{no}{concurrent} ISOLATED LOADING{target}" 1548 1549 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1550 if isinstance(expression.this, list): 1551 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1552 if expression.this: 1553 modulus = self.sql(expression, "this") 1554 remainder = self.sql(expression, "expression") 1555 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1556 1557 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1558 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1559 return f"FROM ({from_expressions}) TO ({to_expressions})" 1560 1561 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1562 this = self.sql(expression, "this") 1563 1564 for_values_or_default = expression.expression 1565 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1566 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1567 else: 1568 for_values_or_default = " DEFAULT" 1569 1570 return f"PARTITION OF {this}{for_values_or_default}" 1571 1572 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1573 kind = expression.args.get("kind") 1574 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1575 for_or_in = expression.args.get("for_or_in") 1576 for_or_in = f" {for_or_in}" if for_or_in else "" 1577 lock_type = expression.args.get("lock_type") 1578 override = " OVERRIDE" if expression.args.get("override") else "" 1579 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}" 1580 1581 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1582 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1583 statistics = expression.args.get("statistics") 1584 statistics_sql = "" 1585 if statistics is not None: 1586 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1587 return f"{data_sql}{statistics_sql}" 1588 1589 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1590 this = self.sql(expression, "this") 1591 this = f"HISTORY_TABLE={this}" if this else "" 1592 data_consistency: t.Optional[str] = self.sql(expression, "data_consistency") 1593 data_consistency = ( 1594 f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None 1595 ) 1596 retention_period: t.Optional[str] = self.sql(expression, "retention_period") 1597 retention_period = ( 1598 f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None 1599 ) 1600 1601 if this: 1602 on_sql = self.func("ON", this, data_consistency, retention_period) 1603 else: 1604 on_sql = "ON" if expression.args.get("on") else "OFF" 1605 1606 sql = f"SYSTEM_VERSIONING={on_sql}" 1607 1608 return f"WITH({sql})" if expression.args.get("with") else sql 1609 1610 def insert_sql(self, expression: exp.Insert) -> str: 1611 hint = self.sql(expression, "hint") 1612 overwrite = expression.args.get("overwrite") 1613 1614 if isinstance(expression.this, exp.Directory): 1615 this = " OVERWRITE" if overwrite else " INTO" 1616 else: 1617 this = self.INSERT_OVERWRITE if overwrite else " INTO" 1618 1619 stored = self.sql(expression, "stored") 1620 stored = f" {stored}" if stored else "" 1621 alternative = expression.args.get("alternative") 1622 alternative = f" OR {alternative}" if alternative else "" 1623 ignore = " IGNORE" if expression.args.get("ignore") else "" 1624 is_function = expression.args.get("is_function") 1625 if is_function: 1626 this = f"{this} FUNCTION" 1627 this = f"{this} {self.sql(expression, 'this')}" 1628 1629 exists = " IF EXISTS" if expression.args.get("exists") else "" 1630 where = self.sql(expression, "where") 1631 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1632 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1633 on_conflict = self.sql(expression, "conflict") 1634 on_conflict = f" {on_conflict}" if on_conflict else "" 1635 by_name = " BY NAME" if expression.args.get("by_name") else "" 1636 returning = self.sql(expression, "returning") 1637 1638 if self.RETURNING_END: 1639 expression_sql = f"{expression_sql}{on_conflict}{returning}" 1640 else: 1641 expression_sql = f"{returning}{expression_sql}{on_conflict}" 1642 1643 sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{where}{expression_sql}" 1644 return self.prepend_ctes(expression, sql) 1645 1646 def intersect_sql(self, expression: exp.Intersect) -> str: 1647 return self.set_operations(expression) 1648 1649 def intersect_op(self, expression: exp.Intersect) -> str: 1650 return f"INTERSECT{'' if expression.args.get('distinct') else ' ALL'}" 1651 1652 def introducer_sql(self, expression: exp.Introducer) -> str: 1653 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 1654 1655 def kill_sql(self, expression: exp.Kill) -> str: 1656 kind = self.sql(expression, "kind") 1657 kind = f" {kind}" if kind else "" 1658 this = self.sql(expression, "this") 1659 this = f" {this}" if this else "" 1660 return f"KILL{kind}{this}" 1661 1662 def pseudotype_sql(self, expression: exp.PseudoType) -> str: 1663 return expression.name 1664 1665 def objectidentifier_sql(self, expression: exp.ObjectIdentifier) -> str: 1666 return expression.name 1667 1668 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1669 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1670 1671 constraint = self.sql(expression, "constraint") 1672 constraint = f" ON CONSTRAINT {constraint}" if constraint else "" 1673 1674 conflict_keys = self.expressions(expression, key="conflict_keys", flat=True) 1675 conflict_keys = f"({conflict_keys}) " if conflict_keys else " " 1676 action = self.sql(expression, "action") 1677 1678 expressions = self.expressions(expression, flat=True) 1679 if expressions: 1680 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1681 expressions = f" {set_keyword}{expressions}" 1682 1683 return f"{conflict}{constraint}{conflict_keys}{action}{expressions}" 1684 1685 def returning_sql(self, expression: exp.Returning) -> str: 1686 return f"{self.seg('RETURNING')} {self.expressions(expression, flat=True)}" 1687 1688 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1689 fields = self.sql(expression, "fields") 1690 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1691 escaped = self.sql(expression, "escaped") 1692 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1693 items = self.sql(expression, "collection_items") 1694 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1695 keys = self.sql(expression, "map_keys") 1696 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1697 lines = self.sql(expression, "lines") 1698 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1699 null = self.sql(expression, "null") 1700 null = f" NULL DEFINED AS {null}" if null else "" 1701 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}" 1702 1703 def withtablehint_sql(self, expression: exp.WithTableHint) -> str: 1704 return f"WITH ({self.expressions(expression, flat=True)})" 1705 1706 def indextablehint_sql(self, expression: exp.IndexTableHint) -> str: 1707 this = f"{self.sql(expression, 'this')} INDEX" 1708 target = self.sql(expression, "target") 1709 target = f" FOR {target}" if target else "" 1710 return f"{this}{target} ({self.expressions(expression, flat=True)})" 1711 1712 def historicaldata_sql(self, expression: exp.HistoricalData) -> str: 1713 this = self.sql(expression, "this") 1714 kind = self.sql(expression, "kind") 1715 expr = self.sql(expression, "expression") 1716 return f"{this} ({kind} => {expr})" 1717 1718 def table_parts(self, expression: exp.Table) -> str: 1719 return ".".join( 1720 self.sql(part) 1721 for part in ( 1722 expression.args.get("catalog"), 1723 expression.args.get("db"), 1724 expression.args.get("this"), 1725 ) 1726 if part is not None 1727 ) 1728 1729 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1730 table = self.table_parts(expression) 1731 only = "ONLY " if expression.args.get("only") else "" 1732 partition = self.sql(expression, "partition") 1733 partition = f" {partition}" if partition else "" 1734 version = self.sql(expression, "version") 1735 version = f" {version}" if version else "" 1736 alias = self.sql(expression, "alias") 1737 alias = f"{sep}{alias}" if alias else "" 1738 hints = self.expressions(expression, key="hints", sep=" ") 1739 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1740 pivots = self.expressions(expression, key="pivots", sep="", flat=True) 1741 joins = self.indent( 1742 self.expressions(expression, key="joins", sep="", flat=True), skip_first=True 1743 ) 1744 laterals = self.expressions(expression, key="laterals", sep="") 1745 1746 file_format = self.sql(expression, "format") 1747 if file_format: 1748 pattern = self.sql(expression, "pattern") 1749 pattern = f", PATTERN => {pattern}" if pattern else "" 1750 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1751 1752 ordinality = expression.args.get("ordinality") or "" 1753 if ordinality: 1754 ordinality = f" WITH ORDINALITY{alias}" 1755 alias = "" 1756 1757 when = self.sql(expression, "when") 1758 if when: 1759 table = f"{table} {when}" 1760 1761 changes = self.sql(expression, "changes") 1762 changes = f" {changes}" if changes else "" 1763 1764 rows_from = self.expressions(expression, key="rows_from") 1765 if rows_from: 1766 table = f"ROWS FROM {self.wrap(rows_from)}" 1767 1768 return f"{only}{table}{changes}{partition}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}" 1769 1770 def tablesample_sql( 1771 self, 1772 expression: exp.TableSample, 1773 sep: str = " AS ", 1774 tablesample_keyword: t.Optional[str] = None, 1775 ) -> str: 1776 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1777 table = expression.this.copy() 1778 table.set("alias", None) 1779 this = self.sql(table) 1780 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1781 else: 1782 this = self.sql(expression, "this") 1783 alias = "" 1784 1785 method = self.sql(expression, "method") 1786 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1787 numerator = self.sql(expression, "bucket_numerator") 1788 denominator = self.sql(expression, "bucket_denominator") 1789 field = self.sql(expression, "bucket_field") 1790 field = f" ON {field}" if field else "" 1791 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1792 seed = self.sql(expression, "seed") 1793 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1794 1795 size = self.sql(expression, "size") 1796 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1797 size = f"{size} ROWS" 1798 1799 percent = self.sql(expression, "percent") 1800 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1801 percent = f"{percent} PERCENT" 1802 1803 expr = f"{bucket}{percent}{size}" 1804 if self.TABLESAMPLE_REQUIRES_PARENS: 1805 expr = f"({expr})" 1806 1807 return ( 1808 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1809 ) 1810 1811 def pivot_sql(self, expression: exp.Pivot) -> str: 1812 expressions = self.expressions(expression, flat=True) 1813 1814 if expression.this: 1815 this = self.sql(expression, "this") 1816 if not expressions: 1817 return f"UNPIVOT {this}" 1818 1819 on = f"{self.seg('ON')} {expressions}" 1820 using = self.expressions(expression, key="using", flat=True) 1821 using = f"{self.seg('USING')} {using}" if using else "" 1822 group = self.sql(expression, "group") 1823 return f"PIVOT {this}{on}{using}{group}" 1824 1825 alias = self.sql(expression, "alias") 1826 alias = f" AS {alias}" if alias else "" 1827 direction = self.seg("UNPIVOT" if expression.unpivot else "PIVOT") 1828 field = self.sql(expression, "field") 1829 include_nulls = expression.args.get("include_nulls") 1830 if include_nulls is not None: 1831 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1832 else: 1833 nulls = "" 1834 return f"{direction}{nulls}({expressions} FOR {field}){alias}" 1835 1836 def version_sql(self, expression: exp.Version) -> str: 1837 this = f"FOR {expression.name}" 1838 kind = expression.text("kind") 1839 expr = self.sql(expression, "expression") 1840 return f"{this} {kind} {expr}" 1841 1842 def tuple_sql(self, expression: exp.Tuple) -> str: 1843 return f"({self.expressions(expression, dynamic=True, new_line=True, skip_first=True, skip_last=True)})" 1844 1845 def update_sql(self, expression: exp.Update) -> str: 1846 this = self.sql(expression, "this") 1847 set_sql = self.expressions(expression, flat=True) 1848 from_sql = self.sql(expression, "from") 1849 where_sql = self.sql(expression, "where") 1850 returning = self.sql(expression, "returning") 1851 order = self.sql(expression, "order") 1852 limit = self.sql(expression, "limit") 1853 if self.RETURNING_END: 1854 expression_sql = f"{from_sql}{where_sql}{returning}" 1855 else: 1856 expression_sql = f"{returning}{from_sql}{where_sql}" 1857 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1858 return self.prepend_ctes(expression, sql) 1859 1860 def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str: 1861 values_as_table = values_as_table and self.VALUES_AS_TABLE 1862 1863 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1864 if values_as_table or not expression.find_ancestor(exp.From, exp.Join): 1865 args = self.expressions(expression) 1866 alias = self.sql(expression, "alias") 1867 values = f"VALUES{self.seg('')}{args}" 1868 values = ( 1869 f"({values})" 1870 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1871 else values 1872 ) 1873 return f"{values} AS {alias}" if alias else values 1874 1875 # Converts `VALUES...` expression into a series of select unions. 1876 alias_node = expression.args.get("alias") 1877 column_names = alias_node and alias_node.columns 1878 1879 selects: t.List[exp.Query] = [] 1880 1881 for i, tup in enumerate(expression.expressions): 1882 row = tup.expressions 1883 1884 if i == 0 and column_names: 1885 row = [ 1886 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1887 ] 1888 1889 selects.append(exp.Select(expressions=row)) 1890 1891 if self.pretty: 1892 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1893 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1894 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1895 query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1896 return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False)) 1897 1898 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1899 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1900 return f"({unions}){alias}" 1901 1902 def var_sql(self, expression: exp.Var) -> str: 1903 return self.sql(expression, "this") 1904 1905 def into_sql(self, expression: exp.Into) -> str: 1906 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1907 unlogged = " UNLOGGED" if expression.args.get("unlogged") else "" 1908 return f"{self.seg('INTO')}{temporary or unlogged} {self.sql(expression, 'this')}" 1909 1910 def from_sql(self, expression: exp.From) -> str: 1911 return f"{self.seg('FROM')} {self.sql(expression, 'this')}" 1912 1913 def group_sql(self, expression: exp.Group) -> str: 1914 group_by_all = expression.args.get("all") 1915 if group_by_all is True: 1916 modifier = " ALL" 1917 elif group_by_all is False: 1918 modifier = " DISTINCT" 1919 else: 1920 modifier = "" 1921 1922 group_by = self.op_expressions(f"GROUP BY{modifier}", expression) 1923 1924 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1925 grouping_sets = ( 1926 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1927 ) 1928 1929 cube = expression.args.get("cube", []) 1930 if seq_get(cube, 0) is True: 1931 return f"{group_by}{self.seg('WITH CUBE')}" 1932 else: 1933 cube_sql = self.expressions(expression, key="cube", indent=False) 1934 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1935 1936 rollup = expression.args.get("rollup", []) 1937 if seq_get(rollup, 0) is True: 1938 return f"{group_by}{self.seg('WITH ROLLUP')}" 1939 else: 1940 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1941 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1942 1943 groupings = csv( 1944 grouping_sets, 1945 cube_sql, 1946 rollup_sql, 1947 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1948 sep=self.GROUPINGS_SEP, 1949 ) 1950 1951 if expression.args.get("expressions") and groupings: 1952 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1953 1954 return f"{group_by}{groupings}" 1955 1956 def having_sql(self, expression: exp.Having) -> str: 1957 this = self.indent(self.sql(expression, "this")) 1958 return f"{self.seg('HAVING')}{self.sep()}{this}" 1959 1960 def connect_sql(self, expression: exp.Connect) -> str: 1961 start = self.sql(expression, "start") 1962 start = self.seg(f"START WITH {start}") if start else "" 1963 nocycle = " NOCYCLE" if expression.args.get("nocycle") else "" 1964 connect = self.sql(expression, "connect") 1965 connect = self.seg(f"CONNECT BY{nocycle} {connect}") 1966 return start + connect 1967 1968 def prior_sql(self, expression: exp.Prior) -> str: 1969 return f"PRIOR {self.sql(expression, 'this')}" 1970 1971 def join_sql(self, expression: exp.Join) -> str: 1972 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1973 side = None 1974 else: 1975 side = expression.side 1976 1977 op_sql = " ".join( 1978 op 1979 for op in ( 1980 expression.method, 1981 "GLOBAL" if expression.args.get("global") else None, 1982 side, 1983 expression.kind, 1984 expression.hint if self.JOIN_HINTS else None, 1985 ) 1986 if op 1987 ) 1988 match_cond = self.sql(expression, "match_condition") 1989 match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else "" 1990 on_sql = self.sql(expression, "on") 1991 using = expression.args.get("using") 1992 1993 if not on_sql and using: 1994 on_sql = csv(*(self.sql(column) for column in using)) 1995 1996 this = expression.this 1997 this_sql = self.sql(this) 1998 1999 if on_sql: 2000 on_sql = self.indent(on_sql, skip_first=True) 2001 space = self.seg(" " * self.pad) if self.pretty else " " 2002 if using: 2003 on_sql = f"{space}USING ({on_sql})" 2004 else: 2005 on_sql = f"{space}ON {on_sql}" 2006 elif not op_sql: 2007 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 2008 return f" {this_sql}" 2009 2010 return f", {this_sql}" 2011 2012 if op_sql != "STRAIGHT_JOIN": 2013 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 2014 2015 return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}" 2016 2017 def lambda_sql(self, expression: exp.Lambda, arrow_sep: str = "->") -> str: 2018 args = self.expressions(expression, flat=True) 2019 args = f"({args})" if len(args.split(",")) > 1 else args 2020 return f"{args} {arrow_sep} {self.sql(expression, 'this')}" 2021 2022 def lateral_op(self, expression: exp.Lateral) -> str: 2023 cross_apply = expression.args.get("cross_apply") 2024 2025 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 2026 if cross_apply is True: 2027 op = "INNER JOIN " 2028 elif cross_apply is False: 2029 op = "LEFT JOIN " 2030 else: 2031 op = "" 2032 2033 return f"{op}LATERAL" 2034 2035 def lateral_sql(self, expression: exp.Lateral) -> str: 2036 this = self.sql(expression, "this") 2037 2038 if expression.args.get("view"): 2039 alias = expression.args["alias"] 2040 columns = self.expressions(alias, key="columns", flat=True) 2041 table = f" {alias.name}" if alias.name else "" 2042 columns = f" AS {columns}" if columns else "" 2043 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 2044 return f"{op_sql}{self.sep()}{this}{table}{columns}" 2045 2046 alias = self.sql(expression, "alias") 2047 alias = f" AS {alias}" if alias else "" 2048 return f"{self.lateral_op(expression)} {this}{alias}" 2049 2050 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 2051 this = self.sql(expression, "this") 2052 2053 args = [ 2054 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 2055 for e in (expression.args.get(k) for k in ("offset", "expression")) 2056 if e 2057 ] 2058 2059 args_sql = ", ".join(self.sql(e) for e in args) 2060 args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql 2061 expressions = self.expressions(expression, flat=True) 2062 expressions = f" BY {expressions}" if expressions else "" 2063 2064 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}" 2065 2066 def offset_sql(self, expression: exp.Offset) -> str: 2067 this = self.sql(expression, "this") 2068 value = expression.expression 2069 value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value 2070 expressions = self.expressions(expression, flat=True) 2071 expressions = f" BY {expressions}" if expressions else "" 2072 return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}" 2073 2074 def setitem_sql(self, expression: exp.SetItem) -> str: 2075 kind = self.sql(expression, "kind") 2076 kind = f"{kind} " if kind else "" 2077 this = self.sql(expression, "this") 2078 expressions = self.expressions(expression) 2079 collate = self.sql(expression, "collate") 2080 collate = f" COLLATE {collate}" if collate else "" 2081 global_ = "GLOBAL " if expression.args.get("global") else "" 2082 return f"{global_}{kind}{this}{expressions}{collate}" 2083 2084 def set_sql(self, expression: exp.Set) -> str: 2085 expressions = ( 2086 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 2087 ) 2088 tag = " TAG" if expression.args.get("tag") else "" 2089 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}" 2090 2091 def pragma_sql(self, expression: exp.Pragma) -> str: 2092 return f"PRAGMA {self.sql(expression, 'this')}" 2093 2094 def lock_sql(self, expression: exp.Lock) -> str: 2095 if not self.LOCKING_READS_SUPPORTED: 2096 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 2097 return "" 2098 2099 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 2100 expressions = self.expressions(expression, flat=True) 2101 expressions = f" OF {expressions}" if expressions else "" 2102 wait = expression.args.get("wait") 2103 2104 if wait is not None: 2105 if isinstance(wait, exp.Literal): 2106 wait = f" WAIT {self.sql(wait)}" 2107 else: 2108 wait = " NOWAIT" if wait else " SKIP LOCKED" 2109 2110 return f"{lock_type}{expressions}{wait or ''}" 2111 2112 def literal_sql(self, expression: exp.Literal) -> str: 2113 text = expression.this or "" 2114 if expression.is_string: 2115 text = f"{self.dialect.QUOTE_START}{self.escape_str(text)}{self.dialect.QUOTE_END}" 2116 return text 2117 2118 def escape_str(self, text: str, escape_backslash: bool = True) -> str: 2119 if self.dialect.ESCAPED_SEQUENCES: 2120 to_escaped = self.dialect.ESCAPED_SEQUENCES 2121 text = "".join( 2122 to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text 2123 ) 2124 2125 return self._replace_line_breaks(text).replace( 2126 self.dialect.QUOTE_END, self._escaped_quote_end 2127 ) 2128 2129 def loaddata_sql(self, expression: exp.LoadData) -> str: 2130 local = " LOCAL" if expression.args.get("local") else "" 2131 inpath = f" INPATH {self.sql(expression, 'inpath')}" 2132 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 2133 this = f" INTO TABLE {self.sql(expression, 'this')}" 2134 partition = self.sql(expression, "partition") 2135 partition = f" {partition}" if partition else "" 2136 input_format = self.sql(expression, "input_format") 2137 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 2138 serde = self.sql(expression, "serde") 2139 serde = f" SERDE {serde}" if serde else "" 2140 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}" 2141 2142 def null_sql(self, *_) -> str: 2143 return "NULL" 2144 2145 def boolean_sql(self, expression: exp.Boolean) -> str: 2146 return "TRUE" if expression.this else "FALSE" 2147 2148 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 2149 this = self.sql(expression, "this") 2150 this = f"{this} " if this else this 2151 siblings = "SIBLINGS " if expression.args.get("siblings") else "" 2152 order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore 2153 interpolated_values = [ 2154 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 2155 for named_expression in expression.args.get("interpolate") or [] 2156 ] 2157 interpolate = ( 2158 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 2159 ) 2160 return f"{order}{interpolate}" 2161 2162 def withfill_sql(self, expression: exp.WithFill) -> str: 2163 from_sql = self.sql(expression, "from") 2164 from_sql = f" FROM {from_sql}" if from_sql else "" 2165 to_sql = self.sql(expression, "to") 2166 to_sql = f" TO {to_sql}" if to_sql else "" 2167 step_sql = self.sql(expression, "step") 2168 step_sql = f" STEP {step_sql}" if step_sql else "" 2169 return f"WITH FILL{from_sql}{to_sql}{step_sql}" 2170 2171 def cluster_sql(self, expression: exp.Cluster) -> str: 2172 return self.op_expressions("CLUSTER BY", expression) 2173 2174 def distribute_sql(self, expression: exp.Distribute) -> str: 2175 return self.op_expressions("DISTRIBUTE BY", expression) 2176 2177 def sort_sql(self, expression: exp.Sort) -> str: 2178 return self.op_expressions("SORT BY", expression) 2179 2180 def ordered_sql(self, expression: exp.Ordered) -> str: 2181 desc = expression.args.get("desc") 2182 asc = not desc 2183 2184 nulls_first = expression.args.get("nulls_first") 2185 nulls_last = not nulls_first 2186 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 2187 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 2188 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 2189 2190 this = self.sql(expression, "this") 2191 2192 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 2193 nulls_sort_change = "" 2194 if nulls_first and ( 2195 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 2196 ): 2197 nulls_sort_change = " NULLS FIRST" 2198 elif ( 2199 nulls_last 2200 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 2201 and not nulls_are_last 2202 ): 2203 nulls_sort_change = " NULLS LAST" 2204 2205 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 2206 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 2207 window = expression.find_ancestor(exp.Window, exp.Select) 2208 if isinstance(window, exp.Window) and window.args.get("spec"): 2209 self.unsupported( 2210 f"'{nulls_sort_change.strip()}' translation not supported in window functions" 2211 ) 2212 nulls_sort_change = "" 2213 elif self.NULL_ORDERING_SUPPORTED is None: 2214 if expression.this.is_int: 2215 self.unsupported( 2216 f"'{nulls_sort_change.strip()}' translation not supported with positional ordering" 2217 ) 2218 elif not isinstance(expression.this, exp.Rand): 2219 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 2220 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 2221 nulls_sort_change = "" 2222 2223 with_fill = self.sql(expression, "with_fill") 2224 with_fill = f" {with_fill}" if with_fill else "" 2225 2226 return f"{this}{sort_order}{nulls_sort_change}{with_fill}" 2227 2228 def matchrecognizemeasure_sql(self, expression: exp.MatchRecognizeMeasure) -> str: 2229 window_frame = self.sql(expression, "window_frame") 2230 window_frame = f"{window_frame} " if window_frame else "" 2231 2232 this = self.sql(expression, "this") 2233 2234 return f"{window_frame}{this}" 2235 2236 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 2237 partition = self.partition_by_sql(expression) 2238 order = self.sql(expression, "order") 2239 measures = self.expressions(expression, key="measures") 2240 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 2241 rows = self.sql(expression, "rows") 2242 rows = self.seg(rows) if rows else "" 2243 after = self.sql(expression, "after") 2244 after = self.seg(after) if after else "" 2245 pattern = self.sql(expression, "pattern") 2246 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 2247 definition_sqls = [ 2248 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 2249 for definition in expression.args.get("define", []) 2250 ] 2251 definitions = self.expressions(sqls=definition_sqls) 2252 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 2253 body = "".join( 2254 ( 2255 partition, 2256 order, 2257 measures, 2258 rows, 2259 after, 2260 pattern, 2261 define, 2262 ) 2263 ) 2264 alias = self.sql(expression, "alias") 2265 alias = f" {alias}" if alias else "" 2266 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}" 2267 2268 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 2269 limit = expression.args.get("limit") 2270 2271 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 2272 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 2273 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 2274 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 2275 2276 return csv( 2277 *sqls, 2278 *[self.sql(join) for join in expression.args.get("joins") or []], 2279 self.sql(expression, "connect"), 2280 self.sql(expression, "match"), 2281 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 2282 self.sql(expression, "prewhere"), 2283 self.sql(expression, "where"), 2284 self.sql(expression, "group"), 2285 self.sql(expression, "having"), 2286 *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()], 2287 self.sql(expression, "order"), 2288 *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit), 2289 *self.after_limit_modifiers(expression), 2290 self.options_modifier(expression), 2291 sep="", 2292 ) 2293 2294 def options_modifier(self, expression: exp.Expression) -> str: 2295 options = self.expressions(expression, key="options") 2296 return f" {options}" if options else "" 2297 2298 def queryoption_sql(self, expression: exp.QueryOption) -> str: 2299 return "" 2300 2301 def offset_limit_modifiers( 2302 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 2303 ) -> t.List[str]: 2304 return [ 2305 self.sql(expression, "offset") if fetch else self.sql(limit), 2306 self.sql(limit) if fetch else self.sql(expression, "offset"), 2307 ] 2308 2309 def after_limit_modifiers(self, expression: exp.Expression) -> t.List[str]: 2310 locks = self.expressions(expression, key="locks", sep=" ") 2311 locks = f" {locks}" if locks else "" 2312 return [locks, self.sql(expression, "sample")] 2313 2314 def select_sql(self, expression: exp.Select) -> str: 2315 into = expression.args.get("into") 2316 if not self.SUPPORTS_SELECT_INTO and into: 2317 into.pop() 2318 2319 hint = self.sql(expression, "hint") 2320 distinct = self.sql(expression, "distinct") 2321 distinct = f" {distinct}" if distinct else "" 2322 kind = self.sql(expression, "kind") 2323 2324 limit = expression.args.get("limit") 2325 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP: 2326 top = self.limit_sql(limit, top=True) 2327 limit.pop() 2328 else: 2329 top = "" 2330 2331 expressions = self.expressions(expression) 2332 2333 if kind: 2334 if kind in self.SELECT_KINDS: 2335 kind = f" AS {kind}" 2336 else: 2337 if kind == "STRUCT": 2338 expressions = self.expressions( 2339 sqls=[ 2340 self.sql( 2341 exp.Struct( 2342 expressions=[ 2343 exp.PropertyEQ(this=e.args.get("alias"), expression=e.this) 2344 if isinstance(e, exp.Alias) 2345 else e 2346 for e in expression.expressions 2347 ] 2348 ) 2349 ) 2350 ] 2351 ) 2352 kind = "" 2353 2354 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2355 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2356 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2357 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2358 sql = self.query_modifiers( 2359 expression, 2360 f"SELECT{top_distinct}{kind}{expressions}", 2361 self.sql(expression, "into", comment=False), 2362 self.sql(expression, "from", comment=False), 2363 ) 2364 2365 sql = self.prepend_ctes(expression, sql) 2366 2367 if not self.SUPPORTS_SELECT_INTO and into: 2368 if into.args.get("temporary"): 2369 table_kind = " TEMPORARY" 2370 elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"): 2371 table_kind = " UNLOGGED" 2372 else: 2373 table_kind = "" 2374 sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}" 2375 2376 return sql 2377 2378 def schema_sql(self, expression: exp.Schema) -> str: 2379 this = self.sql(expression, "this") 2380 sql = self.schema_columns_sql(expression) 2381 return f"{this} {sql}" if this and sql else this or sql 2382 2383 def schema_columns_sql(self, expression: exp.Schema) -> str: 2384 if expression.expressions: 2385 return f"({self.sep('')}{self.expressions(expression)}{self.seg(')', sep='')}" 2386 return "" 2387 2388 def star_sql(self, expression: exp.Star) -> str: 2389 except_ = self.expressions(expression, key="except", flat=True) 2390 except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else "" 2391 replace = self.expressions(expression, key="replace", flat=True) 2392 replace = f"{self.seg('REPLACE')} ({replace})" if replace else "" 2393 rename = self.expressions(expression, key="rename", flat=True) 2394 rename = f"{self.seg('RENAME')} ({rename})" if rename else "" 2395 return f"*{except_}{replace}{rename}" 2396 2397 def parameter_sql(self, expression: exp.Parameter) -> str: 2398 this = self.sql(expression, "this") 2399 return f"{self.PARAMETER_TOKEN}{this}" 2400 2401 def sessionparameter_sql(self, expression: exp.SessionParameter) -> str: 2402 this = self.sql(expression, "this") 2403 kind = expression.text("kind") 2404 if kind: 2405 kind = f"{kind}." 2406 return f"@@{kind}{this}" 2407 2408 def placeholder_sql(self, expression: exp.Placeholder) -> str: 2409 return f"{self.NAMED_PLACEHOLDER_TOKEN}{expression.name}" if expression.this else "?" 2410 2411 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2412 alias = self.sql(expression, "alias") 2413 alias = f"{sep}{alias}" if alias else "" 2414 pivots = self.expressions(expression, key="pivots", sep="", flat=True) 2415 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2416 return self.prepend_ctes(expression, sql) 2417 2418 def qualify_sql(self, expression: exp.Qualify) -> str: 2419 this = self.indent(self.sql(expression, "this")) 2420 return f"{self.seg('QUALIFY')}{self.sep()}{this}" 2421 2422 def set_operations(self, expression: exp.SetOperation) -> str: 2423 if not self.SET_OP_MODIFIERS: 2424 limit = expression.args.get("limit") 2425 order = expression.args.get("order") 2426 2427 if limit or order: 2428 select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False) 2429 2430 if limit: 2431 select = select.limit(limit.pop(), copy=False) 2432 if order: 2433 select = select.order_by(order.pop(), copy=False) 2434 return self.sql(select) 2435 2436 sqls: t.List[str] = [] 2437 stack: t.List[t.Union[str, exp.Expression]] = [expression] 2438 2439 while stack: 2440 node = stack.pop() 2441 2442 if isinstance(node, exp.SetOperation): 2443 stack.append(node.expression) 2444 stack.append( 2445 self.maybe_comment( 2446 getattr(self, f"{node.key}_op")(node), 2447 comments=node.comments, 2448 separated=True, 2449 ) 2450 ) 2451 stack.append(node.this) 2452 else: 2453 sqls.append(self.sql(node)) 2454 2455 this = self.sep().join(sqls) 2456 this = self.query_modifiers(expression, this) 2457 return self.prepend_ctes(expression, this) 2458 2459 def union_sql(self, expression: exp.Union) -> str: 2460 return self.set_operations(expression) 2461 2462 def union_op(self, expression: exp.SetOperation) -> str: 2463 kind = " DISTINCT" if self.EXPLICIT_SET_OP else "" 2464 kind = kind if expression.args.get("distinct") else " ALL" 2465 by_name = " BY NAME" if expression.args.get("by_name") else "" 2466 return f"UNION{kind}{by_name}" 2467 2468 def unnest_sql(self, expression: exp.Unnest) -> str: 2469 args = self.expressions(expression, flat=True) 2470 2471 alias = expression.args.get("alias") 2472 offset = expression.args.get("offset") 2473 2474 if self.UNNEST_WITH_ORDINALITY: 2475 if alias and isinstance(offset, exp.Expression): 2476 alias.append("columns", offset) 2477 2478 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2479 columns = alias.columns 2480 alias = self.sql(columns[0]) if columns else "" 2481 else: 2482 alias = self.sql(alias) 2483 2484 alias = f" AS {alias}" if alias else alias 2485 if self.UNNEST_WITH_ORDINALITY: 2486 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2487 else: 2488 if isinstance(offset, exp.Expression): 2489 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2490 elif offset: 2491 suffix = f"{alias} WITH OFFSET" 2492 else: 2493 suffix = alias 2494 2495 return f"UNNEST({args}){suffix}" 2496 2497 def prewhere_sql(self, expression: exp.PreWhere) -> str: 2498 return "" 2499 2500 def where_sql(self, expression: exp.Where) -> str: 2501 this = self.indent(self.sql(expression, "this")) 2502 return f"{self.seg('WHERE')}{self.sep()}{this}" 2503 2504 def window_sql(self, expression: exp.Window) -> str: 2505 this = self.sql(expression, "this") 2506 partition = self.partition_by_sql(expression) 2507 order = expression.args.get("order") 2508 order = self.order_sql(order, flat=True) if order else "" 2509 spec = self.sql(expression, "spec") 2510 alias = self.sql(expression, "alias") 2511 over = self.sql(expression, "over") or "OVER" 2512 2513 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2514 2515 first = expression.args.get("first") 2516 if first is None: 2517 first = "" 2518 else: 2519 first = "FIRST" if first else "LAST" 2520 2521 if not partition and not order and not spec and alias: 2522 return f"{this} {alias}" 2523 2524 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2525 return f"{this} ({args})" 2526 2527 def partition_by_sql(self, expression: exp.Window | exp.MatchRecognize) -> str: 2528 partition = self.expressions(expression, key="partition_by", flat=True) 2529 return f"PARTITION BY {partition}" if partition else "" 2530 2531 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2532 kind = self.sql(expression, "kind") 2533 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2534 end = ( 2535 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2536 or "CURRENT ROW" 2537 ) 2538 return f"{kind} BETWEEN {start} AND {end}" 2539 2540 def withingroup_sql(self, expression: exp.WithinGroup) -> str: 2541 this = self.sql(expression, "this") 2542 expression_sql = self.sql(expression, "expression")[1:] # order has a leading space 2543 return f"{this} WITHIN GROUP ({expression_sql})" 2544 2545 def between_sql(self, expression: exp.Between) -> str: 2546 this = self.sql(expression, "this") 2547 low = self.sql(expression, "low") 2548 high = self.sql(expression, "high") 2549 return f"{this} BETWEEN {low} AND {high}" 2550 2551 def bracket_offset_expressions(self, expression: exp.Bracket) -> t.List[exp.Expression]: 2552 return apply_index_offset( 2553 expression.this, 2554 expression.expressions, 2555 self.dialect.INDEX_OFFSET - expression.args.get("offset", 0), 2556 ) 2557 2558 def bracket_sql(self, expression: exp.Bracket) -> str: 2559 expressions = self.bracket_offset_expressions(expression) 2560 expressions_sql = ", ".join(self.sql(e) for e in expressions) 2561 return f"{self.sql(expression, 'this')}[{expressions_sql}]" 2562 2563 def all_sql(self, expression: exp.All) -> str: 2564 return f"ALL {self.wrap(expression)}" 2565 2566 def any_sql(self, expression: exp.Any) -> str: 2567 this = self.sql(expression, "this") 2568 if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)): 2569 if isinstance(expression.this, exp.UNWRAPPED_QUERIES): 2570 this = self.wrap(this) 2571 return f"ANY{this}" 2572 return f"ANY {this}" 2573 2574 def exists_sql(self, expression: exp.Exists) -> str: 2575 return f"EXISTS{self.wrap(expression)}" 2576 2577 def case_sql(self, expression: exp.Case) -> str: 2578 this = self.sql(expression, "this") 2579 statements = [f"CASE {this}" if this else "CASE"] 2580 2581 for e in expression.args["ifs"]: 2582 statements.append(f"WHEN {self.sql(e, 'this')}") 2583 statements.append(f"THEN {self.sql(e, 'true')}") 2584 2585 default = self.sql(expression, "default") 2586 2587 if default: 2588 statements.append(f"ELSE {default}") 2589 2590 statements.append("END") 2591 2592 if self.pretty and self.too_wide(statements): 2593 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2594 2595 return " ".join(statements) 2596 2597 def constraint_sql(self, expression: exp.Constraint) -> str: 2598 this = self.sql(expression, "this") 2599 expressions = self.expressions(expression, flat=True) 2600 return f"CONSTRAINT {this} {expressions}" 2601 2602 def nextvaluefor_sql(self, expression: exp.NextValueFor) -> str: 2603 order = expression.args.get("order") 2604 order = f" OVER ({self.order_sql(order, flat=True)})" if order else "" 2605 return f"NEXT VALUE FOR {self.sql(expression, 'this')}{order}" 2606 2607 def extract_sql(self, expression: exp.Extract) -> str: 2608 this = self.sql(expression, "this") if self.EXTRACT_ALLOWS_QUOTES else expression.this.name 2609 expression_sql = self.sql(expression, "expression") 2610 return f"EXTRACT({this} FROM {expression_sql})" 2611 2612 def trim_sql(self, expression: exp.Trim) -> str: 2613 trim_type = self.sql(expression, "position") 2614 2615 if trim_type == "LEADING": 2616 return self.func("LTRIM", expression.this) 2617 elif trim_type == "TRAILING": 2618 return self.func("RTRIM", expression.this) 2619 else: 2620 return self.func("TRIM", expression.this, expression.expression) 2621 2622 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2623 args = expression.expressions 2624 if isinstance(expression, exp.ConcatWs): 2625 args = args[1:] # Skip the delimiter 2626 2627 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2628 args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args] 2629 2630 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2631 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2632 2633 return args 2634 2635 def concat_sql(self, expression: exp.Concat) -> str: 2636 expressions = self.convert_concat_args(expression) 2637 2638 # Some dialects don't allow a single-argument CONCAT call 2639 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2640 return self.sql(expressions[0]) 2641 2642 return self.func("CONCAT", *expressions) 2643 2644 def concatws_sql(self, expression: exp.ConcatWs) -> str: 2645 return self.func( 2646 "CONCAT_WS", seq_get(expression.expressions, 0), *self.convert_concat_args(expression) 2647 ) 2648 2649 def check_sql(self, expression: exp.Check) -> str: 2650 this = self.sql(expression, key="this") 2651 return f"CHECK ({this})" 2652 2653 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2654 expressions = self.expressions(expression, flat=True) 2655 reference = self.sql(expression, "reference") 2656 reference = f" {reference}" if reference else "" 2657 delete = self.sql(expression, "delete") 2658 delete = f" ON DELETE {delete}" if delete else "" 2659 update = self.sql(expression, "update") 2660 update = f" ON UPDATE {update}" if update else "" 2661 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}" 2662 2663 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2664 expressions = self.expressions(expression, flat=True) 2665 options = self.expressions(expression, key="options", flat=True, sep=" ") 2666 options = f" {options}" if options else "" 2667 return f"PRIMARY KEY ({expressions}){options}" 2668 2669 def if_sql(self, expression: exp.If) -> str: 2670 return self.case_sql(exp.Case(ifs=[expression], default=expression.args.get("false"))) 2671 2672 def matchagainst_sql(self, expression: exp.MatchAgainst) -> str: 2673 modifier = expression.args.get("modifier") 2674 modifier = f" {modifier}" if modifier else "" 2675 return f"{self.func('MATCH', *expression.expressions)} AGAINST({self.sql(expression, 'this')}{modifier})" 2676 2677 def jsonkeyvalue_sql(self, expression: exp.JSONKeyValue) -> str: 2678 return f"{self.sql(expression, 'this')}{self.JSON_KEY_VALUE_PAIR_SEP} {self.sql(expression, 'expression')}" 2679 2680 def jsonpath_sql(self, expression: exp.JSONPath) -> str: 2681 path = self.expressions(expression, sep="", flat=True).lstrip(".") 2682 if self.QUOTE_JSON_PATH: 2683 path = f"{self.dialect.QUOTE_START}{path}{self.dialect.QUOTE_END}" 2684 2685 return path 2686 2687 def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str: 2688 if isinstance(expression, exp.JSONPathPart): 2689 transform = self.TRANSFORMS.get(expression.__class__) 2690 if not callable(transform): 2691 self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}") 2692 return "" 2693 2694 return transform(self, expression) 2695 2696 if isinstance(expression, int): 2697 return str(expression) 2698 2699 if self.JSON_PATH_SINGLE_QUOTE_ESCAPE: 2700 escaped = expression.replace("'", "\\'") 2701 escaped = f"\\'{expression}\\'" 2702 else: 2703 escaped = expression.replace('"', '\\"') 2704 escaped = f'"{escaped}"' 2705 2706 return escaped 2707 2708 def formatjson_sql(self, expression: exp.FormatJson) -> str: 2709 return f"{self.sql(expression, 'this')} FORMAT JSON" 2710 2711 def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str: 2712 null_handling = expression.args.get("null_handling") 2713 null_handling = f" {null_handling}" if null_handling else "" 2714 2715 unique_keys = expression.args.get("unique_keys") 2716 if unique_keys is not None: 2717 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2718 else: 2719 unique_keys = "" 2720 2721 return_type = self.sql(expression, "return_type") 2722 return_type = f" RETURNING {return_type}" if return_type else "" 2723 encoding = self.sql(expression, "encoding") 2724 encoding = f" ENCODING {encoding}" if encoding else "" 2725 2726 return self.func( 2727 "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG", 2728 *expression.expressions, 2729 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2730 ) 2731 2732 def jsonobjectagg_sql(self, expression: exp.JSONObjectAgg) -> str: 2733 return self.jsonobject_sql(expression) 2734 2735 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2736 null_handling = expression.args.get("null_handling") 2737 null_handling = f" {null_handling}" if null_handling else "" 2738 return_type = self.sql(expression, "return_type") 2739 return_type = f" RETURNING {return_type}" if return_type else "" 2740 strict = " STRICT" if expression.args.get("strict") else "" 2741 return self.func( 2742 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2743 ) 2744 2745 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2746 this = self.sql(expression, "this") 2747 order = self.sql(expression, "order") 2748 null_handling = expression.args.get("null_handling") 2749 null_handling = f" {null_handling}" if null_handling else "" 2750 return_type = self.sql(expression, "return_type") 2751 return_type = f" RETURNING {return_type}" if return_type else "" 2752 strict = " STRICT" if expression.args.get("strict") else "" 2753 return self.func( 2754 "JSON_ARRAYAGG", 2755 this, 2756 suffix=f"{order}{null_handling}{return_type}{strict})", 2757 ) 2758 2759 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2760 path = self.sql(expression, "path") 2761 path = f" PATH {path}" if path else "" 2762 nested_schema = self.sql(expression, "nested_schema") 2763 2764 if nested_schema: 2765 return f"NESTED{path} {nested_schema}" 2766 2767 this = self.sql(expression, "this") 2768 kind = self.sql(expression, "kind") 2769 kind = f" {kind}" if kind else "" 2770 return f"{this}{kind}{path}" 2771 2772 def jsonschema_sql(self, expression: exp.JSONSchema) -> str: 2773 return self.func("COLUMNS", *expression.expressions) 2774 2775 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2776 this = self.sql(expression, "this") 2777 path = self.sql(expression, "path") 2778 path = f", {path}" if path else "" 2779 error_handling = expression.args.get("error_handling") 2780 error_handling = f" {error_handling}" if error_handling else "" 2781 empty_handling = expression.args.get("empty_handling") 2782 empty_handling = f" {empty_handling}" if empty_handling else "" 2783 schema = self.sql(expression, "schema") 2784 return self.func( 2785 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2786 ) 2787 2788 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2789 this = self.sql(expression, "this") 2790 kind = self.sql(expression, "kind") 2791 path = self.sql(expression, "path") 2792 path = f" {path}" if path else "" 2793 as_json = " AS JSON" if expression.args.get("as_json") else "" 2794 return f"{this} {kind}{path}{as_json}" 2795 2796 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2797 this = self.sql(expression, "this") 2798 path = self.sql(expression, "path") 2799 path = f", {path}" if path else "" 2800 expressions = self.expressions(expression) 2801 with_ = ( 2802 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2803 if expressions 2804 else "" 2805 ) 2806 return f"OPENJSON({this}{path}){with_}" 2807 2808 def in_sql(self, expression: exp.In) -> str: 2809 query = expression.args.get("query") 2810 unnest = expression.args.get("unnest") 2811 field = expression.args.get("field") 2812 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2813 2814 if query: 2815 in_sql = self.sql(query) 2816 elif unnest: 2817 in_sql = self.in_unnest_op(unnest) 2818 elif field: 2819 in_sql = self.sql(field) 2820 else: 2821 in_sql = f"({self.expressions(expression, flat=True)})" 2822 2823 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}" 2824 2825 def in_unnest_op(self, unnest: exp.Unnest) -> str: 2826 return f"(SELECT {self.sql(unnest)})" 2827 2828 def interval_sql(self, expression: exp.Interval) -> str: 2829 unit = self.sql(expression, "unit") 2830 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2831 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2832 unit = f" {unit}" if unit else "" 2833 2834 if self.SINGLE_STRING_INTERVAL: 2835 this = expression.this.name if expression.this else "" 2836 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2837 2838 this = self.sql(expression, "this") 2839 if this: 2840 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2841 this = f" {this}" if unwrapped else f" ({this})" 2842 2843 return f"INTERVAL{this}{unit}" 2844 2845 def return_sql(self, expression: exp.Return) -> str: 2846 return f"RETURN {self.sql(expression, 'this')}" 2847 2848 def reference_sql(self, expression: exp.Reference) -> str: 2849 this = self.sql(expression, "this") 2850 expressions = self.expressions(expression, flat=True) 2851 expressions = f"({expressions})" if expressions else "" 2852 options = self.expressions(expression, key="options", flat=True, sep=" ") 2853 options = f" {options}" if options else "" 2854 return f"REFERENCES {this}{expressions}{options}" 2855 2856 def anonymous_sql(self, expression: exp.Anonymous) -> str: 2857 return self.func(self.sql(expression, "this"), *expression.expressions) 2858 2859 def paren_sql(self, expression: exp.Paren) -> str: 2860 sql = self.seg(self.indent(self.sql(expression, "this")), sep="") 2861 return f"({sql}{self.seg(')', sep='')}" 2862 2863 def neg_sql(self, expression: exp.Neg) -> str: 2864 # This makes sure we don't convert "- - 5" to "--5", which is a comment 2865 this_sql = self.sql(expression, "this") 2866 sep = " " if this_sql[0] == "-" else "" 2867 return f"-{sep}{this_sql}" 2868 2869 def not_sql(self, expression: exp.Not) -> str: 2870 return f"NOT {self.sql(expression, 'this')}" 2871 2872 def alias_sql(self, expression: exp.Alias) -> str: 2873 alias = self.sql(expression, "alias") 2874 alias = f" AS {alias}" if alias else "" 2875 return f"{self.sql(expression, 'this')}{alias}" 2876 2877 def pivotalias_sql(self, expression: exp.PivotAlias) -> str: 2878 alias = expression.args["alias"] 2879 identifier_alias = isinstance(alias, exp.Identifier) 2880 2881 if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2882 alias.replace(exp.Literal.string(alias.output_name)) 2883 elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2884 alias.replace(exp.to_identifier(alias.output_name)) 2885 2886 return self.alias_sql(expression) 2887 2888 def aliases_sql(self, expression: exp.Aliases) -> str: 2889 return f"{self.sql(expression, 'this')} AS ({self.expressions(expression, flat=True)})" 2890 2891 def atindex_sql(self, expression: exp.AtTimeZone) -> str: 2892 this = self.sql(expression, "this") 2893 index = self.sql(expression, "expression") 2894 return f"{this} AT {index}" 2895 2896 def attimezone_sql(self, expression: exp.AtTimeZone) -> str: 2897 this = self.sql(expression, "this") 2898 zone = self.sql(expression, "zone") 2899 return f"{this} AT TIME ZONE {zone}" 2900 2901 def fromtimezone_sql(self, expression: exp.FromTimeZone) -> str: 2902 this = self.sql(expression, "this") 2903 zone = self.sql(expression, "zone") 2904 return f"{this} AT TIME ZONE {zone} AT TIME ZONE 'UTC'" 2905 2906 def add_sql(self, expression: exp.Add) -> str: 2907 return self.binary(expression, "+") 2908 2909 def and_sql( 2910 self, expression: exp.And, stack: t.Optional[t.List[str | exp.Expression]] = None 2911 ) -> str: 2912 return self.connector_sql(expression, "AND", stack) 2913 2914 def or_sql( 2915 self, expression: exp.Or, stack: t.Optional[t.List[str | exp.Expression]] = None 2916 ) -> str: 2917 return self.connector_sql(expression, "OR", stack) 2918 2919 def xor_sql( 2920 self, expression: exp.Xor, stack: t.Optional[t.List[str | exp.Expression]] = None 2921 ) -> str: 2922 return self.connector_sql(expression, "XOR", stack) 2923 2924 def connector_sql( 2925 self, 2926 expression: exp.Connector, 2927 op: str, 2928 stack: t.Optional[t.List[str | exp.Expression]] = None, 2929 ) -> str: 2930 if stack is not None: 2931 if expression.expressions: 2932 stack.append(self.expressions(expression, sep=f" {op} ")) 2933 else: 2934 stack.append(expression.right) 2935 if expression.comments and self.comments: 2936 for comment in expression.comments: 2937 if comment: 2938 op += f" /*{self.pad_comment(comment)}*/" 2939 stack.extend((op, expression.left)) 2940 return op 2941 2942 stack = [expression] 2943 sqls: t.List[str] = [] 2944 ops = set() 2945 2946 while stack: 2947 node = stack.pop() 2948 if isinstance(node, exp.Connector): 2949 ops.add(getattr(self, f"{node.key}_sql")(node, stack)) 2950 else: 2951 sql = self.sql(node) 2952 if sqls and sqls[-1] in ops: 2953 sqls[-1] += f" {sql}" 2954 else: 2955 sqls.append(sql) 2956 2957 sep = "\n" if self.pretty and self.too_wide(sqls) else " " 2958 return sep.join(sqls) 2959 2960 def bitwiseand_sql(self, expression: exp.BitwiseAnd) -> str: 2961 return self.binary(expression, "&") 2962 2963 def bitwiseleftshift_sql(self, expression: exp.BitwiseLeftShift) -> str: 2964 return self.binary(expression, "<<") 2965 2966 def bitwisenot_sql(self, expression: exp.BitwiseNot) -> str: 2967 return f"~{self.sql(expression, 'this')}" 2968 2969 def bitwiseor_sql(self, expression: exp.BitwiseOr) -> str: 2970 return self.binary(expression, "|") 2971 2972 def bitwiserightshift_sql(self, expression: exp.BitwiseRightShift) -> str: 2973 return self.binary(expression, ">>") 2974 2975 def bitwisexor_sql(self, expression: exp.BitwiseXor) -> str: 2976 return self.binary(expression, "^") 2977 2978 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2979 format_sql = self.sql(expression, "format") 2980 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2981 to_sql = self.sql(expression, "to") 2982 to_sql = f" {to_sql}" if to_sql else "" 2983 action = self.sql(expression, "action") 2984 action = f" {action}" if action else "" 2985 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql}{action})" 2986 2987 def currentdate_sql(self, expression: exp.CurrentDate) -> str: 2988 zone = self.sql(expression, "this") 2989 return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE" 2990 2991 def collate_sql(self, expression: exp.Collate) -> str: 2992 if self.COLLATE_IS_FUNC: 2993 return self.function_fallback_sql(expression) 2994 return self.binary(expression, "COLLATE") 2995 2996 def command_sql(self, expression: exp.Command) -> str: 2997 return f"{self.sql(expression, 'this')} {expression.text('expression').strip()}" 2998 2999 def comment_sql(self, expression: exp.Comment) -> str: 3000 this = self.sql(expression, "this") 3001 kind = expression.args["kind"] 3002 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 3003 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 3004 expression_sql = self.sql(expression, "expression") 3005 return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}" 3006 3007 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 3008 this = self.sql(expression, "this") 3009 delete = " DELETE" if expression.args.get("delete") else "" 3010 recompress = self.sql(expression, "recompress") 3011 recompress = f" RECOMPRESS {recompress}" if recompress else "" 3012 to_disk = self.sql(expression, "to_disk") 3013 to_disk = f" TO DISK {to_disk}" if to_disk else "" 3014 to_volume = self.sql(expression, "to_volume") 3015 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 3016 return f"{this}{delete}{recompress}{to_disk}{to_volume}" 3017 3018 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 3019 where = self.sql(expression, "where") 3020 group = self.sql(expression, "group") 3021 aggregates = self.expressions(expression, key="aggregates") 3022 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 3023 3024 if not (where or group or aggregates) and len(expression.expressions) == 1: 3025 return f"TTL {self.expressions(expression, flat=True)}" 3026 3027 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}" 3028 3029 def transaction_sql(self, expression: exp.Transaction) -> str: 3030 return "BEGIN" 3031 3032 def commit_sql(self, expression: exp.Commit) -> str: 3033 chain = expression.args.get("chain") 3034 if chain is not None: 3035 chain = " AND CHAIN" if chain else " AND NO CHAIN" 3036 3037 return f"COMMIT{chain or ''}" 3038 3039 def rollback_sql(self, expression: exp.Rollback) -> str: 3040 savepoint = expression.args.get("savepoint") 3041 savepoint = f" TO {savepoint}" if savepoint else "" 3042 return f"ROLLBACK{savepoint}" 3043 3044 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 3045 this = self.sql(expression, "this") 3046 3047 dtype = self.sql(expression, "dtype") 3048 if dtype: 3049 collate = self.sql(expression, "collate") 3050 collate = f" COLLATE {collate}" if collate else "" 3051 using = self.sql(expression, "using") 3052 using = f" USING {using}" if using else "" 3053 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 3054 3055 default = self.sql(expression, "default") 3056 if default: 3057 return f"ALTER COLUMN {this} SET DEFAULT {default}" 3058 3059 comment = self.sql(expression, "comment") 3060 if comment: 3061 return f"ALTER COLUMN {this} COMMENT {comment}" 3062 3063 allow_null = expression.args.get("allow_null") 3064 drop = expression.args.get("drop") 3065 3066 if not drop and not allow_null: 3067 self.unsupported("Unsupported ALTER COLUMN syntax") 3068 3069 if allow_null is not None: 3070 keyword = "DROP" if drop else "SET" 3071 return f"ALTER COLUMN {this} {keyword} NOT NULL" 3072 3073 return f"ALTER COLUMN {this} DROP DEFAULT" 3074 3075 def alterdiststyle_sql(self, expression: exp.AlterDistStyle) -> str: 3076 this = self.sql(expression, "this") 3077 if not isinstance(expression.this, exp.Var): 3078 this = f"KEY DISTKEY {this}" 3079 return f"ALTER DISTSTYLE {this}" 3080 3081 def altersortkey_sql(self, expression: exp.AlterSortKey) -> str: 3082 compound = " COMPOUND" if expression.args.get("compound") else "" 3083 this = self.sql(expression, "this") 3084 expressions = self.expressions(expression, flat=True) 3085 expressions = f"({expressions})" if expressions else "" 3086 return f"ALTER{compound} SORTKEY {this or expressions}" 3087 3088 def renametable_sql(self, expression: exp.RenameTable) -> str: 3089 if not self.RENAME_TABLE_WITH_DB: 3090 # Remove db from tables 3091 expression = expression.transform( 3092 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 3093 ).assert_is(exp.RenameTable) 3094 this = self.sql(expression, "this") 3095 return f"RENAME TO {this}" 3096 3097 def renamecolumn_sql(self, expression: exp.RenameColumn) -> str: 3098 exists = " IF EXISTS" if expression.args.get("exists") else "" 3099 old_column = self.sql(expression, "this") 3100 new_column = self.sql(expression, "to") 3101 return f"RENAME COLUMN{exists} {old_column} TO {new_column}" 3102 3103 def alterset_sql(self, expression: exp.AlterSet) -> str: 3104 exprs = self.expressions(expression, flat=True) 3105 return f"SET {exprs}" 3106 3107 def altertable_sql(self, expression: exp.AlterTable) -> str: 3108 actions = expression.args["actions"] 3109 3110 if isinstance(actions[0], exp.ColumnDef): 3111 actions = self.add_column_sql(expression) 3112 elif isinstance(actions[0], exp.Schema): 3113 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 3114 elif isinstance(actions[0], exp.Delete): 3115 actions = self.expressions(expression, key="actions", flat=True) 3116 else: 3117 actions = self.expressions(expression, key="actions", flat=True) 3118 3119 exists = " IF EXISTS" if expression.args.get("exists") else "" 3120 on_cluster = self.sql(expression, "cluster") 3121 on_cluster = f" {on_cluster}" if on_cluster else "" 3122 only = " ONLY" if expression.args.get("only") else "" 3123 options = self.expressions(expression, key="options") 3124 options = f", {options}" if options else "" 3125 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{options}" 3126 3127 def add_column_sql(self, expression: exp.AlterTable) -> str: 3128 if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD: 3129 return self.expressions( 3130 expression, 3131 key="actions", 3132 prefix="ADD COLUMN ", 3133 skip_first=True, 3134 ) 3135 return f"ADD {self.expressions(expression, key='actions', flat=True)}" 3136 3137 def droppartition_sql(self, expression: exp.DropPartition) -> str: 3138 expressions = self.expressions(expression) 3139 exists = " IF EXISTS " if expression.args.get("exists") else " " 3140 return f"DROP{exists}{expressions}" 3141 3142 def addconstraint_sql(self, expression: exp.AddConstraint) -> str: 3143 return f"ADD {self.expressions(expression)}" 3144 3145 def distinct_sql(self, expression: exp.Distinct) -> str: 3146 this = self.expressions(expression, flat=True) 3147 3148 if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1: 3149 case = exp.case() 3150 for arg in expression.expressions: 3151 case = case.when(arg.is_(exp.null()), exp.null()) 3152 this = self.sql(case.else_(f"({this})")) 3153 3154 this = f" {this}" if this else "" 3155 3156 on = self.sql(expression, "on") 3157 on = f" ON {on}" if on else "" 3158 return f"DISTINCT{this}{on}" 3159 3160 def ignorenulls_sql(self, expression: exp.IgnoreNulls) -> str: 3161 return self._embed_ignore_nulls(expression, "IGNORE NULLS") 3162 3163 def respectnulls_sql(self, expression: exp.RespectNulls) -> str: 3164 return self._embed_ignore_nulls(expression, "RESPECT NULLS") 3165 3166 def havingmax_sql(self, expression: exp.HavingMax) -> str: 3167 this_sql = self.sql(expression, "this") 3168 expression_sql = self.sql(expression, "expression") 3169 kind = "MAX" if expression.args.get("max") else "MIN" 3170 return f"{this_sql} HAVING {kind} {expression_sql}" 3171 3172 def intdiv_sql(self, expression: exp.IntDiv) -> str: 3173 return self.sql( 3174 exp.Cast( 3175 this=exp.Div(this=expression.this, expression=expression.expression), 3176 to=exp.DataType(this=exp.DataType.Type.INT), 3177 ) 3178 ) 3179 3180 def dpipe_sql(self, expression: exp.DPipe) -> str: 3181 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 3182 return self.func( 3183 "CONCAT", *(exp.cast(e, exp.DataType.Type.TEXT) for e in expression.flatten()) 3184 ) 3185 return self.binary(expression, "||") 3186 3187 def div_sql(self, expression: exp.Div) -> str: 3188 l, r = expression.left, expression.right 3189 3190 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 3191 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 3192 3193 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 3194 if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES): 3195 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 3196 3197 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 3198 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 3199 return self.sql( 3200 exp.cast( 3201 l / r, 3202 to=exp.DataType.Type.BIGINT, 3203 ) 3204 ) 3205 3206 return self.binary(expression, "/") 3207 3208 def overlaps_sql(self, expression: exp.Overlaps) -> str: 3209 return self.binary(expression, "OVERLAPS") 3210 3211 def distance_sql(self, expression: exp.Distance) -> str: 3212 return self.binary(expression, "<->") 3213 3214 def dot_sql(self, expression: exp.Dot) -> str: 3215 return f"{self.sql(expression, 'this')}.{self.sql(expression, 'expression')}" 3216 3217 def eq_sql(self, expression: exp.EQ) -> str: 3218 return self.binary(expression, "=") 3219 3220 def propertyeq_sql(self, expression: exp.PropertyEQ) -> str: 3221 return self.binary(expression, ":=") 3222 3223 def escape_sql(self, expression: exp.Escape) -> str: 3224 return self.binary(expression, "ESCAPE") 3225 3226 def glob_sql(self, expression: exp.Glob) -> str: 3227 return self.binary(expression, "GLOB") 3228 3229 def gt_sql(self, expression: exp.GT) -> str: 3230 return self.binary(expression, ">") 3231 3232 def gte_sql(self, expression: exp.GTE) -> str: 3233 return self.binary(expression, ">=") 3234 3235 def ilike_sql(self, expression: exp.ILike) -> str: 3236 return self.binary(expression, "ILIKE") 3237 3238 def ilikeany_sql(self, expression: exp.ILikeAny) -> str: 3239 return self.binary(expression, "ILIKE ANY") 3240 3241 def is_sql(self, expression: exp.Is) -> str: 3242 if not self.IS_BOOL_ALLOWED and isinstance(expression.expression, exp.Boolean): 3243 return self.sql( 3244 expression.this if expression.expression.this else exp.not_(expression.this) 3245 ) 3246 return self.binary(expression, "IS") 3247 3248 def like_sql(self, expression: exp.Like) -> str: 3249 return self.binary(expression, "LIKE") 3250 3251 def likeany_sql(self, expression: exp.LikeAny) -> str: 3252 return self.binary(expression, "LIKE ANY") 3253 3254 def similarto_sql(self, expression: exp.SimilarTo) -> str: 3255 return self.binary(expression, "SIMILAR TO") 3256 3257 def lt_sql(self, expression: exp.LT) -> str: 3258 return self.binary(expression, "<") 3259 3260 def lte_sql(self, expression: exp.LTE) -> str: 3261 return self.binary(expression, "<=") 3262 3263 def mod_sql(self, expression: exp.Mod) -> str: 3264 return self.binary(expression, "%") 3265 3266 def mul_sql(self, expression: exp.Mul) -> str: 3267 return self.binary(expression, "*") 3268 3269 def neq_sql(self, expression: exp.NEQ) -> str: 3270 return self.binary(expression, "<>") 3271 3272 def nullsafeeq_sql(self, expression: exp.NullSafeEQ) -> str: 3273 return self.binary(expression, "IS NOT DISTINCT FROM") 3274 3275 def nullsafeneq_sql(self, expression: exp.NullSafeNEQ) -> str: 3276 return self.binary(expression, "IS DISTINCT FROM") 3277 3278 def slice_sql(self, expression: exp.Slice) -> str: 3279 return self.binary(expression, ":") 3280 3281 def sub_sql(self, expression: exp.Sub) -> str: 3282 return self.binary(expression, "-") 3283 3284 def trycast_sql(self, expression: exp.TryCast) -> str: 3285 return self.cast_sql(expression, safe_prefix="TRY_") 3286 3287 def try_sql(self, expression: exp.Try) -> str: 3288 if not self.TRY_SUPPORTED: 3289 self.unsupported("Unsupported TRY function") 3290 return self.sql(expression, "this") 3291 3292 return self.func("TRY", expression.this) 3293 3294 def log_sql(self, expression: exp.Log) -> str: 3295 this = expression.this 3296 expr = expression.expression 3297 3298 if self.dialect.LOG_BASE_FIRST is False: 3299 this, expr = expr, this 3300 elif self.dialect.LOG_BASE_FIRST is None and expr: 3301 if this.name in ("2", "10"): 3302 return self.func(f"LOG{this.name}", expr) 3303 3304 self.unsupported(f"Unsupported logarithm with base {self.sql(this)}") 3305 3306 return self.func("LOG", this, expr) 3307 3308 def use_sql(self, expression: exp.Use) -> str: 3309 kind = self.sql(expression, "kind") 3310 kind = f" {kind}" if kind else "" 3311 this = self.sql(expression, "this") 3312 this = f" {this}" if this else "" 3313 return f"USE{kind}{this}" 3314 3315 def binary(self, expression: exp.Binary, op: str) -> str: 3316 op = self.maybe_comment(op, comments=expression.comments) 3317 return f"{self.sql(expression, 'this')} {op} {self.sql(expression, 'expression')}" 3318 3319 def function_fallback_sql(self, expression: exp.Func) -> str: 3320 args = [] 3321 3322 for key in expression.arg_types: 3323 arg_value = expression.args.get(key) 3324 3325 if isinstance(arg_value, list): 3326 for value in arg_value: 3327 args.append(value) 3328 elif arg_value is not None: 3329 args.append(arg_value) 3330 3331 if self.normalize_functions: 3332 name = expression.sql_name() 3333 else: 3334 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 3335 3336 return self.func(name, *args) 3337 3338 def func( 3339 self, 3340 name: str, 3341 *args: t.Optional[exp.Expression | str], 3342 prefix: str = "(", 3343 suffix: str = ")", 3344 ) -> str: 3345 return f"{self.normalize_func(name)}{prefix}{self.format_args(*args)}{suffix}" 3346 3347 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 3348 arg_sqls = tuple( 3349 self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool) 3350 ) 3351 if self.pretty and self.too_wide(arg_sqls): 3352 return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 3353 return ", ".join(arg_sqls) 3354 3355 def too_wide(self, args: t.Iterable) -> bool: 3356 return sum(len(arg) for arg in args) > self.max_text_width 3357 3358 def format_time( 3359 self, 3360 expression: exp.Expression, 3361 inverse_time_mapping: t.Optional[t.Dict[str, str]] = None, 3362 inverse_time_trie: t.Optional[t.Dict] = None, 3363 ) -> t.Optional[str]: 3364 return format_time( 3365 self.sql(expression, "format"), 3366 inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING, 3367 inverse_time_trie or self.dialect.INVERSE_TIME_TRIE, 3368 ) 3369 3370 def expressions( 3371 self, 3372 expression: t.Optional[exp.Expression] = None, 3373 key: t.Optional[str] = None, 3374 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 3375 flat: bool = False, 3376 indent: bool = True, 3377 skip_first: bool = False, 3378 skip_last: bool = False, 3379 sep: str = ", ", 3380 prefix: str = "", 3381 dynamic: bool = False, 3382 new_line: bool = False, 3383 ) -> str: 3384 expressions = expression.args.get(key or "expressions") if expression else sqls 3385 3386 if not expressions: 3387 return "" 3388 3389 if flat: 3390 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 3391 3392 num_sqls = len(expressions) 3393 result_sqls = [] 3394 3395 for i, e in enumerate(expressions): 3396 sql = self.sql(e, comment=False) 3397 if not sql: 3398 continue 3399 3400 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 3401 3402 if self.pretty: 3403 if self.leading_comma: 3404 result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}") 3405 else: 3406 result_sqls.append( 3407 f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}" 3408 ) 3409 else: 3410 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 3411 3412 if self.pretty and (not dynamic or self.too_wide(result_sqls)): 3413 if new_line: 3414 result_sqls.insert(0, "") 3415 result_sqls.append("") 3416 result_sql = "\n".join(s.rstrip() for s in result_sqls) 3417 else: 3418 result_sql = "".join(result_sqls) 3419 return ( 3420 self.indent(result_sql, skip_first=skip_first, skip_last=skip_last) 3421 if indent 3422 else result_sql 3423 ) 3424 3425 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 3426 flat = flat or isinstance(expression.parent, exp.Properties) 3427 expressions_sql = self.expressions(expression, flat=flat) 3428 if flat: 3429 return f"{op} {expressions_sql}" 3430 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}" 3431 3432 def naked_property(self, expression: exp.Property) -> str: 3433 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 3434 if not property_name: 3435 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 3436 return f"{property_name} {self.sql(expression, 'this')}" 3437 3438 def tag_sql(self, expression: exp.Tag) -> str: 3439 return f"{expression.args.get('prefix')}{self.sql(expression.this)}{expression.args.get('postfix')}" 3440 3441 def token_sql(self, token_type: TokenType) -> str: 3442 return self.TOKEN_MAPPING.get(token_type, token_type.name) 3443 3444 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 3445 this = self.sql(expression, "this") 3446 expressions = self.no_identify(self.expressions, expression) 3447 expressions = ( 3448 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 3449 ) 3450 return f"{this}{expressions}" 3451 3452 def joinhint_sql(self, expression: exp.JoinHint) -> str: 3453 this = self.sql(expression, "this") 3454 expressions = self.expressions(expression, flat=True) 3455 return f"{this}({expressions})" 3456 3457 def kwarg_sql(self, expression: exp.Kwarg) -> str: 3458 return self.binary(expression, "=>") 3459 3460 def when_sql(self, expression: exp.When) -> str: 3461 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 3462 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 3463 condition = self.sql(expression, "condition") 3464 condition = f" AND {condition}" if condition else "" 3465 3466 then_expression = expression.args.get("then") 3467 if isinstance(then_expression, exp.Insert): 3468 this = self.sql(then_expression, "this") 3469 this = f"INSERT {this}" if this else "INSERT" 3470 then = self.sql(then_expression, "expression") 3471 then = f"{this} VALUES {then}" if then else this 3472 elif isinstance(then_expression, exp.Update): 3473 if isinstance(then_expression.args.get("expressions"), exp.Star): 3474 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 3475 else: 3476 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 3477 else: 3478 then = self.sql(then_expression) 3479 return f"WHEN {matched}{source}{condition} THEN {then}" 3480 3481 def merge_sql(self, expression: exp.Merge) -> str: 3482 table = expression.this 3483 table_alias = "" 3484 3485 hints = table.args.get("hints") 3486 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 3487 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 3488 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 3489 3490 this = self.sql(table) 3491 using = f"USING {self.sql(expression, 'using')}" 3492 on = f"ON {self.sql(expression, 'on')}" 3493 expressions = self.expressions(expression, sep=" ", indent=False) 3494 sep = self.sep() 3495 3496 return self.prepend_ctes( 3497 expression, f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{expressions}" 3498 ) 3499 3500 def tochar_sql(self, expression: exp.ToChar) -> str: 3501 if expression.args.get("format"): 3502 self.unsupported("Format argument unsupported for TO_CHAR/TO_VARCHAR function") 3503 3504 return self.sql(exp.cast(expression.this, exp.DataType.Type.TEXT)) 3505 3506 def tonumber_sql(self, expression: exp.ToNumber) -> str: 3507 if not self.SUPPORTS_TO_NUMBER: 3508 self.unsupported("Unsupported TO_NUMBER function") 3509 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3510 3511 fmt = expression.args.get("format") 3512 if not fmt: 3513 self.unsupported("Conversion format is required for TO_NUMBER") 3514 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3515 3516 return self.func("TO_NUMBER", expression.this, fmt) 3517 3518 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 3519 this = self.sql(expression, "this") 3520 kind = self.sql(expression, "kind") 3521 settings_sql = self.expressions(expression, key="settings", sep=" ") 3522 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 3523 return f"{this}({kind}{args})" 3524 3525 def dictrange_sql(self, expression: exp.DictRange) -> str: 3526 this = self.sql(expression, "this") 3527 max = self.sql(expression, "max") 3528 min = self.sql(expression, "min") 3529 return f"{this}(MIN {min} MAX {max})" 3530 3531 def dictsubproperty_sql(self, expression: exp.DictSubProperty) -> str: 3532 return f"{self.sql(expression, 'this')} {self.sql(expression, 'value')}" 3533 3534 def oncluster_sql(self, expression: exp.OnCluster) -> str: 3535 return "" 3536 3537 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3538 expressions = self.expressions(expression, key="expressions", flat=True) 3539 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3540 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3541 buckets = self.sql(expression, "buckets") 3542 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS" 3543 3544 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3545 this = self.sql(expression, "this") 3546 having = self.sql(expression, "having") 3547 3548 if having: 3549 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3550 3551 return self.func("ANY_VALUE", this) 3552 3553 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3554 transform = self.func("TRANSFORM", *expression.expressions) 3555 row_format_before = self.sql(expression, "row_format_before") 3556 row_format_before = f" {row_format_before}" if row_format_before else "" 3557 record_writer = self.sql(expression, "record_writer") 3558 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3559 using = f" USING {self.sql(expression, 'command_script')}" 3560 schema = self.sql(expression, "schema") 3561 schema = f" AS {schema}" if schema else "" 3562 row_format_after = self.sql(expression, "row_format_after") 3563 row_format_after = f" {row_format_after}" if row_format_after else "" 3564 record_reader = self.sql(expression, "record_reader") 3565 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3566 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}" 3567 3568 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3569 key_block_size = self.sql(expression, "key_block_size") 3570 if key_block_size: 3571 return f"KEY_BLOCK_SIZE = {key_block_size}" 3572 3573 using = self.sql(expression, "using") 3574 if using: 3575 return f"USING {using}" 3576 3577 parser = self.sql(expression, "parser") 3578 if parser: 3579 return f"WITH PARSER {parser}" 3580 3581 comment = self.sql(expression, "comment") 3582 if comment: 3583 return f"COMMENT {comment}" 3584 3585 visible = expression.args.get("visible") 3586 if visible is not None: 3587 return "VISIBLE" if visible else "INVISIBLE" 3588 3589 engine_attr = self.sql(expression, "engine_attr") 3590 if engine_attr: 3591 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3592 3593 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3594 if secondary_engine_attr: 3595 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3596 3597 self.unsupported("Unsupported index constraint option.") 3598 return "" 3599 3600 def checkcolumnconstraint_sql(self, expression: exp.CheckColumnConstraint) -> str: 3601 enforced = " ENFORCED" if expression.args.get("enforced") else "" 3602 return f"CHECK ({self.sql(expression, 'this')}){enforced}" 3603 3604 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3605 kind = self.sql(expression, "kind") 3606 kind = f"{kind} INDEX" if kind else "INDEX" 3607 this = self.sql(expression, "this") 3608 this = f" {this}" if this else "" 3609 index_type = self.sql(expression, "index_type") 3610 index_type = f" USING {index_type}" if index_type else "" 3611 expressions = self.expressions(expression, flat=True) 3612 expressions = f" ({expressions})" if expressions else "" 3613 options = self.expressions(expression, key="options", sep=" ") 3614 options = f" {options}" if options else "" 3615 return f"{kind}{this}{index_type}{expressions}{options}" 3616 3617 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3618 if self.NVL2_SUPPORTED: 3619 return self.function_fallback_sql(expression) 3620 3621 case = exp.Case().when( 3622 expression.this.is_(exp.null()).not_(copy=False), 3623 expression.args["true"], 3624 copy=False, 3625 ) 3626 else_cond = expression.args.get("false") 3627 if else_cond: 3628 case.else_(else_cond, copy=False) 3629 3630 return self.sql(case) 3631 3632 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3633 this = self.sql(expression, "this") 3634 expr = self.sql(expression, "expression") 3635 iterator = self.sql(expression, "iterator") 3636 condition = self.sql(expression, "condition") 3637 condition = f" IF {condition}" if condition else "" 3638 return f"{this} FOR {expr} IN {iterator}{condition}" 3639 3640 def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str: 3641 return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})" 3642 3643 def opclass_sql(self, expression: exp.Opclass) -> str: 3644 return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" 3645 3646 def predict_sql(self, expression: exp.Predict) -> str: 3647 model = self.sql(expression, "this") 3648 model = f"MODEL {model}" 3649 table = self.sql(expression, "expression") 3650 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3651 parameters = self.sql(expression, "params_struct") 3652 return self.func("PREDICT", model, table, parameters or None) 3653 3654 def forin_sql(self, expression: exp.ForIn) -> str: 3655 this = self.sql(expression, "this") 3656 expression_sql = self.sql(expression, "expression") 3657 return f"FOR {this} DO {expression_sql}" 3658 3659 def refresh_sql(self, expression: exp.Refresh) -> str: 3660 this = self.sql(expression, "this") 3661 table = "" if isinstance(expression.this, exp.Literal) else "TABLE " 3662 return f"REFRESH {table}{this}" 3663 3664 def operator_sql(self, expression: exp.Operator) -> str: 3665 return self.binary(expression, f"OPERATOR({self.sql(expression, 'operator')})") 3666 3667 def toarray_sql(self, expression: exp.ToArray) -> str: 3668 arg = expression.this 3669 if not arg.type: 3670 from sqlglot.optimizer.annotate_types import annotate_types 3671 3672 arg = annotate_types(arg) 3673 3674 if arg.is_type(exp.DataType.Type.ARRAY): 3675 return self.sql(arg) 3676 3677 cond_for_null = arg.is_(exp.null()) 3678 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False))) 3679 3680 def tsordstotime_sql(self, expression: exp.TsOrDsToTime) -> str: 3681 this = expression.this 3682 if isinstance(this, exp.TsOrDsToTime) or this.is_type(exp.DataType.Type.TIME): 3683 return self.sql(this) 3684 3685 return self.sql(exp.cast(this, exp.DataType.Type.TIME)) 3686 3687 def tsordstotimestamp_sql(self, expression: exp.TsOrDsToTimestamp) -> str: 3688 this = expression.this 3689 if isinstance(this, exp.TsOrDsToTimestamp) or this.is_type(exp.DataType.Type.TIMESTAMP): 3690 return self.sql(this) 3691 3692 return self.sql(exp.cast(this, exp.DataType.Type.TIMESTAMP)) 3693 3694 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3695 this = expression.this 3696 time_format = self.format_time(expression) 3697 3698 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3699 return self.sql( 3700 exp.cast( 3701 exp.StrToTime(this=this, format=expression.args["format"]), 3702 exp.DataType.Type.DATE, 3703 ) 3704 ) 3705 3706 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3707 return self.sql(this) 3708 3709 return self.sql(exp.cast(this, exp.DataType.Type.DATE)) 3710 3711 def unixdate_sql(self, expression: exp.UnixDate) -> str: 3712 return self.sql( 3713 exp.func( 3714 "DATEDIFF", 3715 expression.this, 3716 exp.cast(exp.Literal.string("1970-01-01"), exp.DataType.Type.DATE), 3717 "day", 3718 ) 3719 ) 3720 3721 def lastday_sql(self, expression: exp.LastDay) -> str: 3722 if self.LAST_DAY_SUPPORTS_DATE_PART: 3723 return self.function_fallback_sql(expression) 3724 3725 unit = expression.text("unit") 3726 if unit and unit != "MONTH": 3727 self.unsupported("Date parts are not supported in LAST_DAY.") 3728 3729 return self.func("LAST_DAY", expression.this) 3730 3731 def dateadd_sql(self, expression: exp.DateAdd) -> str: 3732 from sqlglot.dialects.dialect import unit_to_str 3733 3734 return self.func( 3735 "DATE_ADD", expression.this, expression.expression, unit_to_str(expression) 3736 ) 3737 3738 def arrayany_sql(self, expression: exp.ArrayAny) -> str: 3739 if self.CAN_IMPLEMENT_ARRAY_ANY: 3740 filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression) 3741 filtered_not_empty = exp.ArraySize(this=filtered).neq(0) 3742 original_is_empty = exp.ArraySize(this=expression.this).eq(0) 3743 return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty))) 3744 3745 from sqlglot.dialects import Dialect 3746 3747 # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect 3748 if self.dialect.__class__ != Dialect: 3749 self.unsupported("ARRAY_ANY is unsupported") 3750 3751 return self.function_fallback_sql(expression) 3752 3753 def struct_sql(self, expression: exp.Struct) -> str: 3754 expression.set( 3755 "expressions", 3756 [ 3757 exp.alias_(e.expression, e.name if e.this.is_string else e.this) 3758 if isinstance(e, exp.PropertyEQ) 3759 else e 3760 for e in expression.expressions 3761 ], 3762 ) 3763 3764 return self.function_fallback_sql(expression) 3765 3766 def partitionrange_sql(self, expression: exp.PartitionRange) -> str: 3767 low = self.sql(expression, "this") 3768 high = self.sql(expression, "expression") 3769 3770 return f"{low} TO {high}" 3771 3772 def truncatetable_sql(self, expression: exp.TruncateTable) -> str: 3773 target = "DATABASE" if expression.args.get("is_database") else "TABLE" 3774 tables = f" {self.expressions(expression)}" 3775 3776 exists = " IF EXISTS" if expression.args.get("exists") else "" 3777 3778 on_cluster = self.sql(expression, "cluster") 3779 on_cluster = f" {on_cluster}" if on_cluster else "" 3780 3781 identity = self.sql(expression, "identity") 3782 identity = f" {identity} IDENTITY" if identity else "" 3783 3784 option = self.sql(expression, "option") 3785 option = f" {option}" if option else "" 3786 3787 partition = self.sql(expression, "partition") 3788 partition = f" {partition}" if partition else "" 3789 3790 return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}" 3791 3792 # This transpiles T-SQL's CONVERT function 3793 # https://learn.microsoft.com/en-us/sql/t-sql/functions/cast-and-convert-transact-sql?view=sql-server-ver16 3794 def convert_sql(self, expression: exp.Convert) -> str: 3795 to = expression.this 3796 value = expression.expression 3797 style = expression.args.get("style") 3798 safe = expression.args.get("safe") 3799 strict = expression.args.get("strict") 3800 3801 if not to or not value: 3802 return "" 3803 3804 # Retrieve length of datatype and override to default if not specified 3805 if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3806 to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False) 3807 3808 transformed: t.Optional[exp.Expression] = None 3809 cast = exp.Cast if strict else exp.TryCast 3810 3811 # Check whether a conversion with format (T-SQL calls this 'style') is applicable 3812 if isinstance(style, exp.Literal) and style.is_int: 3813 from sqlglot.dialects.tsql import TSQL 3814 3815 style_value = style.name 3816 converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value) 3817 if not converted_style: 3818 self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}") 3819 3820 fmt = exp.Literal.string(converted_style) 3821 3822 if to.this == exp.DataType.Type.DATE: 3823 transformed = exp.StrToDate(this=value, format=fmt) 3824 elif to.this == exp.DataType.Type.DATETIME: 3825 transformed = exp.StrToTime(this=value, format=fmt) 3826 elif to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3827 transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe) 3828 elif to.this == exp.DataType.Type.TEXT: 3829 transformed = exp.TimeToStr(this=value, format=fmt) 3830 3831 if not transformed: 3832 transformed = cast(this=value, to=to, safe=safe) 3833 3834 return self.sql(transformed) 3835 3836 def _jsonpathkey_sql(self, expression: exp.JSONPathKey) -> str: 3837 this = expression.this 3838 if isinstance(this, exp.JSONPathWildcard): 3839 this = self.json_path_part(this) 3840 return f".{this}" if this else "" 3841 3842 if exp.SAFE_IDENTIFIER_RE.match(this): 3843 return f".{this}" 3844 3845 this = self.json_path_part(this) 3846 return f"[{this}]" if self.JSON_PATH_BRACKETED_KEY_SUPPORTED else f".{this}" 3847 3848 def _jsonpathsubscript_sql(self, expression: exp.JSONPathSubscript) -> str: 3849 this = self.json_path_part(expression.this) 3850 return f"[{this}]" if this else "" 3851 3852 def _simplify_unless_literal(self, expression: E) -> E: 3853 if not isinstance(expression, exp.Literal): 3854 from sqlglot.optimizer.simplify import simplify 3855 3856 expression = simplify(expression, dialect=self.dialect) 3857 3858 return expression 3859 3860 def _embed_ignore_nulls(self, expression: exp.IgnoreNulls | exp.RespectNulls, text: str) -> str: 3861 if self.IGNORE_NULLS_IN_FUNC and not expression.meta.get("inline"): 3862 # The first modifier here will be the one closest to the AggFunc's arg 3863 mods = sorted( 3864 expression.find_all(exp.HavingMax, exp.Order, exp.Limit), 3865 key=lambda x: 0 3866 if isinstance(x, exp.HavingMax) 3867 else (1 if isinstance(x, exp.Order) else 2), 3868 ) 3869 3870 if mods: 3871 mod = mods[0] 3872 this = expression.__class__(this=mod.this.copy()) 3873 this.meta["inline"] = True 3874 mod.this.replace(this) 3875 return self.sql(expression.this) 3876 3877 agg_func = expression.find(exp.AggFunc) 3878 3879 if agg_func: 3880 return self.sql(agg_func)[:-1] + f" {text})" 3881 3882 return f"{self.sql(expression, 'this')} {text}" 3883 3884 def _replace_line_breaks(self, string: str) -> str: 3885 """We don't want to extra indent line breaks so we temporarily replace them with sentinels.""" 3886 if self.pretty: 3887 return string.replace("\n", self.SENTINEL_LINE_BREAK) 3888 return string 3889 3890 def copyparameter_sql(self, expression: exp.CopyParameter) -> str: 3891 option = self.sql(expression, "this") 3892 3893 if expression.expressions: 3894 upper = option.upper() 3895 3896 # Snowflake FILE_FORMAT options are separated by whitespace 3897 sep = " " if upper == "FILE_FORMAT" else ", " 3898 3899 # Databricks copy/format options do not set their list of values with EQ 3900 op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = " 3901 values = self.expressions(expression, flat=True, sep=sep) 3902 return f"{option}{op}({values})" 3903 3904 value = self.sql(expression, "expression") 3905 3906 if not value: 3907 return option 3908 3909 op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " " 3910 3911 return f"{option}{op}{value}" 3912 3913 def credentials_sql(self, expression: exp.Credentials) -> str: 3914 cred_expr = expression.args.get("credentials") 3915 if isinstance(cred_expr, exp.Literal): 3916 # Redshift case: CREDENTIALS <string> 3917 credentials = self.sql(expression, "credentials") 3918 credentials = f"CREDENTIALS {credentials}" if credentials else "" 3919 else: 3920 # Snowflake case: CREDENTIALS = (...) 3921 credentials = self.expressions(expression, key="credentials", flat=True, sep=" ") 3922 credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else "" 3923 3924 storage = self.sql(expression, "storage") 3925 storage = f"STORAGE_INTEGRATION = {storage}" if storage else "" 3926 3927 encryption = self.expressions(expression, key="encryption", flat=True, sep=" ") 3928 encryption = f" ENCRYPTION = ({encryption})" if encryption else "" 3929 3930 iam_role = self.sql(expression, "iam_role") 3931 iam_role = f"IAM_ROLE {iam_role}" if iam_role else "" 3932 3933 region = self.sql(expression, "region") 3934 region = f" REGION {region}" if region else "" 3935 3936 return f"{credentials}{storage}{encryption}{iam_role}{region}" 3937 3938 def copy_sql(self, expression: exp.Copy) -> str: 3939 this = self.sql(expression, "this") 3940 this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}" 3941 3942 credentials = self.sql(expression, "credentials") 3943 credentials = self.seg(credentials) if credentials else "" 3944 kind = self.seg("FROM" if expression.args.get("kind") else "TO") 3945 files = self.expressions(expression, key="files", flat=True) 3946 3947 sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " " 3948 params = self.expressions( 3949 expression, 3950 key="params", 3951 sep=sep, 3952 new_line=True, 3953 skip_last=True, 3954 skip_first=True, 3955 indent=self.COPY_PARAMS_ARE_WRAPPED, 3956 ) 3957 3958 if params: 3959 if self.COPY_PARAMS_ARE_WRAPPED: 3960 params = f" WITH ({params})" 3961 elif not self.pretty: 3962 params = f" {params}" 3963 3964 return f"COPY{this}{kind} {files}{credentials}{params}" 3965 3966 def semicolon_sql(self, expression: exp.Semicolon) -> str: 3967 return "" 3968 3969 def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str: 3970 on_sql = "ON" if expression.args.get("on") else "OFF" 3971 filter_col: t.Optional[str] = self.sql(expression, "filter_column") 3972 filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None 3973 retention_period: t.Optional[str] = self.sql(expression, "retention_period") 3974 retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None 3975 3976 if filter_col or retention_period: 3977 on_sql = self.func("ON", filter_col, retention_period) 3978 3979 return f"DATA_DELETION={on_sql}" 3980 3981 def maskingpolicycolumnconstraint_sql( 3982 self, expression: exp.MaskingPolicyColumnConstraint 3983 ) -> str: 3984 this = self.sql(expression, "this") 3985 expressions = self.expressions(expression, flat=True) 3986 expressions = f" USING ({expressions})" if expressions else "" 3987 return f"MASKING POLICY {this}{expressions}" 3988 3989 def gapfill_sql(self, expression: exp.GapFill) -> str: 3990 this = self.sql(expression, "this") 3991 this = f"TABLE {this}" 3992 return self.func("GAP_FILL", this, *[v for k, v in expression.args.items() if k != "this"]) 3993 3994 def scope_resolution(self, rhs: str, scope_name: str) -> str: 3995 return self.func("SCOPE_RESOLUTION", scope_name or None, rhs) 3996 3997 def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str: 3998 this = self.sql(expression, "this") 3999 expr = expression.expression 4000 4001 if isinstance(expr, exp.Func): 4002 # T-SQL's CLR functions are case sensitive 4003 expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})" 4004 else: 4005 expr = self.sql(expression, "expression") 4006 4007 return self.scope_resolution(expr, this) 4008 4009 def parsejson_sql(self, expression: exp.ParseJSON) -> str: 4010 if self.PARSE_JSON_NAME is None: 4011 return self.sql(expression.this) 4012 4013 return self.func(self.PARSE_JSON_NAME, expression.this, expression.expression) 4014 4015 def rand_sql(self, expression: exp.Rand) -> str: 4016 lower = self.sql(expression, "lower") 4017 upper = self.sql(expression, "upper") 4018 4019 if lower and upper: 4020 return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}" 4021 return self.func("RAND", expression.this) 4022 4023 def changes_sql(self, expression: exp.Changes) -> str: 4024 information = self.sql(expression, "information") 4025 information = f"INFORMATION => {information}" 4026 at_before = self.sql(expression, "at_before") 4027 at_before = f"{self.seg('')}{at_before}" if at_before else "" 4028 end = self.sql(expression, "end") 4029 end = f"{self.seg('')}{end}" if end else "" 4030 4031 return f"CHANGES ({information}){at_before}{end}" 4032 4033 def pad_sql(self, expression: exp.Pad) -> str: 4034 prefix = "L" if expression.args.get("is_left") else "R" 4035 4036 fill_pattern = self.sql(expression, "fill_pattern") or None 4037 if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED: 4038 fill_pattern = "' '" 4039 4040 return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern) 4041 4042 def summarize_sql(self, expression: exp.Summarize) -> str: 4043 table = " TABLE" if expression.args.get("table") else "" 4044 return f"SUMMARIZE{table} {self.sql(expression.this)}"
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)
571 def __init__( 572 self, 573 pretty: t.Optional[bool] = None, 574 identify: str | bool = False, 575 normalize: bool = False, 576 pad: int = 2, 577 indent: int = 2, 578 normalize_functions: t.Optional[str | bool] = None, 579 unsupported_level: ErrorLevel = ErrorLevel.WARN, 580 max_unsupported: int = 3, 581 leading_comma: bool = False, 582 max_text_width: int = 80, 583 comments: bool = True, 584 dialect: DialectType = None, 585 ): 586 import sqlglot 587 from sqlglot.dialects import Dialect 588 589 self.pretty = pretty if pretty is not None else sqlglot.pretty 590 self.identify = identify 591 self.normalize = normalize 592 self.pad = pad 593 self._indent = indent 594 self.unsupported_level = unsupported_level 595 self.max_unsupported = max_unsupported 596 self.leading_comma = leading_comma 597 self.max_text_width = max_text_width 598 self.comments = comments 599 self.dialect = Dialect.get_or_raise(dialect) 600 601 # This is both a Dialect property and a Generator argument, so we prioritize the latter 602 self.normalize_functions = ( 603 self.dialect.NORMALIZE_FUNCTIONS if normalize_functions is None else normalize_functions 604 ) 605 606 self.unsupported_messages: t.List[str] = [] 607 self._escaped_quote_end: str = ( 608 self.dialect.tokenizer_class.STRING_ESCAPES[0] + self.dialect.QUOTE_END 609 ) 610 self._escaped_identifier_end: str = ( 611 self.dialect.tokenizer_class.IDENTIFIER_ESCAPES[0] + self.dialect.IDENTIFIER_END 612 ) 613 614 self._next_name = name_sequence("_t")
TRANSFORMS: Dict[Type[sqlglot.expressions.Expression], Callable[..., str]] =
{<class 'sqlglot.expressions.JSONPathFilter'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathKey'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRecursive'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathRoot'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathScript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSelector'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSlice'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathSubscript'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathUnion'>: <function <lambda>>, <class 'sqlglot.expressions.JSONPathWildcard'>: <function <lambda>>, <class 'sqlglot.expressions.AllowedValuesProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.BackupProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CaseSpecificColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CharacterSetProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ClusteredColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CollateColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.CommentColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ConnectByRoot'>: <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.DynamicProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EncodeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.EphemeralColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExcludeColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExecuteAsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.ExternalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.GlobalProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.HeapProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IcebergProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InheritsProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InlineLengthColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.InputModelProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.IntervalSpan'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.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.ProjectionPolicyColumnConstraint'>: <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.SecureProperty'>: <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.StrictProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TemporaryProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TagColumnConstraint'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.TitleColumnConstraint'>: <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.WithSchemaBindingProperty'>: <function Generator.<lambda>>, <class 'sqlglot.expressions.WithOperator'>: <function Generator.<lambda>>}
SUPPORTED_JSON_PATH_PARTS =
{<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'>, <class 'sqlglot.expressions.JSONPathFilter'>}
TYPE_MAPPING =
{<Type.NCHAR: 'NCHAR'>: 'CHAR', <Type.NVARCHAR: 'NVARCHAR'>: 'VARCHAR', <Type.MEDIUMTEXT: 'MEDIUMTEXT'>: 'TEXT', <Type.LONGTEXT: 'LONGTEXT'>: 'TEXT', <Type.TINYTEXT: 'TINYTEXT'>: 'TEXT', <Type.MEDIUMBLOB: 'MEDIUMBLOB'>: 'BLOB', <Type.LONGBLOB: 'LONGBLOB'>: 'BLOB', <Type.TINYBLOB: 'TINYBLOB'>: 'BLOB', <Type.INET: 'INET'>: 'INET', <Type.ROWVERSION: 'ROWVERSION'>: 'VARBINARY'}
TIME_PART_SINGULARS =
{'MICROSECONDS': 'MICROSECOND', 'SECONDS': 'SECOND', 'MINUTES': 'MINUTE', 'HOURS': 'HOUR', 'DAYS': 'DAY', 'WEEKS': 'WEEK', 'MONTHS': 'MONTH', 'QUARTERS': 'QUARTER', 'YEARS': 'YEAR'}
AFTER_HAVING_MODIFIER_TRANSFORMS =
{'cluster': <function Generator.<lambda>>, 'distribute': <function Generator.<lambda>>, 'sort': <function Generator.<lambda>>, 'windows': <function Generator.<lambda>>, 'qualify': <function Generator.<lambda>>}
PROPERTIES_LOCATION =
{<class 'sqlglot.expressions.AllowedValuesProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AlgorithmProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <class 'sqlglot.expressions.AutoIncrementProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.AutoRefreshProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BackupProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.BlockCompressionProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CharacterSetProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ChecksumProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.CollateProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.CopyGrantsProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.Cluster'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.ClusteredByProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.DataBlocksizeProperty'>: <Location.POST_NAME: 'POST_NAME'>, <class 'sqlglot.expressions.DataDeletionProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <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.DynamicProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <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.SecureProperty'>: <Location.POST_CREATE: 'POST_CREATE'>, <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.StrictProperty'>: <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.WithSchemaBindingProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>, <class 'sqlglot.expressions.WithSystemVersioningProperty'>: <Location.POST_SCHEMA: 'POST_SCHEMA'>}
WITH_SEPARATED_COMMENTS: Tuple[Type[sqlglot.expressions.Expression], ...] =
(<class 'sqlglot.expressions.Command'>, <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.SetOperation'>, <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.SetOperation'>)
UNWRAPPED_INTERVAL_VALUES: Tuple[Type[sqlglot.expressions.Expression], ...] =
(<class 'sqlglot.expressions.Column'>, <class 'sqlglot.expressions.Literal'>, <class 'sqlglot.expressions.Neg'>, <class 'sqlglot.expressions.Paren'>)
PARAMETERIZABLE_TEXT_TYPES =
{<Type.NCHAR: 'NCHAR'>, <Type.NVARCHAR: 'NVARCHAR'>, <Type.CHAR: 'CHAR'>, <Type.VARCHAR: 'VARCHAR'>}
616 def generate(self, expression: exp.Expression, copy: bool = True) -> str: 617 """ 618 Generates the SQL string corresponding to the given syntax tree. 619 620 Args: 621 expression: The syntax tree. 622 copy: Whether to copy the expression. The generator performs mutations so 623 it is safer to copy. 624 625 Returns: 626 The SQL string corresponding to `expression`. 627 """ 628 if copy: 629 expression = expression.copy() 630 631 expression = self.preprocess(expression) 632 633 self.unsupported_messages = [] 634 sql = self.sql(expression).strip() 635 636 if self.pretty: 637 sql = sql.replace(self.SENTINEL_LINE_BREAK, "\n") 638 639 if self.unsupported_level == ErrorLevel.IGNORE: 640 return sql 641 642 if self.unsupported_level == ErrorLevel.WARN: 643 for msg in self.unsupported_messages: 644 logger.warning(msg) 645 elif self.unsupported_level == ErrorLevel.RAISE and self.unsupported_messages: 646 raise UnsupportedError(concat_messages(self.unsupported_messages, self.max_unsupported)) 647 648 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:
650 def preprocess(self, expression: exp.Expression) -> exp.Expression: 651 """Apply generic preprocessing transformations to a given expression.""" 652 if ( 653 not expression.parent 654 and type(expression) in self.EXPRESSIONS_WITHOUT_NESTED_CTES 655 and any(node.parent is not expression for node in expression.find_all(exp.With)) 656 ): 657 from sqlglot.transforms import move_ctes_to_top_level 658 659 expression = move_ctes_to_top_level(expression) 660 661 if self.ENSURE_BOOLS: 662 from sqlglot.transforms import ensure_bools 663 664 expression = ensure_bools(expression) 665 666 return expression
Apply generic preprocessing transformations to a given expression.
def
maybe_comment( self, sql: str, expression: Optional[sqlglot.expressions.Expression] = None, comments: Optional[List[str]] = None, separated: bool = False) -> str:
684 def maybe_comment( 685 self, 686 sql: str, 687 expression: t.Optional[exp.Expression] = None, 688 comments: t.Optional[t.List[str]] = None, 689 separated: bool = False, 690 ) -> str: 691 comments = ( 692 ((expression and expression.comments) if comments is None else comments) # type: ignore 693 if self.comments 694 else None 695 ) 696 697 if not comments or isinstance(expression, self.EXCLUDE_COMMENTS): 698 return sql 699 700 comments_sql = " ".join( 701 f"/*{self.pad_comment(comment)}*/" for comment in comments if comment 702 ) 703 704 if not comments_sql: 705 return sql 706 707 comments_sql = self._replace_line_breaks(comments_sql) 708 709 if separated or isinstance(expression, self.WITH_SEPARATED_COMMENTS): 710 return ( 711 f"{self.sep()}{comments_sql}{sql}" 712 if not sql or sql[0].isspace() 713 else f"{comments_sql}{self.sep()}{sql}" 714 ) 715 716 return f"{sql} {comments_sql}"
718 def wrap(self, expression: exp.Expression | str) -> str: 719 this_sql = ( 720 self.sql(expression) 721 if isinstance(expression, exp.UNWRAPPED_QUERIES) 722 else self.sql(expression, "this") 723 ) 724 if not this_sql: 725 return "()" 726 727 this_sql = self.indent(this_sql, level=1, pad=0) 728 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:
744 def indent( 745 self, 746 sql: str, 747 level: int = 0, 748 pad: t.Optional[int] = None, 749 skip_first: bool = False, 750 skip_last: bool = False, 751 ) -> str: 752 if not self.pretty or not sql: 753 return sql 754 755 pad = self.pad if pad is None else pad 756 lines = sql.split("\n") 757 758 return "\n".join( 759 ( 760 line 761 if (skip_first and i == 0) or (skip_last and i == len(lines) - 1) 762 else f"{' ' * (level * self._indent + pad)}{line}" 763 ) 764 for i, line in enumerate(lines) 765 )
def
sql( self, expression: Union[str, sqlglot.expressions.Expression, NoneType], key: Optional[str] = None, comment: bool = True) -> str:
767 def sql( 768 self, 769 expression: t.Optional[str | exp.Expression], 770 key: t.Optional[str] = None, 771 comment: bool = True, 772 ) -> str: 773 if not expression: 774 return "" 775 776 if isinstance(expression, str): 777 return expression 778 779 if key: 780 value = expression.args.get(key) 781 if value: 782 return self.sql(value) 783 return "" 784 785 transform = self.TRANSFORMS.get(expression.__class__) 786 787 if callable(transform): 788 sql = transform(self, expression) 789 elif isinstance(expression, exp.Expression): 790 exp_handler_name = f"{expression.key}_sql" 791 792 if hasattr(self, exp_handler_name): 793 sql = getattr(self, exp_handler_name)(expression) 794 elif isinstance(expression, exp.Func): 795 sql = self.function_fallback_sql(expression) 796 elif isinstance(expression, exp.Property): 797 sql = self.property_sql(expression) 798 else: 799 raise ValueError(f"Unsupported expression type {expression.__class__.__name__}") 800 else: 801 raise ValueError(f"Expected an Expression. Received {type(expression)}: {expression}") 802 803 return self.maybe_comment(sql, expression) if self.comments and comment else sql
810 def cache_sql(self, expression: exp.Cache) -> str: 811 lazy = " LAZY" if expression.args.get("lazy") else "" 812 table = self.sql(expression, "this") 813 options = expression.args.get("options") 814 options = f" OPTIONS({self.sql(options[0])} = {self.sql(options[1])})" if options else "" 815 sql = self.sql(expression, "expression") 816 sql = f" AS{self.sep()}{sql}" if sql else "" 817 sql = f"CACHE{lazy} TABLE {table}{options}{sql}" 818 return self.prepend_ctes(expression, sql)
820 def characterset_sql(self, expression: exp.CharacterSet) -> str: 821 if isinstance(expression.parent, exp.Cast): 822 return f"CHAR CHARACTER SET {self.sql(expression, 'this')}" 823 default = "DEFAULT " if expression.args.get("default") else "" 824 return f"{default}CHARACTER SET={self.sql(expression, 'this')}"
838 def column_sql(self, expression: exp.Column) -> str: 839 join_mark = " (+)" if expression.args.get("join_mark") else "" 840 841 if join_mark and not self.dialect.SUPPORTS_COLUMN_JOIN_MARKS: 842 join_mark = "" 843 self.unsupported("Outer join syntax using the (+) operator is not supported.") 844 845 return f"{self.column_parts(expression)}{join_mark}"
853 def columndef_sql(self, expression: exp.ColumnDef, sep: str = " ") -> str: 854 column = self.sql(expression, "this") 855 kind = self.sql(expression, "kind") 856 constraints = self.expressions(expression, key="constraints", sep=" ", flat=True) 857 exists = "IF NOT EXISTS " if expression.args.get("exists") else "" 858 kind = f"{sep}{kind}" if kind else "" 859 constraints = f" {constraints}" if constraints else "" 860 position = self.sql(expression, "position") 861 position = f" {position}" if position else "" 862 863 if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: 864 kind = "" 865 866 return f"{exists}{column}{kind}{constraints}{position}"
def
computedcolumnconstraint_sql(self, expression: sqlglot.expressions.ComputedColumnConstraint) -> str:
873 def computedcolumnconstraint_sql(self, expression: exp.ComputedColumnConstraint) -> str: 874 this = self.sql(expression, "this") 875 if expression.args.get("not_null"): 876 persisted = " PERSISTED NOT NULL" 877 elif expression.args.get("persisted"): 878 persisted = " PERSISTED" 879 else: 880 persisted = "" 881 return f"AS {this}{persisted}"
def
compresscolumnconstraint_sql(self, expression: sqlglot.expressions.CompressColumnConstraint) -> str:
def
generatedasidentitycolumnconstraint_sql( self, expression: sqlglot.expressions.GeneratedAsIdentityColumnConstraint) -> str:
894 def generatedasidentitycolumnconstraint_sql( 895 self, expression: exp.GeneratedAsIdentityColumnConstraint 896 ) -> str: 897 this = "" 898 if expression.this is not None: 899 on_null = " ON NULL" if expression.args.get("on_null") else "" 900 this = " ALWAYS" if expression.this else f" BY DEFAULT{on_null}" 901 902 start = expression.args.get("start") 903 start = f"START WITH {start}" if start else "" 904 increment = expression.args.get("increment") 905 increment = f" INCREMENT BY {increment}" if increment else "" 906 minvalue = expression.args.get("minvalue") 907 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 908 maxvalue = expression.args.get("maxvalue") 909 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 910 cycle = expression.args.get("cycle") 911 cycle_sql = "" 912 913 if cycle is not None: 914 cycle_sql = f"{' NO' if not cycle else ''} CYCLE" 915 cycle_sql = cycle_sql.strip() if not start and not increment else cycle_sql 916 917 sequence_opts = "" 918 if start or increment or cycle_sql: 919 sequence_opts = f"{start}{increment}{minvalue}{maxvalue}{cycle_sql}" 920 sequence_opts = f" ({sequence_opts.strip()})" 921 922 expr = self.sql(expression, "expression") 923 expr = f"({expr})" if expr else "IDENTITY" 924 925 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:
951 def uniquecolumnconstraint_sql(self, expression: exp.UniqueColumnConstraint) -> str: 952 this = self.sql(expression, "this") 953 this = f" {this}" if this else "" 954 index_type = expression.args.get("index_type") 955 index_type = f" USING {index_type}" if index_type else "" 956 on_conflict = self.sql(expression, "on_conflict") 957 on_conflict = f" {on_conflict}" if on_conflict else "" 958 nulls_sql = " NULLS NOT DISTINCT" if expression.args.get("nulls") else "" 959 return f"UNIQUE{nulls_sql}{this}{index_type}{on_conflict}"
964 def create_sql(self, expression: exp.Create) -> str: 965 kind = self.sql(expression, "kind") 966 properties = expression.args.get("properties") 967 properties_locs = self.locate_properties(properties) if properties else defaultdict() 968 969 this = self.createable_sql(expression, properties_locs) 970 971 properties_sql = "" 972 if properties_locs.get(exp.Properties.Location.POST_SCHEMA) or properties_locs.get( 973 exp.Properties.Location.POST_WITH 974 ): 975 properties_sql = self.sql( 976 exp.Properties( 977 expressions=[ 978 *properties_locs[exp.Properties.Location.POST_SCHEMA], 979 *properties_locs[exp.Properties.Location.POST_WITH], 980 ] 981 ) 982 ) 983 984 if properties_locs.get(exp.Properties.Location.POST_SCHEMA): 985 properties_sql = self.sep() + properties_sql 986 elif not self.pretty: 987 # Standalone POST_WITH properties need a leading whitespace in non-pretty mode 988 properties_sql = f" {properties_sql}" 989 990 begin = " BEGIN" if expression.args.get("begin") else "" 991 end = " END" if expression.args.get("end") else "" 992 993 expression_sql = self.sql(expression, "expression") 994 if expression_sql: 995 expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" 996 997 if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): 998 postalias_props_sql = "" 999 if properties_locs.get(exp.Properties.Location.POST_ALIAS): 1000 postalias_props_sql = self.properties( 1001 exp.Properties( 1002 expressions=properties_locs[exp.Properties.Location.POST_ALIAS] 1003 ), 1004 wrapped=False, 1005 ) 1006 postalias_props_sql = f" {postalias_props_sql}" if postalias_props_sql else "" 1007 expression_sql = f" AS{postalias_props_sql}{expression_sql}" 1008 1009 postindex_props_sql = "" 1010 if properties_locs.get(exp.Properties.Location.POST_INDEX): 1011 postindex_props_sql = self.properties( 1012 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_INDEX]), 1013 wrapped=False, 1014 prefix=" ", 1015 ) 1016 1017 indexes = self.expressions(expression, key="indexes", indent=False, sep=" ") 1018 indexes = f" {indexes}" if indexes else "" 1019 index_sql = indexes + postindex_props_sql 1020 1021 replace = " OR REPLACE" if expression.args.get("replace") else "" 1022 unique = " UNIQUE" if expression.args.get("unique") else "" 1023 1024 clustered = expression.args.get("clustered") 1025 if clustered is None: 1026 clustered_sql = "" 1027 elif clustered: 1028 clustered_sql = " CLUSTERED COLUMNSTORE" 1029 else: 1030 clustered_sql = " NONCLUSTERED COLUMNSTORE" 1031 1032 postcreate_props_sql = "" 1033 if properties_locs.get(exp.Properties.Location.POST_CREATE): 1034 postcreate_props_sql = self.properties( 1035 exp.Properties(expressions=properties_locs[exp.Properties.Location.POST_CREATE]), 1036 sep=" ", 1037 prefix=" ", 1038 wrapped=False, 1039 ) 1040 1041 modifiers = "".join((clustered_sql, replace, unique, postcreate_props_sql)) 1042 1043 postexpression_props_sql = "" 1044 if properties_locs.get(exp.Properties.Location.POST_EXPRESSION): 1045 postexpression_props_sql = self.properties( 1046 exp.Properties( 1047 expressions=properties_locs[exp.Properties.Location.POST_EXPRESSION] 1048 ), 1049 sep=" ", 1050 prefix=" ", 1051 wrapped=False, 1052 ) 1053 1054 concurrently = " CONCURRENTLY" if expression.args.get("concurrently") else "" 1055 exists_sql = " IF NOT EXISTS" if expression.args.get("exists") else "" 1056 no_schema_binding = ( 1057 " WITH NO SCHEMA BINDING" if expression.args.get("no_schema_binding") else "" 1058 ) 1059 1060 clone = self.sql(expression, "clone") 1061 clone = f" {clone}" if clone else "" 1062 1063 expression_sql = f"CREATE{modifiers} {kind}{concurrently}{exists_sql} {this}{properties_sql}{expression_sql}{postexpression_props_sql}{index_sql}{no_schema_binding}{clone}" 1064 return self.prepend_ctes(expression, expression_sql)
1066 def sequenceproperties_sql(self, expression: exp.SequenceProperties) -> str: 1067 start = self.sql(expression, "start") 1068 start = f"START WITH {start}" if start else "" 1069 increment = self.sql(expression, "increment") 1070 increment = f" INCREMENT BY {increment}" if increment else "" 1071 minvalue = self.sql(expression, "minvalue") 1072 minvalue = f" MINVALUE {minvalue}" if minvalue else "" 1073 maxvalue = self.sql(expression, "maxvalue") 1074 maxvalue = f" MAXVALUE {maxvalue}" if maxvalue else "" 1075 owned = self.sql(expression, "owned") 1076 owned = f" OWNED BY {owned}" if owned else "" 1077 1078 cache = expression.args.get("cache") 1079 if cache is None: 1080 cache_str = "" 1081 elif cache is True: 1082 cache_str = " CACHE" 1083 else: 1084 cache_str = f" CACHE {cache}" 1085 1086 options = self.expressions(expression, key="options", flat=True, sep=" ") 1087 options = f" {options}" if options else "" 1088 1089 return f"{start}{increment}{minvalue}{maxvalue}{cache_str}{options}{owned}".lstrip()
1091 def clone_sql(self, expression: exp.Clone) -> str: 1092 this = self.sql(expression, "this") 1093 shallow = "SHALLOW " if expression.args.get("shallow") else "" 1094 keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" 1095 return f"{shallow}{keyword} {this}"
1122 def cte_sql(self, expression: exp.CTE) -> str: 1123 alias = self.sql(expression, "alias") 1124 1125 materialized = expression.args.get("materialized") 1126 if materialized is False: 1127 materialized = "NOT MATERIALIZED " 1128 elif materialized: 1129 materialized = "MATERIALIZED " 1130 1131 return f"{alias} AS {materialized or ''}{self.wrap(expression)}"
1133 def tablealias_sql(self, expression: exp.TableAlias) -> str: 1134 alias = self.sql(expression, "this") 1135 columns = self.expressions(expression, key="columns", flat=True) 1136 columns = f"({columns})" if columns else "" 1137 1138 if columns and not self.SUPPORTS_TABLE_ALIAS_COLUMNS: 1139 columns = "" 1140 self.unsupported("Named columns are not supported in table alias.") 1141 1142 if not alias and not self.dialect.UNNEST_COLUMN_ONLY: 1143 alias = self._next_name() 1144 1145 return f"{alias}{columns}"
1165 def unicodestring_sql(self, expression: exp.UnicodeString) -> str: 1166 this = self.sql(expression, "this") 1167 escape = expression.args.get("escape") 1168 1169 if self.dialect.UNICODE_START: 1170 escape_substitute = r"\\\1" 1171 left_quote, right_quote = self.dialect.UNICODE_START, self.dialect.UNICODE_END 1172 else: 1173 escape_substitute = r"\\u\1" 1174 left_quote, right_quote = self.dialect.QUOTE_START, self.dialect.QUOTE_END 1175 1176 if escape: 1177 escape_pattern = re.compile(rf"{escape.name}(\d+)") 1178 escape_sql = f" UESCAPE {self.sql(escape)}" if self.SUPPORTS_UESCAPE else "" 1179 else: 1180 escape_pattern = ESCAPED_UNICODE_RE 1181 escape_sql = "" 1182 1183 if not self.dialect.UNICODE_START or (escape and not self.SUPPORTS_UESCAPE): 1184 this = escape_pattern.sub(escape_substitute, this) 1185 1186 return f"{left_quote}{this}{right_quote}{escape_sql}"
1198 def datatype_sql(self, expression: exp.DataType) -> str: 1199 type_value = expression.this 1200 1201 if type_value == exp.DataType.Type.USERDEFINED and expression.args.get("kind"): 1202 type_sql = self.sql(expression, "kind") 1203 else: 1204 type_sql = ( 1205 self.TYPE_MAPPING.get(type_value, type_value.value) 1206 if isinstance(type_value, exp.DataType.Type) 1207 else type_value 1208 ) 1209 1210 nested = "" 1211 interior = self.expressions(expression, flat=True) 1212 values = "" 1213 1214 if interior: 1215 if expression.args.get("nested"): 1216 nested = f"{self.STRUCT_DELIMITER[0]}{interior}{self.STRUCT_DELIMITER[1]}" 1217 if expression.args.get("values") is not None: 1218 delimiters = ("[", "]") if type_value == exp.DataType.Type.ARRAY else ("(", ")") 1219 values = self.expressions(expression, key="values", flat=True) 1220 values = f"{delimiters[0]}{values}{delimiters[1]}" 1221 elif type_value == exp.DataType.Type.INTERVAL: 1222 nested = f" {interior}" 1223 else: 1224 nested = f"({interior})" 1225 1226 type_sql = f"{type_sql}{nested}{values}" 1227 if self.TZ_TO_WITH_TIME_ZONE and type_value in ( 1228 exp.DataType.Type.TIMETZ, 1229 exp.DataType.Type.TIMESTAMPTZ, 1230 ): 1231 type_sql = f"{type_sql} WITH TIME ZONE" 1232 1233 return type_sql
1235 def directory_sql(self, expression: exp.Directory) -> str: 1236 local = "LOCAL " if expression.args.get("local") else "" 1237 row_format = self.sql(expression, "row_format") 1238 row_format = f" {row_format}" if row_format else "" 1239 return f"{local}DIRECTORY {self.sql(expression, 'this')}{row_format}"
1241 def delete_sql(self, expression: exp.Delete) -> str: 1242 this = self.sql(expression, "this") 1243 this = f" FROM {this}" if this else "" 1244 using = self.sql(expression, "using") 1245 using = f" USING {using}" if using else "" 1246 where = self.sql(expression, "where") 1247 returning = self.sql(expression, "returning") 1248 limit = self.sql(expression, "limit") 1249 tables = self.expressions(expression, key="tables") 1250 tables = f" {tables}" if tables else "" 1251 if self.RETURNING_END: 1252 expression_sql = f"{this}{using}{where}{returning}{limit}" 1253 else: 1254 expression_sql = f"{returning}{this}{using}{where}{limit}" 1255 return self.prepend_ctes(expression, f"DELETE{tables}{expression_sql}")
1257 def drop_sql(self, expression: exp.Drop) -> str: 1258 this = self.sql(expression, "this") 1259 expressions = self.expressions(expression, flat=True) 1260 expressions = f" ({expressions})" if expressions else "" 1261 kind = expression.args["kind"] 1262 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 1263 on_cluster = self.sql(expression, "cluster") 1264 on_cluster = f" {on_cluster}" if on_cluster else "" 1265 temporary = " TEMPORARY" if expression.args.get("temporary") else "" 1266 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 1267 cascade = " CASCADE" if expression.args.get("cascade") else "" 1268 constraints = " CONSTRAINTS" if expression.args.get("constraints") else "" 1269 purge = " PURGE" if expression.args.get("purge") else "" 1270 return f"DROP{temporary}{materialized} {kind}{exists_sql}{this}{on_cluster}{expressions}{cascade}{constraints}{purge}"
1278 def fetch_sql(self, expression: exp.Fetch) -> str: 1279 direction = expression.args.get("direction") 1280 direction = f" {direction}" if direction else "" 1281 count = expression.args.get("count") 1282 count = f" {count}" if count else "" 1283 if expression.args.get("percent"): 1284 count = f"{count} PERCENT" 1285 with_ties_or_only = "WITH TIES" if expression.args.get("with_ties") else "ONLY" 1286 return f"{self.seg('FETCH')}{direction}{count} ROWS {with_ties_or_only}"
1288 def filter_sql(self, expression: exp.Filter) -> str: 1289 if self.AGGREGATE_FILTER_SUPPORTED: 1290 this = self.sql(expression, "this") 1291 where = self.sql(expression, "expression").strip() 1292 return f"{this} FILTER({where})" 1293 1294 agg = expression.this 1295 agg_arg = agg.this 1296 cond = expression.expression.this 1297 agg_arg.replace(exp.If(this=cond.copy(), true=agg_arg.copy())) 1298 return self.sql(agg)
1307 def indexparameters_sql(self, expression: exp.IndexParameters) -> str: 1308 using = self.sql(expression, "using") 1309 using = f" USING {using}" if using else "" 1310 columns = self.expressions(expression, key="columns", flat=True) 1311 columns = f"({columns})" if columns else "" 1312 partition_by = self.expressions(expression, key="partition_by", flat=True) 1313 partition_by = f" PARTITION BY {partition_by}" if partition_by else "" 1314 where = self.sql(expression, "where") 1315 include = self.expressions(expression, key="include", flat=True) 1316 if include: 1317 include = f" INCLUDE ({include})" 1318 with_storage = self.expressions(expression, key="with_storage", flat=True) 1319 with_storage = f" WITH ({with_storage})" if with_storage else "" 1320 tablespace = self.sql(expression, "tablespace") 1321 tablespace = f" USING INDEX TABLESPACE {tablespace}" if tablespace else "" 1322 on = self.sql(expression, "on") 1323 on = f" ON {on}" if on else "" 1324 1325 return f"{using}{columns}{include}{with_storage}{tablespace}{partition_by}{where}{on}"
1327 def index_sql(self, expression: exp.Index) -> str: 1328 unique = "UNIQUE " if expression.args.get("unique") else "" 1329 primary = "PRIMARY " if expression.args.get("primary") else "" 1330 amp = "AMP " if expression.args.get("amp") else "" 1331 name = self.sql(expression, "this") 1332 name = f"{name} " if name else "" 1333 table = self.sql(expression, "table") 1334 table = f"{self.INDEX_ON} {table}" if table else "" 1335 1336 index = "INDEX " if not table else "" 1337 1338 params = self.sql(expression, "params") 1339 return f"{unique}{primary}{amp}{index}{name}{table}{params}"
1341 def identifier_sql(self, expression: exp.Identifier) -> str: 1342 text = expression.name 1343 lower = text.lower() 1344 text = lower if self.normalize and not expression.quoted else text 1345 text = text.replace(self.dialect.IDENTIFIER_END, self._escaped_identifier_end) 1346 if ( 1347 expression.quoted 1348 or self.dialect.can_identify(text, self.identify) 1349 or lower in self.RESERVED_KEYWORDS 1350 or (not self.dialect.IDENTIFIERS_CAN_START_WITH_DIGIT and text[:1].isdigit()) 1351 ): 1352 text = f"{self.dialect.IDENTIFIER_START}{text}{self.dialect.IDENTIFIER_END}" 1353 return text
1368 def inputoutputformat_sql(self, expression: exp.InputOutputFormat) -> str: 1369 input_format = self.sql(expression, "input_format") 1370 input_format = f"INPUTFORMAT {input_format}" if input_format else "" 1371 output_format = self.sql(expression, "output_format") 1372 output_format = f"OUTPUTFORMAT {output_format}" if output_format else "" 1373 return self.sep().join((input_format, output_format))
1382 def properties_sql(self, expression: exp.Properties) -> str: 1383 root_properties = [] 1384 with_properties = [] 1385 1386 for p in expression.expressions: 1387 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1388 if p_loc == exp.Properties.Location.POST_WITH: 1389 with_properties.append(p) 1390 elif p_loc == exp.Properties.Location.POST_SCHEMA: 1391 root_properties.append(p) 1392 1393 root_props = self.root_properties(exp.Properties(expressions=root_properties)) 1394 with_props = self.with_properties(exp.Properties(expressions=with_properties)) 1395 1396 if root_props and with_props and not self.pretty: 1397 with_props = " " + with_props 1398 1399 return root_props + with_props
def
properties( self, properties: sqlglot.expressions.Properties, prefix: str = '', sep: str = ', ', suffix: str = '', wrapped: bool = True) -> str:
1406 def properties( 1407 self, 1408 properties: exp.Properties, 1409 prefix: str = "", 1410 sep: str = ", ", 1411 suffix: str = "", 1412 wrapped: bool = True, 1413 ) -> str: 1414 if properties.expressions: 1415 expressions = self.expressions(properties, sep=sep, indent=False) 1416 if expressions: 1417 expressions = self.wrap(expressions) if wrapped else expressions 1418 return f"{prefix}{' ' if prefix.strip() else ''}{expressions}{suffix}" 1419 return ""
1424 def locate_properties(self, properties: exp.Properties) -> t.DefaultDict: 1425 properties_locs = defaultdict(list) 1426 for p in properties.expressions: 1427 p_loc = self.PROPERTIES_LOCATION[p.__class__] 1428 if p_loc != exp.Properties.Location.UNSUPPORTED: 1429 properties_locs[p_loc].append(p) 1430 else: 1431 self.unsupported(f"Unsupported property {p.key}") 1432 1433 return properties_locs
def
property_name( self, expression: sqlglot.expressions.Property, string_key: bool = False) -> str:
1440 def property_sql(self, expression: exp.Property) -> str: 1441 property_cls = expression.__class__ 1442 if property_cls == exp.Property: 1443 return f"{self.property_name(expression)}={self.sql(expression, 'value')}" 1444 1445 property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls) 1446 if not property_name: 1447 self.unsupported(f"Unsupported property {expression.key}") 1448 1449 return f"{property_name}={self.sql(expression, 'this')}"
1451 def likeproperty_sql(self, expression: exp.LikeProperty) -> str: 1452 if self.SUPPORTS_CREATE_TABLE_LIKE: 1453 options = " ".join(f"{e.name} {self.sql(e, 'value')}" for e in expression.expressions) 1454 options = f" {options}" if options else "" 1455 1456 like = f"LIKE {self.sql(expression, 'this')}{options}" 1457 if self.LIKE_PROPERTY_INSIDE_SCHEMA and not isinstance(expression.parent, exp.Schema): 1458 like = f"({like})" 1459 1460 return like 1461 1462 if expression.expressions: 1463 self.unsupported("Transpilation of LIKE property options is unsupported") 1464 1465 select = exp.select("*").from_(expression.this).limit(0) 1466 return f"AS {self.sql(select)}"
1473 def journalproperty_sql(self, expression: exp.JournalProperty) -> str: 1474 no = "NO " if expression.args.get("no") else "" 1475 local = expression.args.get("local") 1476 local = f"{local} " if local else "" 1477 dual = "DUAL " if expression.args.get("dual") else "" 1478 before = "BEFORE " if expression.args.get("before") else "" 1479 after = "AFTER " if expression.args.get("after") else "" 1480 return f"{no}{local}{dual}{before}{after}JOURNAL"
def
mergeblockratioproperty_sql(self, expression: sqlglot.expressions.MergeBlockRatioProperty) -> str:
1496 def mergeblockratioproperty_sql(self, expression: exp.MergeBlockRatioProperty) -> str: 1497 if expression.args.get("no"): 1498 return "NO MERGEBLOCKRATIO" 1499 if expression.args.get("default"): 1500 return "DEFAULT MERGEBLOCKRATIO" 1501 1502 percent = " PERCENT" if expression.args.get("percent") else "" 1503 return f"MERGEBLOCKRATIO={self.sql(expression, 'this')}{percent}"
1505 def datablocksizeproperty_sql(self, expression: exp.DataBlocksizeProperty) -> str: 1506 default = expression.args.get("default") 1507 minimum = expression.args.get("minimum") 1508 maximum = expression.args.get("maximum") 1509 if default or minimum or maximum: 1510 if default: 1511 prop = "DEFAULT" 1512 elif minimum: 1513 prop = "MINIMUM" 1514 else: 1515 prop = "MAXIMUM" 1516 return f"{prop} DATABLOCKSIZE" 1517 units = expression.args.get("units") 1518 units = f" {units}" if units else "" 1519 return f"DATABLOCKSIZE={self.sql(expression, 'size')}{units}"
def
blockcompressionproperty_sql(self, expression: sqlglot.expressions.BlockCompressionProperty) -> str:
1521 def blockcompressionproperty_sql(self, expression: exp.BlockCompressionProperty) -> str: 1522 autotemp = expression.args.get("autotemp") 1523 always = expression.args.get("always") 1524 default = expression.args.get("default") 1525 manual = expression.args.get("manual") 1526 never = expression.args.get("never") 1527 1528 if autotemp is not None: 1529 prop = f"AUTOTEMP({self.expressions(autotemp)})" 1530 elif always: 1531 prop = "ALWAYS" 1532 elif default: 1533 prop = "DEFAULT" 1534 elif manual: 1535 prop = "MANUAL" 1536 elif never: 1537 prop = "NEVER" 1538 return f"BLOCKCOMPRESSION={prop}"
def
isolatedloadingproperty_sql(self, expression: sqlglot.expressions.IsolatedLoadingProperty) -> str:
1540 def isolatedloadingproperty_sql(self, expression: exp.IsolatedLoadingProperty) -> str: 1541 no = expression.args.get("no") 1542 no = " NO" if no else "" 1543 concurrent = expression.args.get("concurrent") 1544 concurrent = " CONCURRENT" if concurrent else "" 1545 target = self.sql(expression, "target") 1546 target = f" {target}" if target else "" 1547 return f"WITH{no}{concurrent} ISOLATED LOADING{target}"
1549 def partitionboundspec_sql(self, expression: exp.PartitionBoundSpec) -> str: 1550 if isinstance(expression.this, list): 1551 return f"IN ({self.expressions(expression, key='this', flat=True)})" 1552 if expression.this: 1553 modulus = self.sql(expression, "this") 1554 remainder = self.sql(expression, "expression") 1555 return f"WITH (MODULUS {modulus}, REMAINDER {remainder})" 1556 1557 from_expressions = self.expressions(expression, key="from_expressions", flat=True) 1558 to_expressions = self.expressions(expression, key="to_expressions", flat=True) 1559 return f"FROM ({from_expressions}) TO ({to_expressions})"
1561 def partitionedofproperty_sql(self, expression: exp.PartitionedOfProperty) -> str: 1562 this = self.sql(expression, "this") 1563 1564 for_values_or_default = expression.expression 1565 if isinstance(for_values_or_default, exp.PartitionBoundSpec): 1566 for_values_or_default = f" FOR VALUES {self.sql(for_values_or_default)}" 1567 else: 1568 for_values_or_default = " DEFAULT" 1569 1570 return f"PARTITION OF {this}{for_values_or_default}"
1572 def lockingproperty_sql(self, expression: exp.LockingProperty) -> str: 1573 kind = expression.args.get("kind") 1574 this = f" {self.sql(expression, 'this')}" if expression.this else "" 1575 for_or_in = expression.args.get("for_or_in") 1576 for_or_in = f" {for_or_in}" if for_or_in else "" 1577 lock_type = expression.args.get("lock_type") 1578 override = " OVERRIDE" if expression.args.get("override") else "" 1579 return f"LOCKING {kind}{this}{for_or_in} {lock_type}{override}"
1581 def withdataproperty_sql(self, expression: exp.WithDataProperty) -> str: 1582 data_sql = f"WITH {'NO ' if expression.args.get('no') else ''}DATA" 1583 statistics = expression.args.get("statistics") 1584 statistics_sql = "" 1585 if statistics is not None: 1586 statistics_sql = f" AND {'NO ' if not statistics else ''}STATISTICS" 1587 return f"{data_sql}{statistics_sql}"
def
withsystemversioningproperty_sql( self, expression: sqlglot.expressions.WithSystemVersioningProperty) -> str:
1589 def withsystemversioningproperty_sql(self, expression: exp.WithSystemVersioningProperty) -> str: 1590 this = self.sql(expression, "this") 1591 this = f"HISTORY_TABLE={this}" if this else "" 1592 data_consistency: t.Optional[str] = self.sql(expression, "data_consistency") 1593 data_consistency = ( 1594 f"DATA_CONSISTENCY_CHECK={data_consistency}" if data_consistency else None 1595 ) 1596 retention_period: t.Optional[str] = self.sql(expression, "retention_period") 1597 retention_period = ( 1598 f"HISTORY_RETENTION_PERIOD={retention_period}" if retention_period else None 1599 ) 1600 1601 if this: 1602 on_sql = self.func("ON", this, data_consistency, retention_period) 1603 else: 1604 on_sql = "ON" if expression.args.get("on") else "OFF" 1605 1606 sql = f"SYSTEM_VERSIONING={on_sql}" 1607 1608 return f"WITH({sql})" if expression.args.get("with") else sql
1610 def insert_sql(self, expression: exp.Insert) -> str: 1611 hint = self.sql(expression, "hint") 1612 overwrite = expression.args.get("overwrite") 1613 1614 if isinstance(expression.this, exp.Directory): 1615 this = " OVERWRITE" if overwrite else " INTO" 1616 else: 1617 this = self.INSERT_OVERWRITE if overwrite else " INTO" 1618 1619 stored = self.sql(expression, "stored") 1620 stored = f" {stored}" if stored else "" 1621 alternative = expression.args.get("alternative") 1622 alternative = f" OR {alternative}" if alternative else "" 1623 ignore = " IGNORE" if expression.args.get("ignore") else "" 1624 is_function = expression.args.get("is_function") 1625 if is_function: 1626 this = f"{this} FUNCTION" 1627 this = f"{this} {self.sql(expression, 'this')}" 1628 1629 exists = " IF EXISTS" if expression.args.get("exists") else "" 1630 where = self.sql(expression, "where") 1631 where = f"{self.sep()}REPLACE WHERE {where}" if where else "" 1632 expression_sql = f"{self.sep()}{self.sql(expression, 'expression')}" 1633 on_conflict = self.sql(expression, "conflict") 1634 on_conflict = f" {on_conflict}" if on_conflict else "" 1635 by_name = " BY NAME" if expression.args.get("by_name") else "" 1636 returning = self.sql(expression, "returning") 1637 1638 if self.RETURNING_END: 1639 expression_sql = f"{expression_sql}{on_conflict}{returning}" 1640 else: 1641 expression_sql = f"{returning}{expression_sql}{on_conflict}" 1642 1643 sql = f"INSERT{hint}{alternative}{ignore}{this}{stored}{by_name}{exists}{where}{expression_sql}" 1644 return self.prepend_ctes(expression, sql)
1668 def onconflict_sql(self, expression: exp.OnConflict) -> str: 1669 conflict = "ON DUPLICATE KEY" if expression.args.get("duplicate") else "ON CONFLICT" 1670 1671 constraint = self.sql(expression, "constraint") 1672 constraint = f" ON CONSTRAINT {constraint}" if constraint else "" 1673 1674 conflict_keys = self.expressions(expression, key="conflict_keys", flat=True) 1675 conflict_keys = f"({conflict_keys}) " if conflict_keys else " " 1676 action = self.sql(expression, "action") 1677 1678 expressions = self.expressions(expression, flat=True) 1679 if expressions: 1680 set_keyword = "SET " if self.DUPLICATE_KEY_UPDATE_WITH_SET else "" 1681 expressions = f" {set_keyword}{expressions}" 1682 1683 return f"{conflict}{constraint}{conflict_keys}{action}{expressions}"
def
rowformatdelimitedproperty_sql(self, expression: sqlglot.expressions.RowFormatDelimitedProperty) -> str:
1688 def rowformatdelimitedproperty_sql(self, expression: exp.RowFormatDelimitedProperty) -> str: 1689 fields = self.sql(expression, "fields") 1690 fields = f" FIELDS TERMINATED BY {fields}" if fields else "" 1691 escaped = self.sql(expression, "escaped") 1692 escaped = f" ESCAPED BY {escaped}" if escaped else "" 1693 items = self.sql(expression, "collection_items") 1694 items = f" COLLECTION ITEMS TERMINATED BY {items}" if items else "" 1695 keys = self.sql(expression, "map_keys") 1696 keys = f" MAP KEYS TERMINATED BY {keys}" if keys else "" 1697 lines = self.sql(expression, "lines") 1698 lines = f" LINES TERMINATED BY {lines}" if lines else "" 1699 null = self.sql(expression, "null") 1700 null = f" NULL DEFINED AS {null}" if null else "" 1701 return f"ROW FORMAT DELIMITED{fields}{escaped}{items}{keys}{lines}{null}"
1729 def table_sql(self, expression: exp.Table, sep: str = " AS ") -> str: 1730 table = self.table_parts(expression) 1731 only = "ONLY " if expression.args.get("only") else "" 1732 partition = self.sql(expression, "partition") 1733 partition = f" {partition}" if partition else "" 1734 version = self.sql(expression, "version") 1735 version = f" {version}" if version else "" 1736 alias = self.sql(expression, "alias") 1737 alias = f"{sep}{alias}" if alias else "" 1738 hints = self.expressions(expression, key="hints", sep=" ") 1739 hints = f" {hints}" if hints and self.TABLE_HINTS else "" 1740 pivots = self.expressions(expression, key="pivots", sep="", flat=True) 1741 joins = self.indent( 1742 self.expressions(expression, key="joins", sep="", flat=True), skip_first=True 1743 ) 1744 laterals = self.expressions(expression, key="laterals", sep="") 1745 1746 file_format = self.sql(expression, "format") 1747 if file_format: 1748 pattern = self.sql(expression, "pattern") 1749 pattern = f", PATTERN => {pattern}" if pattern else "" 1750 file_format = f" (FILE_FORMAT => {file_format}{pattern})" 1751 1752 ordinality = expression.args.get("ordinality") or "" 1753 if ordinality: 1754 ordinality = f" WITH ORDINALITY{alias}" 1755 alias = "" 1756 1757 when = self.sql(expression, "when") 1758 if when: 1759 table = f"{table} {when}" 1760 1761 changes = self.sql(expression, "changes") 1762 changes = f" {changes}" if changes else "" 1763 1764 rows_from = self.expressions(expression, key="rows_from") 1765 if rows_from: 1766 table = f"ROWS FROM {self.wrap(rows_from)}" 1767 1768 return f"{only}{table}{changes}{partition}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}{ordinality}"
def
tablesample_sql( self, expression: sqlglot.expressions.TableSample, sep: str = ' AS ', tablesample_keyword: Optional[str] = None) -> str:
1770 def tablesample_sql( 1771 self, 1772 expression: exp.TableSample, 1773 sep: str = " AS ", 1774 tablesample_keyword: t.Optional[str] = None, 1775 ) -> str: 1776 if self.dialect.ALIAS_POST_TABLESAMPLE and expression.this and expression.this.alias: 1777 table = expression.this.copy() 1778 table.set("alias", None) 1779 this = self.sql(table) 1780 alias = f"{sep}{self.sql(expression.this, 'alias')}" 1781 else: 1782 this = self.sql(expression, "this") 1783 alias = "" 1784 1785 method = self.sql(expression, "method") 1786 method = f"{method} " if method and self.TABLESAMPLE_WITH_METHOD else "" 1787 numerator = self.sql(expression, "bucket_numerator") 1788 denominator = self.sql(expression, "bucket_denominator") 1789 field = self.sql(expression, "bucket_field") 1790 field = f" ON {field}" if field else "" 1791 bucket = f"BUCKET {numerator} OUT OF {denominator}{field}" if numerator else "" 1792 seed = self.sql(expression, "seed") 1793 seed = f" {self.TABLESAMPLE_SEED_KEYWORD} ({seed})" if seed else "" 1794 1795 size = self.sql(expression, "size") 1796 if size and self.TABLESAMPLE_SIZE_IS_ROWS: 1797 size = f"{size} ROWS" 1798 1799 percent = self.sql(expression, "percent") 1800 if percent and not self.dialect.TABLESAMPLE_SIZE_IS_PERCENT: 1801 percent = f"{percent} PERCENT" 1802 1803 expr = f"{bucket}{percent}{size}" 1804 if self.TABLESAMPLE_REQUIRES_PARENS: 1805 expr = f"({expr})" 1806 1807 return ( 1808 f"{this} {tablesample_keyword or self.TABLESAMPLE_KEYWORDS} {method}{expr}{seed}{alias}" 1809 )
1811 def pivot_sql(self, expression: exp.Pivot) -> str: 1812 expressions = self.expressions(expression, flat=True) 1813 1814 if expression.this: 1815 this = self.sql(expression, "this") 1816 if not expressions: 1817 return f"UNPIVOT {this}" 1818 1819 on = f"{self.seg('ON')} {expressions}" 1820 using = self.expressions(expression, key="using", flat=True) 1821 using = f"{self.seg('USING')} {using}" if using else "" 1822 group = self.sql(expression, "group") 1823 return f"PIVOT {this}{on}{using}{group}" 1824 1825 alias = self.sql(expression, "alias") 1826 alias = f" AS {alias}" if alias else "" 1827 direction = self.seg("UNPIVOT" if expression.unpivot else "PIVOT") 1828 field = self.sql(expression, "field") 1829 include_nulls = expression.args.get("include_nulls") 1830 if include_nulls is not None: 1831 nulls = " INCLUDE NULLS " if include_nulls else " EXCLUDE NULLS " 1832 else: 1833 nulls = "" 1834 return f"{direction}{nulls}({expressions} FOR {field}){alias}"
1845 def update_sql(self, expression: exp.Update) -> str: 1846 this = self.sql(expression, "this") 1847 set_sql = self.expressions(expression, flat=True) 1848 from_sql = self.sql(expression, "from") 1849 where_sql = self.sql(expression, "where") 1850 returning = self.sql(expression, "returning") 1851 order = self.sql(expression, "order") 1852 limit = self.sql(expression, "limit") 1853 if self.RETURNING_END: 1854 expression_sql = f"{from_sql}{where_sql}{returning}" 1855 else: 1856 expression_sql = f"{returning}{from_sql}{where_sql}" 1857 sql = f"UPDATE {this} SET {set_sql}{expression_sql}{order}{limit}" 1858 return self.prepend_ctes(expression, sql)
1860 def values_sql(self, expression: exp.Values, values_as_table: bool = True) -> str: 1861 values_as_table = values_as_table and self.VALUES_AS_TABLE 1862 1863 # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example 1864 if values_as_table or not expression.find_ancestor(exp.From, exp.Join): 1865 args = self.expressions(expression) 1866 alias = self.sql(expression, "alias") 1867 values = f"VALUES{self.seg('')}{args}" 1868 values = ( 1869 f"({values})" 1870 if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From)) 1871 else values 1872 ) 1873 return f"{values} AS {alias}" if alias else values 1874 1875 # Converts `VALUES...` expression into a series of select unions. 1876 alias_node = expression.args.get("alias") 1877 column_names = alias_node and alias_node.columns 1878 1879 selects: t.List[exp.Query] = [] 1880 1881 for i, tup in enumerate(expression.expressions): 1882 row = tup.expressions 1883 1884 if i == 0 and column_names: 1885 row = [ 1886 exp.alias_(value, column_name) for value, column_name in zip(row, column_names) 1887 ] 1888 1889 selects.append(exp.Select(expressions=row)) 1890 1891 if self.pretty: 1892 # This may result in poor performance for large-cardinality `VALUES` tables, due to 1893 # the deep nesting of the resulting exp.Unions. If this is a problem, either increase 1894 # `sys.setrecursionlimit` to avoid RecursionErrors, or don't set `pretty`. 1895 query = reduce(lambda x, y: exp.union(x, y, distinct=False, copy=False), selects) 1896 return self.subquery_sql(query.subquery(alias_node and alias_node.this, copy=False)) 1897 1898 alias = f" AS {self.sql(alias_node, 'this')}" if alias_node else "" 1899 unions = " UNION ALL ".join(self.sql(select) for select in selects) 1900 return f"({unions}){alias}"
1913 def group_sql(self, expression: exp.Group) -> str: 1914 group_by_all = expression.args.get("all") 1915 if group_by_all is True: 1916 modifier = " ALL" 1917 elif group_by_all is False: 1918 modifier = " DISTINCT" 1919 else: 1920 modifier = "" 1921 1922 group_by = self.op_expressions(f"GROUP BY{modifier}", expression) 1923 1924 grouping_sets = self.expressions(expression, key="grouping_sets", indent=False) 1925 grouping_sets = ( 1926 f"{self.seg('GROUPING SETS')} {self.wrap(grouping_sets)}" if grouping_sets else "" 1927 ) 1928 1929 cube = expression.args.get("cube", []) 1930 if seq_get(cube, 0) is True: 1931 return f"{group_by}{self.seg('WITH CUBE')}" 1932 else: 1933 cube_sql = self.expressions(expression, key="cube", indent=False) 1934 cube_sql = f"{self.seg('CUBE')} {self.wrap(cube_sql)}" if cube_sql else "" 1935 1936 rollup = expression.args.get("rollup", []) 1937 if seq_get(rollup, 0) is True: 1938 return f"{group_by}{self.seg('WITH ROLLUP')}" 1939 else: 1940 rollup_sql = self.expressions(expression, key="rollup", indent=False) 1941 rollup_sql = f"{self.seg('ROLLUP')} {self.wrap(rollup_sql)}" if rollup_sql else "" 1942 1943 groupings = csv( 1944 grouping_sets, 1945 cube_sql, 1946 rollup_sql, 1947 self.seg("WITH TOTALS") if expression.args.get("totals") else "", 1948 sep=self.GROUPINGS_SEP, 1949 ) 1950 1951 if expression.args.get("expressions") and groupings: 1952 group_by = f"{group_by}{self.GROUPINGS_SEP}" 1953 1954 return f"{group_by}{groupings}"
1960 def connect_sql(self, expression: exp.Connect) -> str: 1961 start = self.sql(expression, "start") 1962 start = self.seg(f"START WITH {start}") if start else "" 1963 nocycle = " NOCYCLE" if expression.args.get("nocycle") else "" 1964 connect = self.sql(expression, "connect") 1965 connect = self.seg(f"CONNECT BY{nocycle} {connect}") 1966 return start + connect
1971 def join_sql(self, expression: exp.Join) -> str: 1972 if not self.SEMI_ANTI_JOIN_WITH_SIDE and expression.kind in ("SEMI", "ANTI"): 1973 side = None 1974 else: 1975 side = expression.side 1976 1977 op_sql = " ".join( 1978 op 1979 for op in ( 1980 expression.method, 1981 "GLOBAL" if expression.args.get("global") else None, 1982 side, 1983 expression.kind, 1984 expression.hint if self.JOIN_HINTS else None, 1985 ) 1986 if op 1987 ) 1988 match_cond = self.sql(expression, "match_condition") 1989 match_cond = f" MATCH_CONDITION ({match_cond})" if match_cond else "" 1990 on_sql = self.sql(expression, "on") 1991 using = expression.args.get("using") 1992 1993 if not on_sql and using: 1994 on_sql = csv(*(self.sql(column) for column in using)) 1995 1996 this = expression.this 1997 this_sql = self.sql(this) 1998 1999 if on_sql: 2000 on_sql = self.indent(on_sql, skip_first=True) 2001 space = self.seg(" " * self.pad) if self.pretty else " " 2002 if using: 2003 on_sql = f"{space}USING ({on_sql})" 2004 else: 2005 on_sql = f"{space}ON {on_sql}" 2006 elif not op_sql: 2007 if isinstance(this, exp.Lateral) and this.args.get("cross_apply") is not None: 2008 return f" {this_sql}" 2009 2010 return f", {this_sql}" 2011 2012 if op_sql != "STRAIGHT_JOIN": 2013 op_sql = f"{op_sql} JOIN" if op_sql else "JOIN" 2014 2015 return f"{self.seg(op_sql)} {this_sql}{match_cond}{on_sql}"
2022 def lateral_op(self, expression: exp.Lateral) -> str: 2023 cross_apply = expression.args.get("cross_apply") 2024 2025 # https://www.mssqltips.com/sqlservertip/1958/sql-server-cross-apply-and-outer-apply/ 2026 if cross_apply is True: 2027 op = "INNER JOIN " 2028 elif cross_apply is False: 2029 op = "LEFT JOIN " 2030 else: 2031 op = "" 2032 2033 return f"{op}LATERAL"
2035 def lateral_sql(self, expression: exp.Lateral) -> str: 2036 this = self.sql(expression, "this") 2037 2038 if expression.args.get("view"): 2039 alias = expression.args["alias"] 2040 columns = self.expressions(alias, key="columns", flat=True) 2041 table = f" {alias.name}" if alias.name else "" 2042 columns = f" AS {columns}" if columns else "" 2043 op_sql = self.seg(f"LATERAL VIEW{' OUTER' if expression.args.get('outer') else ''}") 2044 return f"{op_sql}{self.sep()}{this}{table}{columns}" 2045 2046 alias = self.sql(expression, "alias") 2047 alias = f" AS {alias}" if alias else "" 2048 return f"{self.lateral_op(expression)} {this}{alias}"
2050 def limit_sql(self, expression: exp.Limit, top: bool = False) -> str: 2051 this = self.sql(expression, "this") 2052 2053 args = [ 2054 self._simplify_unless_literal(e) if self.LIMIT_ONLY_LITERALS else e 2055 for e in (expression.args.get(k) for k in ("offset", "expression")) 2056 if e 2057 ] 2058 2059 args_sql = ", ".join(self.sql(e) for e in args) 2060 args_sql = f"({args_sql})" if top and any(not e.is_number for e in args) else args_sql 2061 expressions = self.expressions(expression, flat=True) 2062 expressions = f" BY {expressions}" if expressions else "" 2063 2064 return f"{this}{self.seg('TOP' if top else 'LIMIT')} {args_sql}{expressions}"
2066 def offset_sql(self, expression: exp.Offset) -> str: 2067 this = self.sql(expression, "this") 2068 value = expression.expression 2069 value = self._simplify_unless_literal(value) if self.LIMIT_ONLY_LITERALS else value 2070 expressions = self.expressions(expression, flat=True) 2071 expressions = f" BY {expressions}" if expressions else "" 2072 return f"{this}{self.seg('OFFSET')} {self.sql(value)}{expressions}"
2074 def setitem_sql(self, expression: exp.SetItem) -> str: 2075 kind = self.sql(expression, "kind") 2076 kind = f"{kind} " if kind else "" 2077 this = self.sql(expression, "this") 2078 expressions = self.expressions(expression) 2079 collate = self.sql(expression, "collate") 2080 collate = f" COLLATE {collate}" if collate else "" 2081 global_ = "GLOBAL " if expression.args.get("global") else "" 2082 return f"{global_}{kind}{this}{expressions}{collate}"
2084 def set_sql(self, expression: exp.Set) -> str: 2085 expressions = ( 2086 f" {self.expressions(expression, flat=True)}" if expression.expressions else "" 2087 ) 2088 tag = " TAG" if expression.args.get("tag") else "" 2089 return f"{'UNSET' if expression.args.get('unset') else 'SET'}{tag}{expressions}"
2094 def lock_sql(self, expression: exp.Lock) -> str: 2095 if not self.LOCKING_READS_SUPPORTED: 2096 self.unsupported("Locking reads using 'FOR UPDATE/SHARE' are not supported") 2097 return "" 2098 2099 lock_type = "FOR UPDATE" if expression.args["update"] else "FOR SHARE" 2100 expressions = self.expressions(expression, flat=True) 2101 expressions = f" OF {expressions}" if expressions else "" 2102 wait = expression.args.get("wait") 2103 2104 if wait is not None: 2105 if isinstance(wait, exp.Literal): 2106 wait = f" WAIT {self.sql(wait)}" 2107 else: 2108 wait = " NOWAIT" if wait else " SKIP LOCKED" 2109 2110 return f"{lock_type}{expressions}{wait or ''}"
def
escape_str(self, text: str, escape_backslash: bool = True) -> str:
2118 def escape_str(self, text: str, escape_backslash: bool = True) -> str: 2119 if self.dialect.ESCAPED_SEQUENCES: 2120 to_escaped = self.dialect.ESCAPED_SEQUENCES 2121 text = "".join( 2122 to_escaped.get(ch, ch) if escape_backslash or ch != "\\" else ch for ch in text 2123 ) 2124 2125 return self._replace_line_breaks(text).replace( 2126 self.dialect.QUOTE_END, self._escaped_quote_end 2127 )
2129 def loaddata_sql(self, expression: exp.LoadData) -> str: 2130 local = " LOCAL" if expression.args.get("local") else "" 2131 inpath = f" INPATH {self.sql(expression, 'inpath')}" 2132 overwrite = " OVERWRITE" if expression.args.get("overwrite") else "" 2133 this = f" INTO TABLE {self.sql(expression, 'this')}" 2134 partition = self.sql(expression, "partition") 2135 partition = f" {partition}" if partition else "" 2136 input_format = self.sql(expression, "input_format") 2137 input_format = f" INPUTFORMAT {input_format}" if input_format else "" 2138 serde = self.sql(expression, "serde") 2139 serde = f" SERDE {serde}" if serde else "" 2140 return f"LOAD DATA{local}{inpath}{overwrite}{this}{partition}{input_format}{serde}"
2148 def order_sql(self, expression: exp.Order, flat: bool = False) -> str: 2149 this = self.sql(expression, "this") 2150 this = f"{this} " if this else this 2151 siblings = "SIBLINGS " if expression.args.get("siblings") else "" 2152 order = self.op_expressions(f"{this}ORDER {siblings}BY", expression, flat=this or flat) # type: ignore 2153 interpolated_values = [ 2154 f"{self.sql(named_expression, 'alias')} AS {self.sql(named_expression, 'this')}" 2155 for named_expression in expression.args.get("interpolate") or [] 2156 ] 2157 interpolate = ( 2158 f" INTERPOLATE ({', '.join(interpolated_values)})" if interpolated_values else "" 2159 ) 2160 return f"{order}{interpolate}"
2162 def withfill_sql(self, expression: exp.WithFill) -> str: 2163 from_sql = self.sql(expression, "from") 2164 from_sql = f" FROM {from_sql}" if from_sql else "" 2165 to_sql = self.sql(expression, "to") 2166 to_sql = f" TO {to_sql}" if to_sql else "" 2167 step_sql = self.sql(expression, "step") 2168 step_sql = f" STEP {step_sql}" if step_sql else "" 2169 return f"WITH FILL{from_sql}{to_sql}{step_sql}"
2180 def ordered_sql(self, expression: exp.Ordered) -> str: 2181 desc = expression.args.get("desc") 2182 asc = not desc 2183 2184 nulls_first = expression.args.get("nulls_first") 2185 nulls_last = not nulls_first 2186 nulls_are_large = self.dialect.NULL_ORDERING == "nulls_are_large" 2187 nulls_are_small = self.dialect.NULL_ORDERING == "nulls_are_small" 2188 nulls_are_last = self.dialect.NULL_ORDERING == "nulls_are_last" 2189 2190 this = self.sql(expression, "this") 2191 2192 sort_order = " DESC" if desc else (" ASC" if desc is False else "") 2193 nulls_sort_change = "" 2194 if nulls_first and ( 2195 (asc and nulls_are_large) or (desc and nulls_are_small) or nulls_are_last 2196 ): 2197 nulls_sort_change = " NULLS FIRST" 2198 elif ( 2199 nulls_last 2200 and ((asc and nulls_are_small) or (desc and nulls_are_large)) 2201 and not nulls_are_last 2202 ): 2203 nulls_sort_change = " NULLS LAST" 2204 2205 # If the NULLS FIRST/LAST clause is unsupported, we add another sort key to simulate it 2206 if nulls_sort_change and not self.NULL_ORDERING_SUPPORTED: 2207 window = expression.find_ancestor(exp.Window, exp.Select) 2208 if isinstance(window, exp.Window) and window.args.get("spec"): 2209 self.unsupported( 2210 f"'{nulls_sort_change.strip()}' translation not supported in window functions" 2211 ) 2212 nulls_sort_change = "" 2213 elif self.NULL_ORDERING_SUPPORTED is None: 2214 if expression.this.is_int: 2215 self.unsupported( 2216 f"'{nulls_sort_change.strip()}' translation not supported with positional ordering" 2217 ) 2218 elif not isinstance(expression.this, exp.Rand): 2219 null_sort_order = " DESC" if nulls_sort_change == " NULLS FIRST" else "" 2220 this = f"CASE WHEN {this} IS NULL THEN 1 ELSE 0 END{null_sort_order}, {this}" 2221 nulls_sort_change = "" 2222 2223 with_fill = self.sql(expression, "with_fill") 2224 with_fill = f" {with_fill}" if with_fill else "" 2225 2226 return f"{this}{sort_order}{nulls_sort_change}{with_fill}"
2236 def matchrecognize_sql(self, expression: exp.MatchRecognize) -> str: 2237 partition = self.partition_by_sql(expression) 2238 order = self.sql(expression, "order") 2239 measures = self.expressions(expression, key="measures") 2240 measures = self.seg(f"MEASURES{self.seg(measures)}") if measures else "" 2241 rows = self.sql(expression, "rows") 2242 rows = self.seg(rows) if rows else "" 2243 after = self.sql(expression, "after") 2244 after = self.seg(after) if after else "" 2245 pattern = self.sql(expression, "pattern") 2246 pattern = self.seg(f"PATTERN ({pattern})") if pattern else "" 2247 definition_sqls = [ 2248 f"{self.sql(definition, 'alias')} AS {self.sql(definition, 'this')}" 2249 for definition in expression.args.get("define", []) 2250 ] 2251 definitions = self.expressions(sqls=definition_sqls) 2252 define = self.seg(f"DEFINE{self.seg(definitions)}") if definitions else "" 2253 body = "".join( 2254 ( 2255 partition, 2256 order, 2257 measures, 2258 rows, 2259 after, 2260 pattern, 2261 define, 2262 ) 2263 ) 2264 alias = self.sql(expression, "alias") 2265 alias = f" {alias}" if alias else "" 2266 return f"{self.seg('MATCH_RECOGNIZE')} {self.wrap(body)}{alias}"
2268 def query_modifiers(self, expression: exp.Expression, *sqls: str) -> str: 2269 limit = expression.args.get("limit") 2270 2271 if self.LIMIT_FETCH == "LIMIT" and isinstance(limit, exp.Fetch): 2272 limit = exp.Limit(expression=exp.maybe_copy(limit.args.get("count"))) 2273 elif self.LIMIT_FETCH == "FETCH" and isinstance(limit, exp.Limit): 2274 limit = exp.Fetch(direction="FIRST", count=exp.maybe_copy(limit.expression)) 2275 2276 return csv( 2277 *sqls, 2278 *[self.sql(join) for join in expression.args.get("joins") or []], 2279 self.sql(expression, "connect"), 2280 self.sql(expression, "match"), 2281 *[self.sql(lateral) for lateral in expression.args.get("laterals") or []], 2282 self.sql(expression, "prewhere"), 2283 self.sql(expression, "where"), 2284 self.sql(expression, "group"), 2285 self.sql(expression, "having"), 2286 *[gen(self, expression) for gen in self.AFTER_HAVING_MODIFIER_TRANSFORMS.values()], 2287 self.sql(expression, "order"), 2288 *self.offset_limit_modifiers(expression, isinstance(limit, exp.Fetch), limit), 2289 *self.after_limit_modifiers(expression), 2290 self.options_modifier(expression), 2291 sep="", 2292 )
def
offset_limit_modifiers( self, expression: sqlglot.expressions.Expression, fetch: bool, limit: Union[sqlglot.expressions.Fetch, sqlglot.expressions.Limit, NoneType]) -> List[str]:
2301 def offset_limit_modifiers( 2302 self, expression: exp.Expression, fetch: bool, limit: t.Optional[exp.Fetch | exp.Limit] 2303 ) -> t.List[str]: 2304 return [ 2305 self.sql(expression, "offset") if fetch else self.sql(limit), 2306 self.sql(limit) if fetch else self.sql(expression, "offset"), 2307 ]
2314 def select_sql(self, expression: exp.Select) -> str: 2315 into = expression.args.get("into") 2316 if not self.SUPPORTS_SELECT_INTO and into: 2317 into.pop() 2318 2319 hint = self.sql(expression, "hint") 2320 distinct = self.sql(expression, "distinct") 2321 distinct = f" {distinct}" if distinct else "" 2322 kind = self.sql(expression, "kind") 2323 2324 limit = expression.args.get("limit") 2325 if isinstance(limit, exp.Limit) and self.LIMIT_IS_TOP: 2326 top = self.limit_sql(limit, top=True) 2327 limit.pop() 2328 else: 2329 top = "" 2330 2331 expressions = self.expressions(expression) 2332 2333 if kind: 2334 if kind in self.SELECT_KINDS: 2335 kind = f" AS {kind}" 2336 else: 2337 if kind == "STRUCT": 2338 expressions = self.expressions( 2339 sqls=[ 2340 self.sql( 2341 exp.Struct( 2342 expressions=[ 2343 exp.PropertyEQ(this=e.args.get("alias"), expression=e.this) 2344 if isinstance(e, exp.Alias) 2345 else e 2346 for e in expression.expressions 2347 ] 2348 ) 2349 ) 2350 ] 2351 ) 2352 kind = "" 2353 2354 # We use LIMIT_IS_TOP as a proxy for whether DISTINCT should go first because tsql and Teradata 2355 # are the only dialects that use LIMIT_IS_TOP and both place DISTINCT first. 2356 top_distinct = f"{distinct}{hint}{top}" if self.LIMIT_IS_TOP else f"{top}{hint}{distinct}" 2357 expressions = f"{self.sep()}{expressions}" if expressions else expressions 2358 sql = self.query_modifiers( 2359 expression, 2360 f"SELECT{top_distinct}{kind}{expressions}", 2361 self.sql(expression, "into", comment=False), 2362 self.sql(expression, "from", comment=False), 2363 ) 2364 2365 sql = self.prepend_ctes(expression, sql) 2366 2367 if not self.SUPPORTS_SELECT_INTO and into: 2368 if into.args.get("temporary"): 2369 table_kind = " TEMPORARY" 2370 elif self.SUPPORTS_UNLOGGED_TABLES and into.args.get("unlogged"): 2371 table_kind = " UNLOGGED" 2372 else: 2373 table_kind = "" 2374 sql = f"CREATE{table_kind} TABLE {self.sql(into.this)} AS {sql}" 2375 2376 return sql
2388 def star_sql(self, expression: exp.Star) -> str: 2389 except_ = self.expressions(expression, key="except", flat=True) 2390 except_ = f"{self.seg(self.STAR_EXCEPT)} ({except_})" if except_ else "" 2391 replace = self.expressions(expression, key="replace", flat=True) 2392 replace = f"{self.seg('REPLACE')} ({replace})" if replace else "" 2393 rename = self.expressions(expression, key="rename", flat=True) 2394 rename = f"{self.seg('RENAME')} ({rename})" if rename else "" 2395 return f"*{except_}{replace}{rename}"
2411 def subquery_sql(self, expression: exp.Subquery, sep: str = " AS ") -> str: 2412 alias = self.sql(expression, "alias") 2413 alias = f"{sep}{alias}" if alias else "" 2414 pivots = self.expressions(expression, key="pivots", sep="", flat=True) 2415 sql = self.query_modifiers(expression, self.wrap(expression), alias, pivots) 2416 return self.prepend_ctes(expression, sql)
2422 def set_operations(self, expression: exp.SetOperation) -> str: 2423 if not self.SET_OP_MODIFIERS: 2424 limit = expression.args.get("limit") 2425 order = expression.args.get("order") 2426 2427 if limit or order: 2428 select = exp.subquery(expression, "_l_0", copy=False).select("*", copy=False) 2429 2430 if limit: 2431 select = select.limit(limit.pop(), copy=False) 2432 if order: 2433 select = select.order_by(order.pop(), copy=False) 2434 return self.sql(select) 2435 2436 sqls: t.List[str] = [] 2437 stack: t.List[t.Union[str, exp.Expression]] = [expression] 2438 2439 while stack: 2440 node = stack.pop() 2441 2442 if isinstance(node, exp.SetOperation): 2443 stack.append(node.expression) 2444 stack.append( 2445 self.maybe_comment( 2446 getattr(self, f"{node.key}_op")(node), 2447 comments=node.comments, 2448 separated=True, 2449 ) 2450 ) 2451 stack.append(node.this) 2452 else: 2453 sqls.append(self.sql(node)) 2454 2455 this = self.sep().join(sqls) 2456 this = self.query_modifiers(expression, this) 2457 return self.prepend_ctes(expression, this)
2468 def unnest_sql(self, expression: exp.Unnest) -> str: 2469 args = self.expressions(expression, flat=True) 2470 2471 alias = expression.args.get("alias") 2472 offset = expression.args.get("offset") 2473 2474 if self.UNNEST_WITH_ORDINALITY: 2475 if alias and isinstance(offset, exp.Expression): 2476 alias.append("columns", offset) 2477 2478 if alias and self.dialect.UNNEST_COLUMN_ONLY: 2479 columns = alias.columns 2480 alias = self.sql(columns[0]) if columns else "" 2481 else: 2482 alias = self.sql(alias) 2483 2484 alias = f" AS {alias}" if alias else alias 2485 if self.UNNEST_WITH_ORDINALITY: 2486 suffix = f" WITH ORDINALITY{alias}" if offset else alias 2487 else: 2488 if isinstance(offset, exp.Expression): 2489 suffix = f"{alias} WITH OFFSET AS {self.sql(offset)}" 2490 elif offset: 2491 suffix = f"{alias} WITH OFFSET" 2492 else: 2493 suffix = alias 2494 2495 return f"UNNEST({args}){suffix}"
2504 def window_sql(self, expression: exp.Window) -> str: 2505 this = self.sql(expression, "this") 2506 partition = self.partition_by_sql(expression) 2507 order = expression.args.get("order") 2508 order = self.order_sql(order, flat=True) if order else "" 2509 spec = self.sql(expression, "spec") 2510 alias = self.sql(expression, "alias") 2511 over = self.sql(expression, "over") or "OVER" 2512 2513 this = f"{this} {'AS' if expression.arg_key == 'windows' else over}" 2514 2515 first = expression.args.get("first") 2516 if first is None: 2517 first = "" 2518 else: 2519 first = "FIRST" if first else "LAST" 2520 2521 if not partition and not order and not spec and alias: 2522 return f"{this} {alias}" 2523 2524 args = " ".join(arg for arg in (alias, first, partition, order, spec) if arg) 2525 return f"{this} ({args})"
def
partition_by_sql( self, expression: sqlglot.expressions.Window | sqlglot.expressions.MatchRecognize) -> str:
2531 def windowspec_sql(self, expression: exp.WindowSpec) -> str: 2532 kind = self.sql(expression, "kind") 2533 start = csv(self.sql(expression, "start"), self.sql(expression, "start_side"), sep=" ") 2534 end = ( 2535 csv(self.sql(expression, "end"), self.sql(expression, "end_side"), sep=" ") 2536 or "CURRENT ROW" 2537 ) 2538 return f"{kind} BETWEEN {start} AND {end}"
def
bracket_offset_expressions( self, expression: sqlglot.expressions.Bracket) -> List[sqlglot.expressions.Expression]:
2566 def any_sql(self, expression: exp.Any) -> str: 2567 this = self.sql(expression, "this") 2568 if isinstance(expression.this, (*exp.UNWRAPPED_QUERIES, exp.Paren)): 2569 if isinstance(expression.this, exp.UNWRAPPED_QUERIES): 2570 this = self.wrap(this) 2571 return f"ANY{this}" 2572 return f"ANY {this}"
2577 def case_sql(self, expression: exp.Case) -> str: 2578 this = self.sql(expression, "this") 2579 statements = [f"CASE {this}" if this else "CASE"] 2580 2581 for e in expression.args["ifs"]: 2582 statements.append(f"WHEN {self.sql(e, 'this')}") 2583 statements.append(f"THEN {self.sql(e, 'true')}") 2584 2585 default = self.sql(expression, "default") 2586 2587 if default: 2588 statements.append(f"ELSE {default}") 2589 2590 statements.append("END") 2591 2592 if self.pretty and self.too_wide(statements): 2593 return self.indent("\n".join(statements), skip_first=True, skip_last=True) 2594 2595 return " ".join(statements)
2612 def trim_sql(self, expression: exp.Trim) -> str: 2613 trim_type = self.sql(expression, "position") 2614 2615 if trim_type == "LEADING": 2616 return self.func("LTRIM", expression.this) 2617 elif trim_type == "TRAILING": 2618 return self.func("RTRIM", expression.this) 2619 else: 2620 return self.func("TRIM", expression.this, expression.expression)
def
convert_concat_args( self, expression: sqlglot.expressions.Concat | sqlglot.expressions.ConcatWs) -> List[sqlglot.expressions.Expression]:
2622 def convert_concat_args(self, expression: exp.Concat | exp.ConcatWs) -> t.List[exp.Expression]: 2623 args = expression.expressions 2624 if isinstance(expression, exp.ConcatWs): 2625 args = args[1:] # Skip the delimiter 2626 2627 if self.dialect.STRICT_STRING_CONCAT and expression.args.get("safe"): 2628 args = [exp.cast(e, exp.DataType.Type.TEXT) for e in args] 2629 2630 if not self.dialect.CONCAT_COALESCE and expression.args.get("coalesce"): 2631 args = [exp.func("coalesce", e, exp.Literal.string("")) for e in args] 2632 2633 return args
2635 def concat_sql(self, expression: exp.Concat) -> str: 2636 expressions = self.convert_concat_args(expression) 2637 2638 # Some dialects don't allow a single-argument CONCAT call 2639 if not self.SUPPORTS_SINGLE_ARG_CONCAT and len(expressions) == 1: 2640 return self.sql(expressions[0]) 2641 2642 return self.func("CONCAT", *expressions)
2653 def foreignkey_sql(self, expression: exp.ForeignKey) -> str: 2654 expressions = self.expressions(expression, flat=True) 2655 reference = self.sql(expression, "reference") 2656 reference = f" {reference}" if reference else "" 2657 delete = self.sql(expression, "delete") 2658 delete = f" ON DELETE {delete}" if delete else "" 2659 update = self.sql(expression, "update") 2660 update = f" ON UPDATE {update}" if update else "" 2661 return f"FOREIGN KEY ({expressions}){reference}{delete}{update}"
2663 def primarykey_sql(self, expression: exp.ForeignKey) -> str: 2664 expressions = self.expressions(expression, flat=True) 2665 options = self.expressions(expression, key="options", flat=True, sep=" ") 2666 options = f" {options}" if options else "" 2667 return f"PRIMARY KEY ({expressions}){options}"
2687 def json_path_part(self, expression: int | str | exp.JSONPathPart) -> str: 2688 if isinstance(expression, exp.JSONPathPart): 2689 transform = self.TRANSFORMS.get(expression.__class__) 2690 if not callable(transform): 2691 self.unsupported(f"Unsupported JSONPathPart type {expression.__class__.__name__}") 2692 return "" 2693 2694 return transform(self, expression) 2695 2696 if isinstance(expression, int): 2697 return str(expression) 2698 2699 if self.JSON_PATH_SINGLE_QUOTE_ESCAPE: 2700 escaped = expression.replace("'", "\\'") 2701 escaped = f"\\'{expression}\\'" 2702 else: 2703 escaped = expression.replace('"', '\\"') 2704 escaped = f'"{escaped}"' 2705 2706 return escaped
def
jsonobject_sql( self, expression: sqlglot.expressions.JSONObject | sqlglot.expressions.JSONObjectAgg) -> str:
2711 def jsonobject_sql(self, expression: exp.JSONObject | exp.JSONObjectAgg) -> str: 2712 null_handling = expression.args.get("null_handling") 2713 null_handling = f" {null_handling}" if null_handling else "" 2714 2715 unique_keys = expression.args.get("unique_keys") 2716 if unique_keys is not None: 2717 unique_keys = f" {'WITH' if unique_keys else 'WITHOUT'} UNIQUE KEYS" 2718 else: 2719 unique_keys = "" 2720 2721 return_type = self.sql(expression, "return_type") 2722 return_type = f" RETURNING {return_type}" if return_type else "" 2723 encoding = self.sql(expression, "encoding") 2724 encoding = f" ENCODING {encoding}" if encoding else "" 2725 2726 return self.func( 2727 "JSON_OBJECT" if isinstance(expression, exp.JSONObject) else "JSON_OBJECTAGG", 2728 *expression.expressions, 2729 suffix=f"{null_handling}{unique_keys}{return_type}{encoding})", 2730 )
2735 def jsonarray_sql(self, expression: exp.JSONArray) -> str: 2736 null_handling = expression.args.get("null_handling") 2737 null_handling = f" {null_handling}" if null_handling else "" 2738 return_type = self.sql(expression, "return_type") 2739 return_type = f" RETURNING {return_type}" if return_type else "" 2740 strict = " STRICT" if expression.args.get("strict") else "" 2741 return self.func( 2742 "JSON_ARRAY", *expression.expressions, suffix=f"{null_handling}{return_type}{strict})" 2743 )
2745 def jsonarrayagg_sql(self, expression: exp.JSONArrayAgg) -> str: 2746 this = self.sql(expression, "this") 2747 order = self.sql(expression, "order") 2748 null_handling = expression.args.get("null_handling") 2749 null_handling = f" {null_handling}" if null_handling else "" 2750 return_type = self.sql(expression, "return_type") 2751 return_type = f" RETURNING {return_type}" if return_type else "" 2752 strict = " STRICT" if expression.args.get("strict") else "" 2753 return self.func( 2754 "JSON_ARRAYAGG", 2755 this, 2756 suffix=f"{order}{null_handling}{return_type}{strict})", 2757 )
2759 def jsoncolumndef_sql(self, expression: exp.JSONColumnDef) -> str: 2760 path = self.sql(expression, "path") 2761 path = f" PATH {path}" if path else "" 2762 nested_schema = self.sql(expression, "nested_schema") 2763 2764 if nested_schema: 2765 return f"NESTED{path} {nested_schema}" 2766 2767 this = self.sql(expression, "this") 2768 kind = self.sql(expression, "kind") 2769 kind = f" {kind}" if kind else "" 2770 return f"{this}{kind}{path}"
2775 def jsontable_sql(self, expression: exp.JSONTable) -> str: 2776 this = self.sql(expression, "this") 2777 path = self.sql(expression, "path") 2778 path = f", {path}" if path else "" 2779 error_handling = expression.args.get("error_handling") 2780 error_handling = f" {error_handling}" if error_handling else "" 2781 empty_handling = expression.args.get("empty_handling") 2782 empty_handling = f" {empty_handling}" if empty_handling else "" 2783 schema = self.sql(expression, "schema") 2784 return self.func( 2785 "JSON_TABLE", this, suffix=f"{path}{error_handling}{empty_handling} {schema})" 2786 )
2788 def openjsoncolumndef_sql(self, expression: exp.OpenJSONColumnDef) -> str: 2789 this = self.sql(expression, "this") 2790 kind = self.sql(expression, "kind") 2791 path = self.sql(expression, "path") 2792 path = f" {path}" if path else "" 2793 as_json = " AS JSON" if expression.args.get("as_json") else "" 2794 return f"{this} {kind}{path}{as_json}"
2796 def openjson_sql(self, expression: exp.OpenJSON) -> str: 2797 this = self.sql(expression, "this") 2798 path = self.sql(expression, "path") 2799 path = f", {path}" if path else "" 2800 expressions = self.expressions(expression) 2801 with_ = ( 2802 f" WITH ({self.seg(self.indent(expressions), sep='')}{self.seg(')', sep='')}" 2803 if expressions 2804 else "" 2805 ) 2806 return f"OPENJSON({this}{path}){with_}"
2808 def in_sql(self, expression: exp.In) -> str: 2809 query = expression.args.get("query") 2810 unnest = expression.args.get("unnest") 2811 field = expression.args.get("field") 2812 is_global = " GLOBAL" if expression.args.get("is_global") else "" 2813 2814 if query: 2815 in_sql = self.sql(query) 2816 elif unnest: 2817 in_sql = self.in_unnest_op(unnest) 2818 elif field: 2819 in_sql = self.sql(field) 2820 else: 2821 in_sql = f"({self.expressions(expression, flat=True)})" 2822 2823 return f"{self.sql(expression, 'this')}{is_global} IN {in_sql}"
2828 def interval_sql(self, expression: exp.Interval) -> str: 2829 unit = self.sql(expression, "unit") 2830 if not self.INTERVAL_ALLOWS_PLURAL_FORM: 2831 unit = self.TIME_PART_SINGULARS.get(unit, unit) 2832 unit = f" {unit}" if unit else "" 2833 2834 if self.SINGLE_STRING_INTERVAL: 2835 this = expression.this.name if expression.this else "" 2836 return f"INTERVAL '{this}{unit}'" if this else f"INTERVAL{unit}" 2837 2838 this = self.sql(expression, "this") 2839 if this: 2840 unwrapped = isinstance(expression.this, self.UNWRAPPED_INTERVAL_VALUES) 2841 this = f" {this}" if unwrapped else f" ({this})" 2842 2843 return f"INTERVAL{this}{unit}"
2848 def reference_sql(self, expression: exp.Reference) -> str: 2849 this = self.sql(expression, "this") 2850 expressions = self.expressions(expression, flat=True) 2851 expressions = f"({expressions})" if expressions else "" 2852 options = self.expressions(expression, key="options", flat=True, sep=" ") 2853 options = f" {options}" if options else "" 2854 return f"REFERENCES {this}{expressions}{options}"
2877 def pivotalias_sql(self, expression: exp.PivotAlias) -> str: 2878 alias = expression.args["alias"] 2879 identifier_alias = isinstance(alias, exp.Identifier) 2880 2881 if identifier_alias and not self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2882 alias.replace(exp.Literal.string(alias.output_name)) 2883 elif not identifier_alias and self.UNPIVOT_ALIASES_ARE_IDENTIFIERS: 2884 alias.replace(exp.to_identifier(alias.output_name)) 2885 2886 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:
2924 def connector_sql( 2925 self, 2926 expression: exp.Connector, 2927 op: str, 2928 stack: t.Optional[t.List[str | exp.Expression]] = None, 2929 ) -> str: 2930 if stack is not None: 2931 if expression.expressions: 2932 stack.append(self.expressions(expression, sep=f" {op} ")) 2933 else: 2934 stack.append(expression.right) 2935 if expression.comments and self.comments: 2936 for comment in expression.comments: 2937 if comment: 2938 op += f" /*{self.pad_comment(comment)}*/" 2939 stack.extend((op, expression.left)) 2940 return op 2941 2942 stack = [expression] 2943 sqls: t.List[str] = [] 2944 ops = set() 2945 2946 while stack: 2947 node = stack.pop() 2948 if isinstance(node, exp.Connector): 2949 ops.add(getattr(self, f"{node.key}_sql")(node, stack)) 2950 else: 2951 sql = self.sql(node) 2952 if sqls and sqls[-1] in ops: 2953 sqls[-1] += f" {sql}" 2954 else: 2955 sqls.append(sql) 2956 2957 sep = "\n" if self.pretty and self.too_wide(sqls) else " " 2958 return sep.join(sqls)
def
cast_sql( self, expression: sqlglot.expressions.Cast, safe_prefix: Optional[str] = None) -> str:
2978 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 2979 format_sql = self.sql(expression, "format") 2980 format_sql = f" FORMAT {format_sql}" if format_sql else "" 2981 to_sql = self.sql(expression, "to") 2982 to_sql = f" {to_sql}" if to_sql else "" 2983 action = self.sql(expression, "action") 2984 action = f" {action}" if action else "" 2985 return f"{safe_prefix or ''}CAST({self.sql(expression, 'this')} AS{to_sql}{format_sql}{action})"
2999 def comment_sql(self, expression: exp.Comment) -> str: 3000 this = self.sql(expression, "this") 3001 kind = expression.args["kind"] 3002 materialized = " MATERIALIZED" if expression.args.get("materialized") else "" 3003 exists_sql = " IF EXISTS " if expression.args.get("exists") else " " 3004 expression_sql = self.sql(expression, "expression") 3005 return f"COMMENT{exists_sql}ON{materialized} {kind} {this} IS {expression_sql}"
3007 def mergetreettlaction_sql(self, expression: exp.MergeTreeTTLAction) -> str: 3008 this = self.sql(expression, "this") 3009 delete = " DELETE" if expression.args.get("delete") else "" 3010 recompress = self.sql(expression, "recompress") 3011 recompress = f" RECOMPRESS {recompress}" if recompress else "" 3012 to_disk = self.sql(expression, "to_disk") 3013 to_disk = f" TO DISK {to_disk}" if to_disk else "" 3014 to_volume = self.sql(expression, "to_volume") 3015 to_volume = f" TO VOLUME {to_volume}" if to_volume else "" 3016 return f"{this}{delete}{recompress}{to_disk}{to_volume}"
3018 def mergetreettl_sql(self, expression: exp.MergeTreeTTL) -> str: 3019 where = self.sql(expression, "where") 3020 group = self.sql(expression, "group") 3021 aggregates = self.expressions(expression, key="aggregates") 3022 aggregates = self.seg("SET") + self.seg(aggregates) if aggregates else "" 3023 3024 if not (where or group or aggregates) and len(expression.expressions) == 1: 3025 return f"TTL {self.expressions(expression, flat=True)}" 3026 3027 return f"TTL{self.seg(self.expressions(expression))}{where}{group}{aggregates}"
3044 def altercolumn_sql(self, expression: exp.AlterColumn) -> str: 3045 this = self.sql(expression, "this") 3046 3047 dtype = self.sql(expression, "dtype") 3048 if dtype: 3049 collate = self.sql(expression, "collate") 3050 collate = f" COLLATE {collate}" if collate else "" 3051 using = self.sql(expression, "using") 3052 using = f" USING {using}" if using else "" 3053 return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" 3054 3055 default = self.sql(expression, "default") 3056 if default: 3057 return f"ALTER COLUMN {this} SET DEFAULT {default}" 3058 3059 comment = self.sql(expression, "comment") 3060 if comment: 3061 return f"ALTER COLUMN {this} COMMENT {comment}" 3062 3063 allow_null = expression.args.get("allow_null") 3064 drop = expression.args.get("drop") 3065 3066 if not drop and not allow_null: 3067 self.unsupported("Unsupported ALTER COLUMN syntax") 3068 3069 if allow_null is not None: 3070 keyword = "DROP" if drop else "SET" 3071 return f"ALTER COLUMN {this} {keyword} NOT NULL" 3072 3073 return f"ALTER COLUMN {this} DROP DEFAULT"
3081 def altersortkey_sql(self, expression: exp.AlterSortKey) -> str: 3082 compound = " COMPOUND" if expression.args.get("compound") else "" 3083 this = self.sql(expression, "this") 3084 expressions = self.expressions(expression, flat=True) 3085 expressions = f"({expressions})" if expressions else "" 3086 return f"ALTER{compound} SORTKEY {this or expressions}"
3088 def renametable_sql(self, expression: exp.RenameTable) -> str: 3089 if not self.RENAME_TABLE_WITH_DB: 3090 # Remove db from tables 3091 expression = expression.transform( 3092 lambda n: exp.table_(n.this) if isinstance(n, exp.Table) else n 3093 ).assert_is(exp.RenameTable) 3094 this = self.sql(expression, "this") 3095 return f"RENAME TO {this}"
3107 def altertable_sql(self, expression: exp.AlterTable) -> str: 3108 actions = expression.args["actions"] 3109 3110 if isinstance(actions[0], exp.ColumnDef): 3111 actions = self.add_column_sql(expression) 3112 elif isinstance(actions[0], exp.Schema): 3113 actions = self.expressions(expression, key="actions", prefix="ADD COLUMNS ") 3114 elif isinstance(actions[0], exp.Delete): 3115 actions = self.expressions(expression, key="actions", flat=True) 3116 else: 3117 actions = self.expressions(expression, key="actions", flat=True) 3118 3119 exists = " IF EXISTS" if expression.args.get("exists") else "" 3120 on_cluster = self.sql(expression, "cluster") 3121 on_cluster = f" {on_cluster}" if on_cluster else "" 3122 only = " ONLY" if expression.args.get("only") else "" 3123 options = self.expressions(expression, key="options") 3124 options = f", {options}" if options else "" 3125 return f"ALTER TABLE{exists}{only} {self.sql(expression, 'this')}{on_cluster} {actions}{options}"
3127 def add_column_sql(self, expression: exp.AlterTable) -> str: 3128 if self.ALTER_TABLE_INCLUDE_COLUMN_KEYWORD: 3129 return self.expressions( 3130 expression, 3131 key="actions", 3132 prefix="ADD COLUMN ", 3133 skip_first=True, 3134 ) 3135 return f"ADD {self.expressions(expression, key='actions', flat=True)}"
3145 def distinct_sql(self, expression: exp.Distinct) -> str: 3146 this = self.expressions(expression, flat=True) 3147 3148 if not self.MULTI_ARG_DISTINCT and len(expression.expressions) > 1: 3149 case = exp.case() 3150 for arg in expression.expressions: 3151 case = case.when(arg.is_(exp.null()), exp.null()) 3152 this = self.sql(case.else_(f"({this})")) 3153 3154 this = f" {this}" if this else "" 3155 3156 on = self.sql(expression, "on") 3157 on = f" ON {on}" if on else "" 3158 return f"DISTINCT{this}{on}"
3187 def div_sql(self, expression: exp.Div) -> str: 3188 l, r = expression.left, expression.right 3189 3190 if not self.dialect.SAFE_DIVISION and expression.args.get("safe"): 3191 r.replace(exp.Nullif(this=r.copy(), expression=exp.Literal.number(0))) 3192 3193 if self.dialect.TYPED_DIVISION and not expression.args.get("typed"): 3194 if not l.is_type(*exp.DataType.REAL_TYPES) and not r.is_type(*exp.DataType.REAL_TYPES): 3195 l.replace(exp.cast(l.copy(), to=exp.DataType.Type.DOUBLE)) 3196 3197 elif not self.dialect.TYPED_DIVISION and expression.args.get("typed"): 3198 if l.is_type(*exp.DataType.INTEGER_TYPES) and r.is_type(*exp.DataType.INTEGER_TYPES): 3199 return self.sql( 3200 exp.cast( 3201 l / r, 3202 to=exp.DataType.Type.BIGINT, 3203 ) 3204 ) 3205 3206 return self.binary(expression, "/")
3294 def log_sql(self, expression: exp.Log) -> str: 3295 this = expression.this 3296 expr = expression.expression 3297 3298 if self.dialect.LOG_BASE_FIRST is False: 3299 this, expr = expr, this 3300 elif self.dialect.LOG_BASE_FIRST is None and expr: 3301 if this.name in ("2", "10"): 3302 return self.func(f"LOG{this.name}", expr) 3303 3304 self.unsupported(f"Unsupported logarithm with base {self.sql(this)}") 3305 3306 return self.func("LOG", this, expr)
3319 def function_fallback_sql(self, expression: exp.Func) -> str: 3320 args = [] 3321 3322 for key in expression.arg_types: 3323 arg_value = expression.args.get(key) 3324 3325 if isinstance(arg_value, list): 3326 for value in arg_value: 3327 args.append(value) 3328 elif arg_value is not None: 3329 args.append(arg_value) 3330 3331 if self.normalize_functions: 3332 name = expression.sql_name() 3333 else: 3334 name = (expression._meta and expression.meta.get("name")) or expression.sql_name() 3335 3336 return self.func(name, *args)
def
func( self, name: str, *args: Union[str, sqlglot.expressions.Expression, NoneType], prefix: str = '(', suffix: str = ')') -> str:
3347 def format_args(self, *args: t.Optional[str | exp.Expression]) -> str: 3348 arg_sqls = tuple( 3349 self.sql(arg) for arg in args if arg is not None and not isinstance(arg, bool) 3350 ) 3351 if self.pretty and self.too_wide(arg_sqls): 3352 return self.indent("\n" + ",\n".join(arg_sqls) + "\n", skip_first=True, skip_last=True) 3353 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]:
3358 def format_time( 3359 self, 3360 expression: exp.Expression, 3361 inverse_time_mapping: t.Optional[t.Dict[str, str]] = None, 3362 inverse_time_trie: t.Optional[t.Dict] = None, 3363 ) -> t.Optional[str]: 3364 return format_time( 3365 self.sql(expression, "format"), 3366 inverse_time_mapping or self.dialect.INVERSE_TIME_MAPPING, 3367 inverse_time_trie or self.dialect.INVERSE_TIME_TRIE, 3368 )
def
expressions( self, expression: Optional[sqlglot.expressions.Expression] = None, key: Optional[str] = None, sqls: Optional[Collection[Union[str, sqlglot.expressions.Expression]]] = None, flat: bool = False, indent: bool = True, skip_first: bool = False, skip_last: bool = False, sep: str = ', ', prefix: str = '', dynamic: bool = False, new_line: bool = False) -> str:
3370 def expressions( 3371 self, 3372 expression: t.Optional[exp.Expression] = None, 3373 key: t.Optional[str] = None, 3374 sqls: t.Optional[t.Collection[str | exp.Expression]] = None, 3375 flat: bool = False, 3376 indent: bool = True, 3377 skip_first: bool = False, 3378 skip_last: bool = False, 3379 sep: str = ", ", 3380 prefix: str = "", 3381 dynamic: bool = False, 3382 new_line: bool = False, 3383 ) -> str: 3384 expressions = expression.args.get(key or "expressions") if expression else sqls 3385 3386 if not expressions: 3387 return "" 3388 3389 if flat: 3390 return sep.join(sql for sql in (self.sql(e) for e in expressions) if sql) 3391 3392 num_sqls = len(expressions) 3393 result_sqls = [] 3394 3395 for i, e in enumerate(expressions): 3396 sql = self.sql(e, comment=False) 3397 if not sql: 3398 continue 3399 3400 comments = self.maybe_comment("", e) if isinstance(e, exp.Expression) else "" 3401 3402 if self.pretty: 3403 if self.leading_comma: 3404 result_sqls.append(f"{sep if i > 0 else ''}{prefix}{sql}{comments}") 3405 else: 3406 result_sqls.append( 3407 f"{prefix}{sql}{(sep.rstrip() if comments else sep) if i + 1 < num_sqls else ''}{comments}" 3408 ) 3409 else: 3410 result_sqls.append(f"{prefix}{sql}{comments}{sep if i + 1 < num_sqls else ''}") 3411 3412 if self.pretty and (not dynamic or self.too_wide(result_sqls)): 3413 if new_line: 3414 result_sqls.insert(0, "") 3415 result_sqls.append("") 3416 result_sql = "\n".join(s.rstrip() for s in result_sqls) 3417 else: 3418 result_sql = "".join(result_sqls) 3419 return ( 3420 self.indent(result_sql, skip_first=skip_first, skip_last=skip_last) 3421 if indent 3422 else result_sql 3423 )
def
op_expressions( self, op: str, expression: sqlglot.expressions.Expression, flat: bool = False) -> str:
3425 def op_expressions(self, op: str, expression: exp.Expression, flat: bool = False) -> str: 3426 flat = flat or isinstance(expression.parent, exp.Properties) 3427 expressions_sql = self.expressions(expression, flat=flat) 3428 if flat: 3429 return f"{op} {expressions_sql}" 3430 return f"{self.seg(op)}{self.sep() if expressions_sql else ''}{expressions_sql}"
3432 def naked_property(self, expression: exp.Property) -> str: 3433 property_name = exp.Properties.PROPERTY_TO_NAME.get(expression.__class__) 3434 if not property_name: 3435 self.unsupported(f"Unsupported property {expression.__class__.__name__}") 3436 return f"{property_name} {self.sql(expression, 'this')}"
3444 def userdefinedfunction_sql(self, expression: exp.UserDefinedFunction) -> str: 3445 this = self.sql(expression, "this") 3446 expressions = self.no_identify(self.expressions, expression) 3447 expressions = ( 3448 self.wrap(expressions) if expression.args.get("wrapped") else f" {expressions}" 3449 ) 3450 return f"{this}{expressions}"
3460 def when_sql(self, expression: exp.When) -> str: 3461 matched = "MATCHED" if expression.args["matched"] else "NOT MATCHED" 3462 source = " BY SOURCE" if self.MATCHED_BY_SOURCE and expression.args.get("source") else "" 3463 condition = self.sql(expression, "condition") 3464 condition = f" AND {condition}" if condition else "" 3465 3466 then_expression = expression.args.get("then") 3467 if isinstance(then_expression, exp.Insert): 3468 this = self.sql(then_expression, "this") 3469 this = f"INSERT {this}" if this else "INSERT" 3470 then = self.sql(then_expression, "expression") 3471 then = f"{this} VALUES {then}" if then else this 3472 elif isinstance(then_expression, exp.Update): 3473 if isinstance(then_expression.args.get("expressions"), exp.Star): 3474 then = f"UPDATE {self.sql(then_expression, 'expressions')}" 3475 else: 3476 then = f"UPDATE SET {self.expressions(then_expression, flat=True)}" 3477 else: 3478 then = self.sql(then_expression) 3479 return f"WHEN {matched}{source}{condition} THEN {then}"
3481 def merge_sql(self, expression: exp.Merge) -> str: 3482 table = expression.this 3483 table_alias = "" 3484 3485 hints = table.args.get("hints") 3486 if hints and table.alias and isinstance(hints[0], exp.WithTableHint): 3487 # T-SQL syntax is MERGE ... <target_table> [WITH (<merge_hint>)] [[AS] table_alias] 3488 table_alias = f" AS {self.sql(table.args['alias'].pop())}" 3489 3490 this = self.sql(table) 3491 using = f"USING {self.sql(expression, 'using')}" 3492 on = f"ON {self.sql(expression, 'on')}" 3493 expressions = self.expressions(expression, sep=" ", indent=False) 3494 sep = self.sep() 3495 3496 return self.prepend_ctes( 3497 expression, f"MERGE INTO {this}{table_alias}{sep}{using}{sep}{on}{sep}{expressions}" 3498 )
3506 def tonumber_sql(self, expression: exp.ToNumber) -> str: 3507 if not self.SUPPORTS_TO_NUMBER: 3508 self.unsupported("Unsupported TO_NUMBER function") 3509 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3510 3511 fmt = expression.args.get("format") 3512 if not fmt: 3513 self.unsupported("Conversion format is required for TO_NUMBER") 3514 return self.sql(exp.cast(expression.this, exp.DataType.Type.DOUBLE)) 3515 3516 return self.func("TO_NUMBER", expression.this, fmt)
3518 def dictproperty_sql(self, expression: exp.DictProperty) -> str: 3519 this = self.sql(expression, "this") 3520 kind = self.sql(expression, "kind") 3521 settings_sql = self.expressions(expression, key="settings", sep=" ") 3522 args = f"({self.sep('')}{settings_sql}{self.seg(')', sep='')}" if settings_sql else "()" 3523 return f"{this}({kind}{args})"
3537 def clusteredbyproperty_sql(self, expression: exp.ClusteredByProperty) -> str: 3538 expressions = self.expressions(expression, key="expressions", flat=True) 3539 sorted_by = self.expressions(expression, key="sorted_by", flat=True) 3540 sorted_by = f" SORTED BY ({sorted_by})" if sorted_by else "" 3541 buckets = self.sql(expression, "buckets") 3542 return f"CLUSTERED BY ({expressions}){sorted_by} INTO {buckets} BUCKETS"
3544 def anyvalue_sql(self, expression: exp.AnyValue) -> str: 3545 this = self.sql(expression, "this") 3546 having = self.sql(expression, "having") 3547 3548 if having: 3549 this = f"{this} HAVING {'MAX' if expression.args.get('max') else 'MIN'} {having}" 3550 3551 return self.func("ANY_VALUE", this)
3553 def querytransform_sql(self, expression: exp.QueryTransform) -> str: 3554 transform = self.func("TRANSFORM", *expression.expressions) 3555 row_format_before = self.sql(expression, "row_format_before") 3556 row_format_before = f" {row_format_before}" if row_format_before else "" 3557 record_writer = self.sql(expression, "record_writer") 3558 record_writer = f" RECORDWRITER {record_writer}" if record_writer else "" 3559 using = f" USING {self.sql(expression, 'command_script')}" 3560 schema = self.sql(expression, "schema") 3561 schema = f" AS {schema}" if schema else "" 3562 row_format_after = self.sql(expression, "row_format_after") 3563 row_format_after = f" {row_format_after}" if row_format_after else "" 3564 record_reader = self.sql(expression, "record_reader") 3565 record_reader = f" RECORDREADER {record_reader}" if record_reader else "" 3566 return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
3568 def indexconstraintoption_sql(self, expression: exp.IndexConstraintOption) -> str: 3569 key_block_size = self.sql(expression, "key_block_size") 3570 if key_block_size: 3571 return f"KEY_BLOCK_SIZE = {key_block_size}" 3572 3573 using = self.sql(expression, "using") 3574 if using: 3575 return f"USING {using}" 3576 3577 parser = self.sql(expression, "parser") 3578 if parser: 3579 return f"WITH PARSER {parser}" 3580 3581 comment = self.sql(expression, "comment") 3582 if comment: 3583 return f"COMMENT {comment}" 3584 3585 visible = expression.args.get("visible") 3586 if visible is not None: 3587 return "VISIBLE" if visible else "INVISIBLE" 3588 3589 engine_attr = self.sql(expression, "engine_attr") 3590 if engine_attr: 3591 return f"ENGINE_ATTRIBUTE = {engine_attr}" 3592 3593 secondary_engine_attr = self.sql(expression, "secondary_engine_attr") 3594 if secondary_engine_attr: 3595 return f"SECONDARY_ENGINE_ATTRIBUTE = {secondary_engine_attr}" 3596 3597 self.unsupported("Unsupported index constraint option.") 3598 return ""
3604 def indexcolumnconstraint_sql(self, expression: exp.IndexColumnConstraint) -> str: 3605 kind = self.sql(expression, "kind") 3606 kind = f"{kind} INDEX" if kind else "INDEX" 3607 this = self.sql(expression, "this") 3608 this = f" {this}" if this else "" 3609 index_type = self.sql(expression, "index_type") 3610 index_type = f" USING {index_type}" if index_type else "" 3611 expressions = self.expressions(expression, flat=True) 3612 expressions = f" ({expressions})" if expressions else "" 3613 options = self.expressions(expression, key="options", sep=" ") 3614 options = f" {options}" if options else "" 3615 return f"{kind}{this}{index_type}{expressions}{options}"
3617 def nvl2_sql(self, expression: exp.Nvl2) -> str: 3618 if self.NVL2_SUPPORTED: 3619 return self.function_fallback_sql(expression) 3620 3621 case = exp.Case().when( 3622 expression.this.is_(exp.null()).not_(copy=False), 3623 expression.args["true"], 3624 copy=False, 3625 ) 3626 else_cond = expression.args.get("false") 3627 if else_cond: 3628 case.else_(else_cond, copy=False) 3629 3630 return self.sql(case)
3632 def comprehension_sql(self, expression: exp.Comprehension) -> str: 3633 this = self.sql(expression, "this") 3634 expr = self.sql(expression, "expression") 3635 iterator = self.sql(expression, "iterator") 3636 condition = self.sql(expression, "condition") 3637 condition = f" IF {condition}" if condition else "" 3638 return f"{this} FOR {expr} IN {iterator}{condition}"
3646 def predict_sql(self, expression: exp.Predict) -> str: 3647 model = self.sql(expression, "this") 3648 model = f"MODEL {model}" 3649 table = self.sql(expression, "expression") 3650 table = f"TABLE {table}" if not isinstance(expression.expression, exp.Subquery) else table 3651 parameters = self.sql(expression, "params_struct") 3652 return self.func("PREDICT", model, table, parameters or None)
3667 def toarray_sql(self, expression: exp.ToArray) -> str: 3668 arg = expression.this 3669 if not arg.type: 3670 from sqlglot.optimizer.annotate_types import annotate_types 3671 3672 arg = annotate_types(arg) 3673 3674 if arg.is_type(exp.DataType.Type.ARRAY): 3675 return self.sql(arg) 3676 3677 cond_for_null = arg.is_(exp.null()) 3678 return self.sql(exp.func("IF", cond_for_null, exp.null(), exp.array(arg, copy=False)))
3694 def tsordstodate_sql(self, expression: exp.TsOrDsToDate) -> str: 3695 this = expression.this 3696 time_format = self.format_time(expression) 3697 3698 if time_format and time_format not in (self.dialect.TIME_FORMAT, self.dialect.DATE_FORMAT): 3699 return self.sql( 3700 exp.cast( 3701 exp.StrToTime(this=this, format=expression.args["format"]), 3702 exp.DataType.Type.DATE, 3703 ) 3704 ) 3705 3706 if isinstance(this, exp.TsOrDsToDate) or this.is_type(exp.DataType.Type.DATE): 3707 return self.sql(this) 3708 3709 return self.sql(exp.cast(this, exp.DataType.Type.DATE))
3721 def lastday_sql(self, expression: exp.LastDay) -> str: 3722 if self.LAST_DAY_SUPPORTS_DATE_PART: 3723 return self.function_fallback_sql(expression) 3724 3725 unit = expression.text("unit") 3726 if unit and unit != "MONTH": 3727 self.unsupported("Date parts are not supported in LAST_DAY.") 3728 3729 return self.func("LAST_DAY", expression.this)
3738 def arrayany_sql(self, expression: exp.ArrayAny) -> str: 3739 if self.CAN_IMPLEMENT_ARRAY_ANY: 3740 filtered = exp.ArrayFilter(this=expression.this, expression=expression.expression) 3741 filtered_not_empty = exp.ArraySize(this=filtered).neq(0) 3742 original_is_empty = exp.ArraySize(this=expression.this).eq(0) 3743 return self.sql(exp.paren(original_is_empty.or_(filtered_not_empty))) 3744 3745 from sqlglot.dialects import Dialect 3746 3747 # SQLGlot's executor supports ARRAY_ANY, so we don't wanna warn for the SQLGlot dialect 3748 if self.dialect.__class__ != Dialect: 3749 self.unsupported("ARRAY_ANY is unsupported") 3750 3751 return self.function_fallback_sql(expression)
3753 def struct_sql(self, expression: exp.Struct) -> str: 3754 expression.set( 3755 "expressions", 3756 [ 3757 exp.alias_(e.expression, e.name if e.this.is_string else e.this) 3758 if isinstance(e, exp.PropertyEQ) 3759 else e 3760 for e in expression.expressions 3761 ], 3762 ) 3763 3764 return self.function_fallback_sql(expression)
3772 def truncatetable_sql(self, expression: exp.TruncateTable) -> str: 3773 target = "DATABASE" if expression.args.get("is_database") else "TABLE" 3774 tables = f" {self.expressions(expression)}" 3775 3776 exists = " IF EXISTS" if expression.args.get("exists") else "" 3777 3778 on_cluster = self.sql(expression, "cluster") 3779 on_cluster = f" {on_cluster}" if on_cluster else "" 3780 3781 identity = self.sql(expression, "identity") 3782 identity = f" {identity} IDENTITY" if identity else "" 3783 3784 option = self.sql(expression, "option") 3785 option = f" {option}" if option else "" 3786 3787 partition = self.sql(expression, "partition") 3788 partition = f" {partition}" if partition else "" 3789 3790 return f"TRUNCATE {target}{exists}{tables}{on_cluster}{identity}{option}{partition}"
3794 def convert_sql(self, expression: exp.Convert) -> str: 3795 to = expression.this 3796 value = expression.expression 3797 style = expression.args.get("style") 3798 safe = expression.args.get("safe") 3799 strict = expression.args.get("strict") 3800 3801 if not to or not value: 3802 return "" 3803 3804 # Retrieve length of datatype and override to default if not specified 3805 if not seq_get(to.expressions, 0) and to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3806 to = exp.DataType.build(to.this, expressions=[exp.Literal.number(30)], nested=False) 3807 3808 transformed: t.Optional[exp.Expression] = None 3809 cast = exp.Cast if strict else exp.TryCast 3810 3811 # Check whether a conversion with format (T-SQL calls this 'style') is applicable 3812 if isinstance(style, exp.Literal) and style.is_int: 3813 from sqlglot.dialects.tsql import TSQL 3814 3815 style_value = style.name 3816 converted_style = TSQL.CONVERT_FORMAT_MAPPING.get(style_value) 3817 if not converted_style: 3818 self.unsupported(f"Unsupported T-SQL 'style' value: {style_value}") 3819 3820 fmt = exp.Literal.string(converted_style) 3821 3822 if to.this == exp.DataType.Type.DATE: 3823 transformed = exp.StrToDate(this=value, format=fmt) 3824 elif to.this == exp.DataType.Type.DATETIME: 3825 transformed = exp.StrToTime(this=value, format=fmt) 3826 elif to.this in self.PARAMETERIZABLE_TEXT_TYPES: 3827 transformed = cast(this=exp.TimeToStr(this=value, format=fmt), to=to, safe=safe) 3828 elif to.this == exp.DataType.Type.TEXT: 3829 transformed = exp.TimeToStr(this=value, format=fmt) 3830 3831 if not transformed: 3832 transformed = cast(this=value, to=to, safe=safe) 3833 3834 return self.sql(transformed)
3890 def copyparameter_sql(self, expression: exp.CopyParameter) -> str: 3891 option = self.sql(expression, "this") 3892 3893 if expression.expressions: 3894 upper = option.upper() 3895 3896 # Snowflake FILE_FORMAT options are separated by whitespace 3897 sep = " " if upper == "FILE_FORMAT" else ", " 3898 3899 # Databricks copy/format options do not set their list of values with EQ 3900 op = " " if upper in ("COPY_OPTIONS", "FORMAT_OPTIONS") else " = " 3901 values = self.expressions(expression, flat=True, sep=sep) 3902 return f"{option}{op}({values})" 3903 3904 value = self.sql(expression, "expression") 3905 3906 if not value: 3907 return option 3908 3909 op = " = " if self.COPY_PARAMS_EQ_REQUIRED else " " 3910 3911 return f"{option}{op}{value}"
3913 def credentials_sql(self, expression: exp.Credentials) -> str: 3914 cred_expr = expression.args.get("credentials") 3915 if isinstance(cred_expr, exp.Literal): 3916 # Redshift case: CREDENTIALS <string> 3917 credentials = self.sql(expression, "credentials") 3918 credentials = f"CREDENTIALS {credentials}" if credentials else "" 3919 else: 3920 # Snowflake case: CREDENTIALS = (...) 3921 credentials = self.expressions(expression, key="credentials", flat=True, sep=" ") 3922 credentials = f"CREDENTIALS = ({credentials})" if cred_expr is not None else "" 3923 3924 storage = self.sql(expression, "storage") 3925 storage = f"STORAGE_INTEGRATION = {storage}" if storage else "" 3926 3927 encryption = self.expressions(expression, key="encryption", flat=True, sep=" ") 3928 encryption = f" ENCRYPTION = ({encryption})" if encryption else "" 3929 3930 iam_role = self.sql(expression, "iam_role") 3931 iam_role = f"IAM_ROLE {iam_role}" if iam_role else "" 3932 3933 region = self.sql(expression, "region") 3934 region = f" REGION {region}" if region else "" 3935 3936 return f"{credentials}{storage}{encryption}{iam_role}{region}"
3938 def copy_sql(self, expression: exp.Copy) -> str: 3939 this = self.sql(expression, "this") 3940 this = f" INTO {this}" if self.COPY_HAS_INTO_KEYWORD else f" {this}" 3941 3942 credentials = self.sql(expression, "credentials") 3943 credentials = self.seg(credentials) if credentials else "" 3944 kind = self.seg("FROM" if expression.args.get("kind") else "TO") 3945 files = self.expressions(expression, key="files", flat=True) 3946 3947 sep = ", " if self.dialect.COPY_PARAMS_ARE_CSV else " " 3948 params = self.expressions( 3949 expression, 3950 key="params", 3951 sep=sep, 3952 new_line=True, 3953 skip_last=True, 3954 skip_first=True, 3955 indent=self.COPY_PARAMS_ARE_WRAPPED, 3956 ) 3957 3958 if params: 3959 if self.COPY_PARAMS_ARE_WRAPPED: 3960 params = f" WITH ({params})" 3961 elif not self.pretty: 3962 params = f" {params}" 3963 3964 return f"COPY{this}{kind} {files}{credentials}{params}"
3969 def datadeletionproperty_sql(self, expression: exp.DataDeletionProperty) -> str: 3970 on_sql = "ON" if expression.args.get("on") else "OFF" 3971 filter_col: t.Optional[str] = self.sql(expression, "filter_column") 3972 filter_col = f"FILTER_COLUMN={filter_col}" if filter_col else None 3973 retention_period: t.Optional[str] = self.sql(expression, "retention_period") 3974 retention_period = f"RETENTION_PERIOD={retention_period}" if retention_period else None 3975 3976 if filter_col or retention_period: 3977 on_sql = self.func("ON", filter_col, retention_period) 3978 3979 return f"DATA_DELETION={on_sql}"
def
maskingpolicycolumnconstraint_sql( self, expression: sqlglot.expressions.MaskingPolicyColumnConstraint) -> str:
3981 def maskingpolicycolumnconstraint_sql( 3982 self, expression: exp.MaskingPolicyColumnConstraint 3983 ) -> str: 3984 this = self.sql(expression, "this") 3985 expressions = self.expressions(expression, flat=True) 3986 expressions = f" USING ({expressions})" if expressions else "" 3987 return f"MASKING POLICY {this}{expressions}"
3997 def scoperesolution_sql(self, expression: exp.ScopeResolution) -> str: 3998 this = self.sql(expression, "this") 3999 expr = expression.expression 4000 4001 if isinstance(expr, exp.Func): 4002 # T-SQL's CLR functions are case sensitive 4003 expr = f"{self.sql(expr, 'this')}({self.format_args(*expr.expressions)})" 4004 else: 4005 expr = self.sql(expression, "expression") 4006 4007 return self.scope_resolution(expr, this)
4015 def rand_sql(self, expression: exp.Rand) -> str: 4016 lower = self.sql(expression, "lower") 4017 upper = self.sql(expression, "upper") 4018 4019 if lower and upper: 4020 return f"({upper} - {lower}) * {self.func('RAND', expression.this)} + {lower}" 4021 return self.func("RAND", expression.this)
4023 def changes_sql(self, expression: exp.Changes) -> str: 4024 information = self.sql(expression, "information") 4025 information = f"INFORMATION => {information}" 4026 at_before = self.sql(expression, "at_before") 4027 at_before = f"{self.seg('')}{at_before}" if at_before else "" 4028 end = self.sql(expression, "end") 4029 end = f"{self.seg('')}{end}" if end else "" 4030 4031 return f"CHANGES ({information}){at_before}{end}"
4033 def pad_sql(self, expression: exp.Pad) -> str: 4034 prefix = "L" if expression.args.get("is_left") else "R" 4035 4036 fill_pattern = self.sql(expression, "fill_pattern") or None 4037 if not fill_pattern and self.PAD_FILL_PATTERN_IS_REQUIRED: 4038 fill_pattern = "' '" 4039 4040 return self.func(f"{prefix}PAD", expression.this, expression.expression, fill_pattern)