sqlglot.dialects.redshift
1from __future__ import annotations 2 3import typing as t 4 5from sqlglot import exp, transforms 6from sqlglot.dialects.dialect import ( 7 NormalizationStrategy, 8 concat_to_dpipe_sql, 9 concat_ws_to_dpipe_sql, 10 date_delta_sql, 11 generatedasidentitycolumnconstraint_sql, 12 json_extract_segments, 13 no_tablesample_sql, 14 rename_func, 15 map_date_part, 16) 17from sqlglot.dialects.postgres import Postgres 18from sqlglot.helper import seq_get 19from sqlglot.tokens import TokenType 20 21if t.TYPE_CHECKING: 22 from sqlglot._typing import E 23 24 25def _build_date_delta(expr_type: t.Type[E]) -> t.Callable[[t.List], E]: 26 def _builder(args: t.List) -> E: 27 expr = expr_type( 28 this=seq_get(args, 2), 29 expression=seq_get(args, 1), 30 unit=map_date_part(seq_get(args, 0)), 31 ) 32 if expr_type is exp.TsOrDsAdd: 33 expr.set("return_type", exp.DataType.build("TIMESTAMP")) 34 35 return expr 36 37 return _builder 38 39 40class Redshift(Postgres): 41 # https://docs.aws.amazon.com/redshift/latest/dg/r_names.html 42 NORMALIZATION_STRATEGY = NormalizationStrategy.CASE_INSENSITIVE 43 44 SUPPORTS_USER_DEFINED_TYPES = False 45 INDEX_OFFSET = 0 46 COPY_PARAMS_ARE_CSV = False 47 HEX_LOWERCASE = True 48 HAS_DISTINCT_ARRAY_CONSTRUCTORS = True 49 50 TIME_FORMAT = "'YYYY-MM-DD HH:MI:SS'" 51 TIME_MAPPING = { 52 **Postgres.TIME_MAPPING, 53 "MON": "%b", 54 "HH": "%H", 55 } 56 57 class Parser(Postgres.Parser): 58 FUNCTIONS = { 59 **Postgres.Parser.FUNCTIONS, 60 "ADD_MONTHS": lambda args: exp.TsOrDsAdd( 61 this=seq_get(args, 0), 62 expression=seq_get(args, 1), 63 unit=exp.var("month"), 64 return_type=exp.DataType.build("TIMESTAMP"), 65 ), 66 "DATEADD": _build_date_delta(exp.TsOrDsAdd), 67 "DATE_ADD": _build_date_delta(exp.TsOrDsAdd), 68 "DATEDIFF": _build_date_delta(exp.TsOrDsDiff), 69 "DATE_DIFF": _build_date_delta(exp.TsOrDsDiff), 70 "GETDATE": exp.CurrentTimestamp.from_arg_list, 71 "LISTAGG": exp.GroupConcat.from_arg_list, 72 "SPLIT_TO_ARRAY": lambda args: exp.StringToArray( 73 this=seq_get(args, 0), expression=seq_get(args, 1) or exp.Literal.string(",") 74 ), 75 "STRTOL": exp.FromBase.from_arg_list, 76 } 77 78 NO_PAREN_FUNCTION_PARSERS = { 79 **Postgres.Parser.NO_PAREN_FUNCTION_PARSERS, 80 "APPROXIMATE": lambda self: self._parse_approximate_count(), 81 "SYSDATE": lambda self: self.expression(exp.CurrentTimestamp, transaction=True), 82 } 83 84 SUPPORTS_IMPLICIT_UNNEST = True 85 86 def _parse_table( 87 self, 88 schema: bool = False, 89 joins: bool = False, 90 alias_tokens: t.Optional[t.Collection[TokenType]] = None, 91 parse_bracket: bool = False, 92 is_db_reference: bool = False, 93 parse_partition: bool = False, 94 ) -> t.Optional[exp.Expression]: 95 # Redshift supports UNPIVOTing SUPER objects, e.g. `UNPIVOT foo.obj[0] AS val AT attr` 96 unpivot = self._match(TokenType.UNPIVOT) 97 table = super()._parse_table( 98 schema=schema, 99 joins=joins, 100 alias_tokens=alias_tokens, 101 parse_bracket=parse_bracket, 102 is_db_reference=is_db_reference, 103 ) 104 105 return self.expression(exp.Pivot, this=table, unpivot=True) if unpivot else table 106 107 def _parse_convert( 108 self, strict: bool, safe: t.Optional[bool] = None 109 ) -> t.Optional[exp.Expression]: 110 to = self._parse_types() 111 self._match(TokenType.COMMA) 112 this = self._parse_bitwise() 113 return self.expression(exp.TryCast, this=this, to=to, safe=safe) 114 115 def _parse_approximate_count(self) -> t.Optional[exp.ApproxDistinct]: 116 index = self._index - 1 117 func = self._parse_function() 118 119 if isinstance(func, exp.Count) and isinstance(func.this, exp.Distinct): 120 return self.expression(exp.ApproxDistinct, this=seq_get(func.this.expressions, 0)) 121 self._retreat(index) 122 return None 123 124 class Tokenizer(Postgres.Tokenizer): 125 BIT_STRINGS = [] 126 HEX_STRINGS = [] 127 STRING_ESCAPES = ["\\", "'"] 128 129 KEYWORDS = { 130 **Postgres.Tokenizer.KEYWORDS, 131 "(+)": TokenType.JOIN_MARKER, 132 "HLLSKETCH": TokenType.HLLSKETCH, 133 "MINUS": TokenType.EXCEPT, 134 "SUPER": TokenType.SUPER, 135 "TOP": TokenType.TOP, 136 "UNLOAD": TokenType.COMMAND, 137 "VARBYTE": TokenType.VARBINARY, 138 } 139 KEYWORDS.pop("VALUES") 140 141 # Redshift allows # to appear as a table identifier prefix 142 SINGLE_TOKENS = Postgres.Tokenizer.SINGLE_TOKENS.copy() 143 SINGLE_TOKENS.pop("#") 144 145 class Generator(Postgres.Generator): 146 LOCKING_READS_SUPPORTED = False 147 QUERY_HINTS = False 148 VALUES_AS_TABLE = False 149 TZ_TO_WITH_TIME_ZONE = True 150 NVL2_SUPPORTED = True 151 LAST_DAY_SUPPORTS_DATE_PART = False 152 CAN_IMPLEMENT_ARRAY_ANY = False 153 MULTI_ARG_DISTINCT = True 154 COPY_PARAMS_ARE_WRAPPED = False 155 HEX_FUNC = "TO_HEX" 156 PARSE_JSON_NAME = "JSON_PARSE" 157 ARRAY_CONCAT_IS_VAR_LEN = False 158 159 # Redshift doesn't have `WITH` as part of their with_properties so we remove it 160 WITH_PROPERTIES_PREFIX = " " 161 162 TYPE_MAPPING = { 163 **Postgres.Generator.TYPE_MAPPING, 164 exp.DataType.Type.BINARY: "VARBYTE", 165 exp.DataType.Type.INT: "INTEGER", 166 exp.DataType.Type.TIMETZ: "TIME", 167 exp.DataType.Type.TIMESTAMPTZ: "TIMESTAMP", 168 exp.DataType.Type.VARBINARY: "VARBYTE", 169 exp.DataType.Type.ROWVERSION: "VARBYTE", 170 } 171 172 TRANSFORMS = { 173 **Postgres.Generator.TRANSFORMS, 174 exp.ArrayConcat: lambda self, e: self.arrayconcat_sql(e, name="ARRAY_CONCAT"), 175 exp.Concat: concat_to_dpipe_sql, 176 exp.ConcatWs: concat_ws_to_dpipe_sql, 177 exp.ApproxDistinct: lambda self, 178 e: f"APPROXIMATE COUNT(DISTINCT {self.sql(e, 'this')})", 179 exp.CurrentTimestamp: lambda self, e: ( 180 "SYSDATE" if e.args.get("transaction") else "GETDATE()" 181 ), 182 exp.DateAdd: date_delta_sql("DATEADD"), 183 exp.DateDiff: date_delta_sql("DATEDIFF"), 184 exp.DistKeyProperty: lambda self, e: self.func("DISTKEY", e.this), 185 exp.DistStyleProperty: lambda self, e: self.naked_property(e), 186 exp.FromBase: rename_func("STRTOL"), 187 exp.GeneratedAsIdentityColumnConstraint: generatedasidentitycolumnconstraint_sql, 188 exp.JSONExtract: json_extract_segments("JSON_EXTRACT_PATH_TEXT"), 189 exp.JSONExtractScalar: json_extract_segments("JSON_EXTRACT_PATH_TEXT"), 190 exp.GroupConcat: rename_func("LISTAGG"), 191 exp.Hex: lambda self, e: self.func("UPPER", self.func("TO_HEX", self.sql(e, "this"))), 192 exp.Select: transforms.preprocess( 193 [ 194 transforms.eliminate_distinct_on, 195 transforms.eliminate_semi_and_anti_joins, 196 transforms.unqualify_unnest, 197 ] 198 ), 199 exp.SortKeyProperty: lambda self, 200 e: f"{'COMPOUND ' if e.args['compound'] else ''}SORTKEY({self.format_args(*e.this)})", 201 exp.StartsWith: lambda self, 202 e: f"{self.sql(e.this)} LIKE {self.sql(e.expression)} || '%'", 203 exp.StringToArray: rename_func("SPLIT_TO_ARRAY"), 204 exp.TableSample: no_tablesample_sql, 205 exp.TsOrDsAdd: date_delta_sql("DATEADD"), 206 exp.TsOrDsDiff: date_delta_sql("DATEDIFF"), 207 exp.UnixToTime: lambda self, 208 e: f"(TIMESTAMP 'epoch' + {self.sql(e.this)} * INTERVAL '1 SECOND')", 209 } 210 211 # Postgres maps exp.Pivot to no_pivot_sql, but Redshift support pivots 212 TRANSFORMS.pop(exp.Pivot) 213 214 # Postgres doesn't support JSON_PARSE, but Redshift does 215 TRANSFORMS.pop(exp.ParseJSON) 216 217 # Redshift uses the POW | POWER (expr1, expr2) syntax instead of expr1 ^ expr2 (postgres) 218 TRANSFORMS.pop(exp.Pow) 219 220 # Redshift supports these functions 221 TRANSFORMS.pop(exp.AnyValue) 222 TRANSFORMS.pop(exp.LastDay) 223 TRANSFORMS.pop(exp.SHA2) 224 225 RESERVED_KEYWORDS = { 226 "aes128", 227 "aes256", 228 "all", 229 "allowoverwrite", 230 "analyse", 231 "analyze", 232 "and", 233 "any", 234 "array", 235 "as", 236 "asc", 237 "authorization", 238 "az64", 239 "backup", 240 "between", 241 "binary", 242 "blanksasnull", 243 "both", 244 "bytedict", 245 "bzip2", 246 "case", 247 "cast", 248 "check", 249 "collate", 250 "column", 251 "constraint", 252 "create", 253 "credentials", 254 "cross", 255 "current_date", 256 "current_time", 257 "current_timestamp", 258 "current_user", 259 "current_user_id", 260 "default", 261 "deferrable", 262 "deflate", 263 "defrag", 264 "delta", 265 "delta32k", 266 "desc", 267 "disable", 268 "distinct", 269 "do", 270 "else", 271 "emptyasnull", 272 "enable", 273 "encode", 274 "encrypt ", 275 "encryption", 276 "end", 277 "except", 278 "explicit", 279 "false", 280 "for", 281 "foreign", 282 "freeze", 283 "from", 284 "full", 285 "globaldict256", 286 "globaldict64k", 287 "grant", 288 "group", 289 "gzip", 290 "having", 291 "identity", 292 "ignore", 293 "ilike", 294 "in", 295 "initially", 296 "inner", 297 "intersect", 298 "interval", 299 "into", 300 "is", 301 "isnull", 302 "join", 303 "leading", 304 "left", 305 "like", 306 "limit", 307 "localtime", 308 "localtimestamp", 309 "lun", 310 "luns", 311 "lzo", 312 "lzop", 313 "minus", 314 "mostly16", 315 "mostly32", 316 "mostly8", 317 "natural", 318 "new", 319 "not", 320 "notnull", 321 "null", 322 "nulls", 323 "off", 324 "offline", 325 "offset", 326 "oid", 327 "old", 328 "on", 329 "only", 330 "open", 331 "or", 332 "order", 333 "outer", 334 "overlaps", 335 "parallel", 336 "partition", 337 "percent", 338 "permissions", 339 "pivot", 340 "placing", 341 "primary", 342 "raw", 343 "readratio", 344 "recover", 345 "references", 346 "rejectlog", 347 "resort", 348 "respect", 349 "restore", 350 "right", 351 "select", 352 "session_user", 353 "similar", 354 "snapshot", 355 "some", 356 "sysdate", 357 "system", 358 "table", 359 "tag", 360 "tdes", 361 "text255", 362 "text32k", 363 "then", 364 "timestamp", 365 "to", 366 "top", 367 "trailing", 368 "true", 369 "truncatecolumns", 370 "type", 371 "union", 372 "unique", 373 "unnest", 374 "unpivot", 375 "user", 376 "using", 377 "verbose", 378 "wallet", 379 "when", 380 "where", 381 "with", 382 "without", 383 } 384 385 def unnest_sql(self, expression: exp.Unnest) -> str: 386 args = expression.expressions 387 num_args = len(args) 388 389 if num_args > 1: 390 self.unsupported(f"Unsupported number of arguments in UNNEST: {num_args}") 391 return "" 392 393 arg = self.sql(seq_get(args, 0)) 394 alias = self.expressions(expression.args.get("alias"), key="columns", flat=True) 395 return f"{arg} AS {alias}" if alias else arg 396 397 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 398 if expression.is_type(exp.DataType.Type.JSON): 399 # Redshift doesn't support a JSON type, so casting to it is treated as a noop 400 return self.sql(expression, "this") 401 402 return super().cast_sql(expression, safe_prefix=safe_prefix) 403 404 def datatype_sql(self, expression: exp.DataType) -> str: 405 """ 406 Redshift converts the `TEXT` data type to `VARCHAR(255)` by default when people more generally mean 407 VARCHAR of max length which is `VARCHAR(max)` in Redshift. Therefore if we get a `TEXT` data type 408 without precision we convert it to `VARCHAR(max)` and if it does have precision then we just convert 409 `TEXT` to `VARCHAR`. 410 """ 411 if expression.is_type("text"): 412 expression.set("this", exp.DataType.Type.VARCHAR) 413 precision = expression.args.get("expressions") 414 415 if not precision: 416 expression.append("expressions", exp.var("MAX")) 417 418 return super().datatype_sql(expression) 419 420 def alterset_sql(self, expression: exp.AlterSet) -> str: 421 exprs = self.expressions(expression, flat=True) 422 exprs = f" TABLE PROPERTIES ({exprs})" if exprs else "" 423 location = self.sql(expression, "location") 424 location = f" LOCATION {location}" if location else "" 425 file_format = self.expressions(expression, key="file_format", flat=True, sep=" ") 426 file_format = f" FILE FORMAT {file_format}" if file_format else "" 427 428 return f"SET{exprs}{location}{file_format}" 429 430 def array_sql(self, expression: exp.Array) -> str: 431 if expression.args.get("bracket_notation"): 432 return super().array_sql(expression) 433 434 return rename_func("ARRAY")(self, expression)
41class Redshift(Postgres): 42 # https://docs.aws.amazon.com/redshift/latest/dg/r_names.html 43 NORMALIZATION_STRATEGY = NormalizationStrategy.CASE_INSENSITIVE 44 45 SUPPORTS_USER_DEFINED_TYPES = False 46 INDEX_OFFSET = 0 47 COPY_PARAMS_ARE_CSV = False 48 HEX_LOWERCASE = True 49 HAS_DISTINCT_ARRAY_CONSTRUCTORS = True 50 51 TIME_FORMAT = "'YYYY-MM-DD HH:MI:SS'" 52 TIME_MAPPING = { 53 **Postgres.TIME_MAPPING, 54 "MON": "%b", 55 "HH": "%H", 56 } 57 58 class Parser(Postgres.Parser): 59 FUNCTIONS = { 60 **Postgres.Parser.FUNCTIONS, 61 "ADD_MONTHS": lambda args: exp.TsOrDsAdd( 62 this=seq_get(args, 0), 63 expression=seq_get(args, 1), 64 unit=exp.var("month"), 65 return_type=exp.DataType.build("TIMESTAMP"), 66 ), 67 "DATEADD": _build_date_delta(exp.TsOrDsAdd), 68 "DATE_ADD": _build_date_delta(exp.TsOrDsAdd), 69 "DATEDIFF": _build_date_delta(exp.TsOrDsDiff), 70 "DATE_DIFF": _build_date_delta(exp.TsOrDsDiff), 71 "GETDATE": exp.CurrentTimestamp.from_arg_list, 72 "LISTAGG": exp.GroupConcat.from_arg_list, 73 "SPLIT_TO_ARRAY": lambda args: exp.StringToArray( 74 this=seq_get(args, 0), expression=seq_get(args, 1) or exp.Literal.string(",") 75 ), 76 "STRTOL": exp.FromBase.from_arg_list, 77 } 78 79 NO_PAREN_FUNCTION_PARSERS = { 80 **Postgres.Parser.NO_PAREN_FUNCTION_PARSERS, 81 "APPROXIMATE": lambda self: self._parse_approximate_count(), 82 "SYSDATE": lambda self: self.expression(exp.CurrentTimestamp, transaction=True), 83 } 84 85 SUPPORTS_IMPLICIT_UNNEST = True 86 87 def _parse_table( 88 self, 89 schema: bool = False, 90 joins: bool = False, 91 alias_tokens: t.Optional[t.Collection[TokenType]] = None, 92 parse_bracket: bool = False, 93 is_db_reference: bool = False, 94 parse_partition: bool = False, 95 ) -> t.Optional[exp.Expression]: 96 # Redshift supports UNPIVOTing SUPER objects, e.g. `UNPIVOT foo.obj[0] AS val AT attr` 97 unpivot = self._match(TokenType.UNPIVOT) 98 table = super()._parse_table( 99 schema=schema, 100 joins=joins, 101 alias_tokens=alias_tokens, 102 parse_bracket=parse_bracket, 103 is_db_reference=is_db_reference, 104 ) 105 106 return self.expression(exp.Pivot, this=table, unpivot=True) if unpivot else table 107 108 def _parse_convert( 109 self, strict: bool, safe: t.Optional[bool] = None 110 ) -> t.Optional[exp.Expression]: 111 to = self._parse_types() 112 self._match(TokenType.COMMA) 113 this = self._parse_bitwise() 114 return self.expression(exp.TryCast, this=this, to=to, safe=safe) 115 116 def _parse_approximate_count(self) -> t.Optional[exp.ApproxDistinct]: 117 index = self._index - 1 118 func = self._parse_function() 119 120 if isinstance(func, exp.Count) and isinstance(func.this, exp.Distinct): 121 return self.expression(exp.ApproxDistinct, this=seq_get(func.this.expressions, 0)) 122 self._retreat(index) 123 return None 124 125 class Tokenizer(Postgres.Tokenizer): 126 BIT_STRINGS = [] 127 HEX_STRINGS = [] 128 STRING_ESCAPES = ["\\", "'"] 129 130 KEYWORDS = { 131 **Postgres.Tokenizer.KEYWORDS, 132 "(+)": TokenType.JOIN_MARKER, 133 "HLLSKETCH": TokenType.HLLSKETCH, 134 "MINUS": TokenType.EXCEPT, 135 "SUPER": TokenType.SUPER, 136 "TOP": TokenType.TOP, 137 "UNLOAD": TokenType.COMMAND, 138 "VARBYTE": TokenType.VARBINARY, 139 } 140 KEYWORDS.pop("VALUES") 141 142 # Redshift allows # to appear as a table identifier prefix 143 SINGLE_TOKENS = Postgres.Tokenizer.SINGLE_TOKENS.copy() 144 SINGLE_TOKENS.pop("#") 145 146 class Generator(Postgres.Generator): 147 LOCKING_READS_SUPPORTED = False 148 QUERY_HINTS = False 149 VALUES_AS_TABLE = False 150 TZ_TO_WITH_TIME_ZONE = True 151 NVL2_SUPPORTED = True 152 LAST_DAY_SUPPORTS_DATE_PART = False 153 CAN_IMPLEMENT_ARRAY_ANY = False 154 MULTI_ARG_DISTINCT = True 155 COPY_PARAMS_ARE_WRAPPED = False 156 HEX_FUNC = "TO_HEX" 157 PARSE_JSON_NAME = "JSON_PARSE" 158 ARRAY_CONCAT_IS_VAR_LEN = False 159 160 # Redshift doesn't have `WITH` as part of their with_properties so we remove it 161 WITH_PROPERTIES_PREFIX = " " 162 163 TYPE_MAPPING = { 164 **Postgres.Generator.TYPE_MAPPING, 165 exp.DataType.Type.BINARY: "VARBYTE", 166 exp.DataType.Type.INT: "INTEGER", 167 exp.DataType.Type.TIMETZ: "TIME", 168 exp.DataType.Type.TIMESTAMPTZ: "TIMESTAMP", 169 exp.DataType.Type.VARBINARY: "VARBYTE", 170 exp.DataType.Type.ROWVERSION: "VARBYTE", 171 } 172 173 TRANSFORMS = { 174 **Postgres.Generator.TRANSFORMS, 175 exp.ArrayConcat: lambda self, e: self.arrayconcat_sql(e, name="ARRAY_CONCAT"), 176 exp.Concat: concat_to_dpipe_sql, 177 exp.ConcatWs: concat_ws_to_dpipe_sql, 178 exp.ApproxDistinct: lambda self, 179 e: f"APPROXIMATE COUNT(DISTINCT {self.sql(e, 'this')})", 180 exp.CurrentTimestamp: lambda self, e: ( 181 "SYSDATE" if e.args.get("transaction") else "GETDATE()" 182 ), 183 exp.DateAdd: date_delta_sql("DATEADD"), 184 exp.DateDiff: date_delta_sql("DATEDIFF"), 185 exp.DistKeyProperty: lambda self, e: self.func("DISTKEY", e.this), 186 exp.DistStyleProperty: lambda self, e: self.naked_property(e), 187 exp.FromBase: rename_func("STRTOL"), 188 exp.GeneratedAsIdentityColumnConstraint: generatedasidentitycolumnconstraint_sql, 189 exp.JSONExtract: json_extract_segments("JSON_EXTRACT_PATH_TEXT"), 190 exp.JSONExtractScalar: json_extract_segments("JSON_EXTRACT_PATH_TEXT"), 191 exp.GroupConcat: rename_func("LISTAGG"), 192 exp.Hex: lambda self, e: self.func("UPPER", self.func("TO_HEX", self.sql(e, "this"))), 193 exp.Select: transforms.preprocess( 194 [ 195 transforms.eliminate_distinct_on, 196 transforms.eliminate_semi_and_anti_joins, 197 transforms.unqualify_unnest, 198 ] 199 ), 200 exp.SortKeyProperty: lambda self, 201 e: f"{'COMPOUND ' if e.args['compound'] else ''}SORTKEY({self.format_args(*e.this)})", 202 exp.StartsWith: lambda self, 203 e: f"{self.sql(e.this)} LIKE {self.sql(e.expression)} || '%'", 204 exp.StringToArray: rename_func("SPLIT_TO_ARRAY"), 205 exp.TableSample: no_tablesample_sql, 206 exp.TsOrDsAdd: date_delta_sql("DATEADD"), 207 exp.TsOrDsDiff: date_delta_sql("DATEDIFF"), 208 exp.UnixToTime: lambda self, 209 e: f"(TIMESTAMP 'epoch' + {self.sql(e.this)} * INTERVAL '1 SECOND')", 210 } 211 212 # Postgres maps exp.Pivot to no_pivot_sql, but Redshift support pivots 213 TRANSFORMS.pop(exp.Pivot) 214 215 # Postgres doesn't support JSON_PARSE, but Redshift does 216 TRANSFORMS.pop(exp.ParseJSON) 217 218 # Redshift uses the POW | POWER (expr1, expr2) syntax instead of expr1 ^ expr2 (postgres) 219 TRANSFORMS.pop(exp.Pow) 220 221 # Redshift supports these functions 222 TRANSFORMS.pop(exp.AnyValue) 223 TRANSFORMS.pop(exp.LastDay) 224 TRANSFORMS.pop(exp.SHA2) 225 226 RESERVED_KEYWORDS = { 227 "aes128", 228 "aes256", 229 "all", 230 "allowoverwrite", 231 "analyse", 232 "analyze", 233 "and", 234 "any", 235 "array", 236 "as", 237 "asc", 238 "authorization", 239 "az64", 240 "backup", 241 "between", 242 "binary", 243 "blanksasnull", 244 "both", 245 "bytedict", 246 "bzip2", 247 "case", 248 "cast", 249 "check", 250 "collate", 251 "column", 252 "constraint", 253 "create", 254 "credentials", 255 "cross", 256 "current_date", 257 "current_time", 258 "current_timestamp", 259 "current_user", 260 "current_user_id", 261 "default", 262 "deferrable", 263 "deflate", 264 "defrag", 265 "delta", 266 "delta32k", 267 "desc", 268 "disable", 269 "distinct", 270 "do", 271 "else", 272 "emptyasnull", 273 "enable", 274 "encode", 275 "encrypt ", 276 "encryption", 277 "end", 278 "except", 279 "explicit", 280 "false", 281 "for", 282 "foreign", 283 "freeze", 284 "from", 285 "full", 286 "globaldict256", 287 "globaldict64k", 288 "grant", 289 "group", 290 "gzip", 291 "having", 292 "identity", 293 "ignore", 294 "ilike", 295 "in", 296 "initially", 297 "inner", 298 "intersect", 299 "interval", 300 "into", 301 "is", 302 "isnull", 303 "join", 304 "leading", 305 "left", 306 "like", 307 "limit", 308 "localtime", 309 "localtimestamp", 310 "lun", 311 "luns", 312 "lzo", 313 "lzop", 314 "minus", 315 "mostly16", 316 "mostly32", 317 "mostly8", 318 "natural", 319 "new", 320 "not", 321 "notnull", 322 "null", 323 "nulls", 324 "off", 325 "offline", 326 "offset", 327 "oid", 328 "old", 329 "on", 330 "only", 331 "open", 332 "or", 333 "order", 334 "outer", 335 "overlaps", 336 "parallel", 337 "partition", 338 "percent", 339 "permissions", 340 "pivot", 341 "placing", 342 "primary", 343 "raw", 344 "readratio", 345 "recover", 346 "references", 347 "rejectlog", 348 "resort", 349 "respect", 350 "restore", 351 "right", 352 "select", 353 "session_user", 354 "similar", 355 "snapshot", 356 "some", 357 "sysdate", 358 "system", 359 "table", 360 "tag", 361 "tdes", 362 "text255", 363 "text32k", 364 "then", 365 "timestamp", 366 "to", 367 "top", 368 "trailing", 369 "true", 370 "truncatecolumns", 371 "type", 372 "union", 373 "unique", 374 "unnest", 375 "unpivot", 376 "user", 377 "using", 378 "verbose", 379 "wallet", 380 "when", 381 "where", 382 "with", 383 "without", 384 } 385 386 def unnest_sql(self, expression: exp.Unnest) -> str: 387 args = expression.expressions 388 num_args = len(args) 389 390 if num_args > 1: 391 self.unsupported(f"Unsupported number of arguments in UNNEST: {num_args}") 392 return "" 393 394 arg = self.sql(seq_get(args, 0)) 395 alias = self.expressions(expression.args.get("alias"), key="columns", flat=True) 396 return f"{arg} AS {alias}" if alias else arg 397 398 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 399 if expression.is_type(exp.DataType.Type.JSON): 400 # Redshift doesn't support a JSON type, so casting to it is treated as a noop 401 return self.sql(expression, "this") 402 403 return super().cast_sql(expression, safe_prefix=safe_prefix) 404 405 def datatype_sql(self, expression: exp.DataType) -> str: 406 """ 407 Redshift converts the `TEXT` data type to `VARCHAR(255)` by default when people more generally mean 408 VARCHAR of max length which is `VARCHAR(max)` in Redshift. Therefore if we get a `TEXT` data type 409 without precision we convert it to `VARCHAR(max)` and if it does have precision then we just convert 410 `TEXT` to `VARCHAR`. 411 """ 412 if expression.is_type("text"): 413 expression.set("this", exp.DataType.Type.VARCHAR) 414 precision = expression.args.get("expressions") 415 416 if not precision: 417 expression.append("expressions", exp.var("MAX")) 418 419 return super().datatype_sql(expression) 420 421 def alterset_sql(self, expression: exp.AlterSet) -> str: 422 exprs = self.expressions(expression, flat=True) 423 exprs = f" TABLE PROPERTIES ({exprs})" if exprs else "" 424 location = self.sql(expression, "location") 425 location = f" LOCATION {location}" if location else "" 426 file_format = self.expressions(expression, key="file_format", flat=True, sep=" ") 427 file_format = f" FILE FORMAT {file_format}" if file_format else "" 428 429 return f"SET{exprs}{location}{file_format}" 430 431 def array_sql(self, expression: exp.Array) -> str: 432 if expression.args.get("bracket_notation"): 433 return super().array_sql(expression) 434 435 return rename_func("ARRAY")(self, expression)
Specifies the strategy according to which identifiers should be normalized.
Whether the ARRAY constructor is context-sensitive, i.e in Redshift ARRAY[1, 2, 3] != ARRAY(1, 2, 3) as the former is of type INT[] vs the latter which is SUPER
Associates this dialect's time formats with their equivalent Python strftime
formats.
Mapping of an escaped sequence (\n
) to its unescaped version (
).
Inherited Members
- sqlglot.dialects.dialect.Dialect
- Dialect
- WEEK_OFFSET
- UNNEST_COLUMN_ONLY
- ALIAS_POST_TABLESAMPLE
- IDENTIFIERS_CAN_START_WITH_DIGIT
- DPIPE_IS_STRING_CONCAT
- STRICT_STRING_CONCAT
- SUPPORTS_SEMI_ANTI_JOIN
- NORMALIZE_FUNCTIONS
- LOG_BASE_FIRST
- SAFE_DIVISION
- DATE_FORMAT
- DATEINT_FORMAT
- FORMAT_MAPPING
- PSEUDOCOLUMNS
- PREFER_CTE_ALIAS_COLUMN
- FORCE_EARLY_ALIAS_REF_EXPANSION
- EXPAND_ALIAS_REFS_EARLY_ONLY_IN_GROUP_BY
- SUPPORTS_ORDER_BY_ALL
- SUPPORTS_FIXED_SIZE_ARRAYS
- DATE_PART_MAPPING
- TYPE_TO_EXPRESSIONS
- ANNOTATORS
- get_or_raise
- format_time
- settings
- normalize_identifier
- case_sensitive
- can_identify
- quote_identifier
- to_json_path
- parse
- parse_into
- generate
- transpile
- tokenize
- tokenizer
- jsonpath_tokenizer
- parser
- generator
58 class Parser(Postgres.Parser): 59 FUNCTIONS = { 60 **Postgres.Parser.FUNCTIONS, 61 "ADD_MONTHS": lambda args: exp.TsOrDsAdd( 62 this=seq_get(args, 0), 63 expression=seq_get(args, 1), 64 unit=exp.var("month"), 65 return_type=exp.DataType.build("TIMESTAMP"), 66 ), 67 "DATEADD": _build_date_delta(exp.TsOrDsAdd), 68 "DATE_ADD": _build_date_delta(exp.TsOrDsAdd), 69 "DATEDIFF": _build_date_delta(exp.TsOrDsDiff), 70 "DATE_DIFF": _build_date_delta(exp.TsOrDsDiff), 71 "GETDATE": exp.CurrentTimestamp.from_arg_list, 72 "LISTAGG": exp.GroupConcat.from_arg_list, 73 "SPLIT_TO_ARRAY": lambda args: exp.StringToArray( 74 this=seq_get(args, 0), expression=seq_get(args, 1) or exp.Literal.string(",") 75 ), 76 "STRTOL": exp.FromBase.from_arg_list, 77 } 78 79 NO_PAREN_FUNCTION_PARSERS = { 80 **Postgres.Parser.NO_PAREN_FUNCTION_PARSERS, 81 "APPROXIMATE": lambda self: self._parse_approximate_count(), 82 "SYSDATE": lambda self: self.expression(exp.CurrentTimestamp, transaction=True), 83 } 84 85 SUPPORTS_IMPLICIT_UNNEST = True 86 87 def _parse_table( 88 self, 89 schema: bool = False, 90 joins: bool = False, 91 alias_tokens: t.Optional[t.Collection[TokenType]] = None, 92 parse_bracket: bool = False, 93 is_db_reference: bool = False, 94 parse_partition: bool = False, 95 ) -> t.Optional[exp.Expression]: 96 # Redshift supports UNPIVOTing SUPER objects, e.g. `UNPIVOT foo.obj[0] AS val AT attr` 97 unpivot = self._match(TokenType.UNPIVOT) 98 table = super()._parse_table( 99 schema=schema, 100 joins=joins, 101 alias_tokens=alias_tokens, 102 parse_bracket=parse_bracket, 103 is_db_reference=is_db_reference, 104 ) 105 106 return self.expression(exp.Pivot, this=table, unpivot=True) if unpivot else table 107 108 def _parse_convert( 109 self, strict: bool, safe: t.Optional[bool] = None 110 ) -> t.Optional[exp.Expression]: 111 to = self._parse_types() 112 self._match(TokenType.COMMA) 113 this = self._parse_bitwise() 114 return self.expression(exp.TryCast, this=this, to=to, safe=safe) 115 116 def _parse_approximate_count(self) -> t.Optional[exp.ApproxDistinct]: 117 index = self._index - 1 118 func = self._parse_function() 119 120 if isinstance(func, exp.Count) and isinstance(func.this, exp.Distinct): 121 return self.expression(exp.ApproxDistinct, this=seq_get(func.this.expressions, 0)) 122 self._retreat(index) 123 return None
Parser consumes a list of tokens produced by the Tokenizer and produces a parsed syntax tree.
Arguments:
- error_level: The desired error level. Default: ErrorLevel.IMMEDIATE
- error_message_context: The amount of context to capture from a query string when displaying the error message (in number of characters). Default: 100
- max_errors: Maximum number of error messages to include in a raised ParseError. This is only relevant if error_level is ErrorLevel.RAISE. Default: 3
Inherited Members
- sqlglot.parser.Parser
- Parser
- NO_PAREN_FUNCTIONS
- STRUCT_TYPE_TOKENS
- NESTED_TYPE_TOKENS
- ENUM_TYPE_TOKENS
- AGGREGATE_TYPE_TOKENS
- TYPE_TOKENS
- SIGNED_TO_UNSIGNED_TYPE_TOKEN
- SUBQUERY_PREDICATES
- RESERVED_TOKENS
- DB_CREATABLES
- CREATABLES
- INTERVAL_VARS
- ALIAS_TOKENS
- ARRAY_CONSTRUCTORS
- COMMENT_TABLE_ALIAS_TOKENS
- UPDATE_ALIAS_TOKENS
- TRIM_TYPES
- FUNC_TOKENS
- CONJUNCTION
- ASSIGNMENT
- DISJUNCTION
- EQUALITY
- COMPARISON
- TERM
- FACTOR
- TIMES
- TIMESTAMPS
- SET_OPERATIONS
- JOIN_METHODS
- JOIN_SIDES
- JOIN_KINDS
- JOIN_HINTS
- LAMBDAS
- EXPRESSION_PARSERS
- UNARY_PARSERS
- STRING_PARSERS
- NUMERIC_PARSERS
- PRIMARY_PARSERS
- PLACEHOLDER_PARSERS
- CONSTRAINT_PARSERS
- ALTER_PARSERS
- ALTER_ALTER_PARSERS
- SCHEMA_UNNAMED_CONSTRAINTS
- INVALID_FUNC_NAME_TOKENS
- FUNCTIONS_WITH_ALIASED_ARGS
- KEY_VALUE_DEFINITIONS
- QUERY_MODIFIER_PARSERS
- SET_PARSERS
- SHOW_PARSERS
- TYPE_LITERAL_PARSERS
- TYPE_CONVERTERS
- DDL_SELECT_TOKENS
- PRE_VOLATILE_TOKENS
- TRANSACTION_KIND
- TRANSACTION_CHARACTERISTICS
- CONFLICT_ACTIONS
- CREATE_SEQUENCE
- ISOLATED_LOADING_OPTIONS
- USABLES
- CAST_ACTIONS
- SCHEMA_BINDING_OPTIONS
- KEY_CONSTRAINT_OPTIONS
- INSERT_ALTERNATIVES
- CLONE_KEYWORDS
- HISTORICAL_DATA_PREFIX
- HISTORICAL_DATA_KIND
- OPCLASS_FOLLOW_KEYWORDS
- OPTYPE_FOLLOW_TOKENS
- TABLE_INDEX_HINT_TOKENS
- VIEW_ATTRIBUTES
- WINDOW_ALIAS_TOKENS
- WINDOW_BEFORE_PAREN_TOKENS
- WINDOW_SIDES
- JSON_KEY_VALUE_SEPARATOR_TOKENS
- FETCH_TOKENS
- ADD_CONSTRAINT_TOKENS
- DISTINCT_TOKENS
- NULL_TOKENS
- UNNEST_OFFSET_ALIAS_TOKENS
- SELECT_START_TOKENS
- COPY_INTO_VARLEN_OPTIONS
- STRICT_CAST
- PREFIXED_PIVOT_COLUMNS
- IDENTIFY_PIVOT_STRINGS
- LOG_DEFAULTS_TO_LN
- ALTER_TABLE_ADD_REQUIRED_FOR_EACH_COLUMN
- TABLESAMPLE_CSV
- DEFAULT_SAMPLING_METHOD
- SET_REQUIRES_ASSIGNMENT_DELIMITER
- TRIM_PATTERN_FIRST
- STRING_ALIASES
- MODIFIERS_ATTACHED_TO_SET_OP
- SET_OP_MODIFIERS
- NO_PAREN_IF_COMMANDS
- COLON_IS_VARIANT_EXTRACT
- VALUES_FOLLOWED_BY_PAREN
- INTERVAL_SPANS
- SUPPORTS_PARTITION_SELECTION
- error_level
- error_message_context
- max_errors
- dialect
- reset
- parse
- parse_into
- check_errors
- raise_error
- expression
- validate_expression
- errors
- sql
125 class Tokenizer(Postgres.Tokenizer): 126 BIT_STRINGS = [] 127 HEX_STRINGS = [] 128 STRING_ESCAPES = ["\\", "'"] 129 130 KEYWORDS = { 131 **Postgres.Tokenizer.KEYWORDS, 132 "(+)": TokenType.JOIN_MARKER, 133 "HLLSKETCH": TokenType.HLLSKETCH, 134 "MINUS": TokenType.EXCEPT, 135 "SUPER": TokenType.SUPER, 136 "TOP": TokenType.TOP, 137 "UNLOAD": TokenType.COMMAND, 138 "VARBYTE": TokenType.VARBINARY, 139 } 140 KEYWORDS.pop("VALUES") 141 142 # Redshift allows # to appear as a table identifier prefix 143 SINGLE_TOKENS = Postgres.Tokenizer.SINGLE_TOKENS.copy() 144 SINGLE_TOKENS.pop("#")
Inherited Members
146 class Generator(Postgres.Generator): 147 LOCKING_READS_SUPPORTED = False 148 QUERY_HINTS = False 149 VALUES_AS_TABLE = False 150 TZ_TO_WITH_TIME_ZONE = True 151 NVL2_SUPPORTED = True 152 LAST_DAY_SUPPORTS_DATE_PART = False 153 CAN_IMPLEMENT_ARRAY_ANY = False 154 MULTI_ARG_DISTINCT = True 155 COPY_PARAMS_ARE_WRAPPED = False 156 HEX_FUNC = "TO_HEX" 157 PARSE_JSON_NAME = "JSON_PARSE" 158 ARRAY_CONCAT_IS_VAR_LEN = False 159 160 # Redshift doesn't have `WITH` as part of their with_properties so we remove it 161 WITH_PROPERTIES_PREFIX = " " 162 163 TYPE_MAPPING = { 164 **Postgres.Generator.TYPE_MAPPING, 165 exp.DataType.Type.BINARY: "VARBYTE", 166 exp.DataType.Type.INT: "INTEGER", 167 exp.DataType.Type.TIMETZ: "TIME", 168 exp.DataType.Type.TIMESTAMPTZ: "TIMESTAMP", 169 exp.DataType.Type.VARBINARY: "VARBYTE", 170 exp.DataType.Type.ROWVERSION: "VARBYTE", 171 } 172 173 TRANSFORMS = { 174 **Postgres.Generator.TRANSFORMS, 175 exp.ArrayConcat: lambda self, e: self.arrayconcat_sql(e, name="ARRAY_CONCAT"), 176 exp.Concat: concat_to_dpipe_sql, 177 exp.ConcatWs: concat_ws_to_dpipe_sql, 178 exp.ApproxDistinct: lambda self, 179 e: f"APPROXIMATE COUNT(DISTINCT {self.sql(e, 'this')})", 180 exp.CurrentTimestamp: lambda self, e: ( 181 "SYSDATE" if e.args.get("transaction") else "GETDATE()" 182 ), 183 exp.DateAdd: date_delta_sql("DATEADD"), 184 exp.DateDiff: date_delta_sql("DATEDIFF"), 185 exp.DistKeyProperty: lambda self, e: self.func("DISTKEY", e.this), 186 exp.DistStyleProperty: lambda self, e: self.naked_property(e), 187 exp.FromBase: rename_func("STRTOL"), 188 exp.GeneratedAsIdentityColumnConstraint: generatedasidentitycolumnconstraint_sql, 189 exp.JSONExtract: json_extract_segments("JSON_EXTRACT_PATH_TEXT"), 190 exp.JSONExtractScalar: json_extract_segments("JSON_EXTRACT_PATH_TEXT"), 191 exp.GroupConcat: rename_func("LISTAGG"), 192 exp.Hex: lambda self, e: self.func("UPPER", self.func("TO_HEX", self.sql(e, "this"))), 193 exp.Select: transforms.preprocess( 194 [ 195 transforms.eliminate_distinct_on, 196 transforms.eliminate_semi_and_anti_joins, 197 transforms.unqualify_unnest, 198 ] 199 ), 200 exp.SortKeyProperty: lambda self, 201 e: f"{'COMPOUND ' if e.args['compound'] else ''}SORTKEY({self.format_args(*e.this)})", 202 exp.StartsWith: lambda self, 203 e: f"{self.sql(e.this)} LIKE {self.sql(e.expression)} || '%'", 204 exp.StringToArray: rename_func("SPLIT_TO_ARRAY"), 205 exp.TableSample: no_tablesample_sql, 206 exp.TsOrDsAdd: date_delta_sql("DATEADD"), 207 exp.TsOrDsDiff: date_delta_sql("DATEDIFF"), 208 exp.UnixToTime: lambda self, 209 e: f"(TIMESTAMP 'epoch' + {self.sql(e.this)} * INTERVAL '1 SECOND')", 210 } 211 212 # Postgres maps exp.Pivot to no_pivot_sql, but Redshift support pivots 213 TRANSFORMS.pop(exp.Pivot) 214 215 # Postgres doesn't support JSON_PARSE, but Redshift does 216 TRANSFORMS.pop(exp.ParseJSON) 217 218 # Redshift uses the POW | POWER (expr1, expr2) syntax instead of expr1 ^ expr2 (postgres) 219 TRANSFORMS.pop(exp.Pow) 220 221 # Redshift supports these functions 222 TRANSFORMS.pop(exp.AnyValue) 223 TRANSFORMS.pop(exp.LastDay) 224 TRANSFORMS.pop(exp.SHA2) 225 226 RESERVED_KEYWORDS = { 227 "aes128", 228 "aes256", 229 "all", 230 "allowoverwrite", 231 "analyse", 232 "analyze", 233 "and", 234 "any", 235 "array", 236 "as", 237 "asc", 238 "authorization", 239 "az64", 240 "backup", 241 "between", 242 "binary", 243 "blanksasnull", 244 "both", 245 "bytedict", 246 "bzip2", 247 "case", 248 "cast", 249 "check", 250 "collate", 251 "column", 252 "constraint", 253 "create", 254 "credentials", 255 "cross", 256 "current_date", 257 "current_time", 258 "current_timestamp", 259 "current_user", 260 "current_user_id", 261 "default", 262 "deferrable", 263 "deflate", 264 "defrag", 265 "delta", 266 "delta32k", 267 "desc", 268 "disable", 269 "distinct", 270 "do", 271 "else", 272 "emptyasnull", 273 "enable", 274 "encode", 275 "encrypt ", 276 "encryption", 277 "end", 278 "except", 279 "explicit", 280 "false", 281 "for", 282 "foreign", 283 "freeze", 284 "from", 285 "full", 286 "globaldict256", 287 "globaldict64k", 288 "grant", 289 "group", 290 "gzip", 291 "having", 292 "identity", 293 "ignore", 294 "ilike", 295 "in", 296 "initially", 297 "inner", 298 "intersect", 299 "interval", 300 "into", 301 "is", 302 "isnull", 303 "join", 304 "leading", 305 "left", 306 "like", 307 "limit", 308 "localtime", 309 "localtimestamp", 310 "lun", 311 "luns", 312 "lzo", 313 "lzop", 314 "minus", 315 "mostly16", 316 "mostly32", 317 "mostly8", 318 "natural", 319 "new", 320 "not", 321 "notnull", 322 "null", 323 "nulls", 324 "off", 325 "offline", 326 "offset", 327 "oid", 328 "old", 329 "on", 330 "only", 331 "open", 332 "or", 333 "order", 334 "outer", 335 "overlaps", 336 "parallel", 337 "partition", 338 "percent", 339 "permissions", 340 "pivot", 341 "placing", 342 "primary", 343 "raw", 344 "readratio", 345 "recover", 346 "references", 347 "rejectlog", 348 "resort", 349 "respect", 350 "restore", 351 "right", 352 "select", 353 "session_user", 354 "similar", 355 "snapshot", 356 "some", 357 "sysdate", 358 "system", 359 "table", 360 "tag", 361 "tdes", 362 "text255", 363 "text32k", 364 "then", 365 "timestamp", 366 "to", 367 "top", 368 "trailing", 369 "true", 370 "truncatecolumns", 371 "type", 372 "union", 373 "unique", 374 "unnest", 375 "unpivot", 376 "user", 377 "using", 378 "verbose", 379 "wallet", 380 "when", 381 "where", 382 "with", 383 "without", 384 } 385 386 def unnest_sql(self, expression: exp.Unnest) -> str: 387 args = expression.expressions 388 num_args = len(args) 389 390 if num_args > 1: 391 self.unsupported(f"Unsupported number of arguments in UNNEST: {num_args}") 392 return "" 393 394 arg = self.sql(seq_get(args, 0)) 395 alias = self.expressions(expression.args.get("alias"), key="columns", flat=True) 396 return f"{arg} AS {alias}" if alias else arg 397 398 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 399 if expression.is_type(exp.DataType.Type.JSON): 400 # Redshift doesn't support a JSON type, so casting to it is treated as a noop 401 return self.sql(expression, "this") 402 403 return super().cast_sql(expression, safe_prefix=safe_prefix) 404 405 def datatype_sql(self, expression: exp.DataType) -> str: 406 """ 407 Redshift converts the `TEXT` data type to `VARCHAR(255)` by default when people more generally mean 408 VARCHAR of max length which is `VARCHAR(max)` in Redshift. Therefore if we get a `TEXT` data type 409 without precision we convert it to `VARCHAR(max)` and if it does have precision then we just convert 410 `TEXT` to `VARCHAR`. 411 """ 412 if expression.is_type("text"): 413 expression.set("this", exp.DataType.Type.VARCHAR) 414 precision = expression.args.get("expressions") 415 416 if not precision: 417 expression.append("expressions", exp.var("MAX")) 418 419 return super().datatype_sql(expression) 420 421 def alterset_sql(self, expression: exp.AlterSet) -> str: 422 exprs = self.expressions(expression, flat=True) 423 exprs = f" TABLE PROPERTIES ({exprs})" if exprs else "" 424 location = self.sql(expression, "location") 425 location = f" LOCATION {location}" if location else "" 426 file_format = self.expressions(expression, key="file_format", flat=True, sep=" ") 427 file_format = f" FILE FORMAT {file_format}" if file_format else "" 428 429 return f"SET{exprs}{location}{file_format}" 430 431 def array_sql(self, expression: exp.Array) -> str: 432 if expression.args.get("bracket_notation"): 433 return super().array_sql(expression) 434 435 return rename_func("ARRAY")(self, expression)
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
386 def unnest_sql(self, expression: exp.Unnest) -> str: 387 args = expression.expressions 388 num_args = len(args) 389 390 if num_args > 1: 391 self.unsupported(f"Unsupported number of arguments in UNNEST: {num_args}") 392 return "" 393 394 arg = self.sql(seq_get(args, 0)) 395 alias = self.expressions(expression.args.get("alias"), key="columns", flat=True) 396 return f"{arg} AS {alias}" if alias else arg
398 def cast_sql(self, expression: exp.Cast, safe_prefix: t.Optional[str] = None) -> str: 399 if expression.is_type(exp.DataType.Type.JSON): 400 # Redshift doesn't support a JSON type, so casting to it is treated as a noop 401 return self.sql(expression, "this") 402 403 return super().cast_sql(expression, safe_prefix=safe_prefix)
405 def datatype_sql(self, expression: exp.DataType) -> str: 406 """ 407 Redshift converts the `TEXT` data type to `VARCHAR(255)` by default when people more generally mean 408 VARCHAR of max length which is `VARCHAR(max)` in Redshift. Therefore if we get a `TEXT` data type 409 without precision we convert it to `VARCHAR(max)` and if it does have precision then we just convert 410 `TEXT` to `VARCHAR`. 411 """ 412 if expression.is_type("text"): 413 expression.set("this", exp.DataType.Type.VARCHAR) 414 precision = expression.args.get("expressions") 415 416 if not precision: 417 expression.append("expressions", exp.var("MAX")) 418 419 return super().datatype_sql(expression)
Redshift converts the TEXT
data type to VARCHAR(255)
by default when people more generally mean
VARCHAR of max length which is VARCHAR(max)
in Redshift. Therefore if we get a TEXT
data type
without precision we convert it to VARCHAR(max)
and if it does have precision then we just convert
TEXT
to VARCHAR
.
421 def alterset_sql(self, expression: exp.AlterSet) -> str: 422 exprs = self.expressions(expression, flat=True) 423 exprs = f" TABLE PROPERTIES ({exprs})" if exprs else "" 424 location = self.sql(expression, "location") 425 location = f" LOCATION {location}" if location else "" 426 file_format = self.expressions(expression, key="file_format", flat=True, sep=" ") 427 file_format = f" FILE FORMAT {file_format}" if file_format else "" 428 429 return f"SET{exprs}{location}{file_format}"
Inherited Members
- sqlglot.generator.Generator
- Generator
- NULL_ORDERING_SUPPORTED
- IGNORE_NULLS_IN_FUNC
- EXPLICIT_SET_OP
- WRAP_DERIVED_VALUES
- CREATE_FUNCTION_RETURN_AS
- MATCHED_BY_SOURCE
- INTERVAL_ALLOWS_PLURAL_FORM
- LIMIT_FETCH
- LIMIT_ONLY_LITERALS
- GROUPINGS_SEP
- INDEX_ON
- QUERY_HINT_SEP
- IS_BOOL_ALLOWED
- DUPLICATE_KEY_UPDATE_WITH_SET
- LIMIT_IS_TOP
- RETURNING_END
- EXTRACT_ALLOWS_QUOTES
- ALTER_TABLE_INCLUDE_COLUMN_KEYWORD
- UNNEST_WITH_ORDINALITY
- AGGREGATE_FILTER_SUPPORTED
- SEMI_ANTI_JOIN_WITH_SIDE
- COMPUTED_COLUMN_WITH_TYPE
- SUPPORTS_TABLE_COPY
- TABLESAMPLE_REQUIRES_PARENS
- TABLESAMPLE_KEYWORDS
- TABLESAMPLE_WITH_METHOD
- COLLATE_IS_FUNC
- DATA_TYPE_SPECIFIERS_ALLOWED
- ENSURE_BOOLS
- CTE_RECURSIVE_KEYWORD_REQUIRED
- SUPPORTS_SINGLE_ARG_CONCAT
- SUPPORTS_TABLE_ALIAS_COLUMNS
- UNPIVOT_ALIASES_ARE_IDENTIFIERS
- JSON_KEY_VALUE_PAIR_SEP
- INSERT_OVERWRITE
- SUPPORTS_CREATE_TABLE_LIKE
- JSON_PATH_BRACKETED_KEY_SUPPORTED
- JSON_PATH_SINGLE_QUOTE_ESCAPE
- SUPPORTS_TO_NUMBER
- SET_OP_MODIFIERS
- COPY_PARAMS_EQ_REQUIRED
- STAR_EXCEPT
- QUOTE_JSON_PATH
- PAD_FILL_PATTERN_IS_REQUIRED
- SUPPORTS_EXPLODING_PROJECTIONS
- TIME_PART_SINGULARS
- TOKEN_MAPPING
- STRUCT_DELIMITER
- NAMED_PLACEHOLDER_TOKEN
- WITH_SEPARATED_COMMENTS
- EXCLUDE_COMMENTS
- UNWRAPPED_INTERVAL_VALUES
- PARAMETERIZABLE_TEXT_TYPES
- EXPRESSIONS_WITHOUT_NESTED_CTES
- SENTINEL_LINE_BREAK
- pretty
- identify
- normalize
- pad
- unsupported_level
- max_unsupported
- leading_comma
- max_text_width
- comments
- dialect
- normalize_functions
- unsupported_messages
- generate
- preprocess
- unsupported
- sep
- seg
- pad_comment
- maybe_comment
- wrap
- no_identify
- normalize_func
- indent
- sql
- uncache_sql
- cache_sql
- characterset_sql
- column_parts
- column_sql
- columnposition_sql
- columndef_sql
- columnconstraint_sql
- computedcolumnconstraint_sql
- autoincrementcolumnconstraint_sql
- compresscolumnconstraint_sql
- generatedasidentitycolumnconstraint_sql
- generatedasrowcolumnconstraint_sql
- periodforsystemtimeconstraint_sql
- notnullcolumnconstraint_sql
- transformcolumnconstraint_sql
- primarykeycolumnconstraint_sql
- uniquecolumnconstraint_sql
- createable_sql
- create_sql
- sequenceproperties_sql
- clone_sql
- describe_sql
- heredoc_sql
- prepend_ctes
- with_sql
- cte_sql
- tablealias_sql
- bitstring_sql
- hexstring_sql
- bytestring_sql
- unicodestring_sql
- rawstring_sql
- datatypeparam_sql
- directory_sql
- delete_sql
- drop_sql
- except_sql
- except_op
- fetch_sql
- filter_sql
- hint_sql
- indexparameters_sql
- index_sql
- identifier_sql
- hex_sql
- lowerhex_sql
- inputoutputformat_sql
- national_sql
- partition_sql
- properties_sql
- root_properties
- properties
- with_properties
- locate_properties
- property_name
- property_sql
- likeproperty_sql
- fallbackproperty_sql
- journalproperty_sql
- freespaceproperty_sql
- checksumproperty_sql
- mergeblockratioproperty_sql
- datablocksizeproperty_sql
- blockcompressionproperty_sql
- isolatedloadingproperty_sql
- partitionboundspec_sql
- partitionedofproperty_sql
- lockingproperty_sql
- withdataproperty_sql
- withsystemversioningproperty_sql
- insert_sql
- intersect_sql
- intersect_op
- introducer_sql
- kill_sql
- pseudotype_sql
- objectidentifier_sql
- onconflict_sql
- returning_sql
- rowformatdelimitedproperty_sql
- withtablehint_sql
- indextablehint_sql
- historicaldata_sql
- table_parts
- table_sql
- tablesample_sql
- pivot_sql
- version_sql
- tuple_sql
- update_sql
- values_sql
- var_sql
- into_sql
- from_sql
- group_sql
- having_sql
- connect_sql
- prior_sql
- join_sql
- lambda_sql
- lateral_op
- lateral_sql
- limit_sql
- offset_sql
- setitem_sql
- set_sql
- pragma_sql
- lock_sql
- literal_sql
- escape_str
- loaddata_sql
- null_sql
- boolean_sql
- order_sql
- withfill_sql
- cluster_sql
- distribute_sql
- sort_sql
- ordered_sql
- matchrecognizemeasure_sql
- matchrecognize_sql
- query_modifiers
- options_modifier
- queryoption_sql
- offset_limit_modifiers
- after_limit_modifiers
- select_sql
- schema_sql
- schema_columns_sql
- star_sql
- parameter_sql
- sessionparameter_sql
- placeholder_sql
- subquery_sql
- qualify_sql
- set_operations
- union_sql
- union_op
- prewhere_sql
- where_sql
- window_sql
- partition_by_sql
- windowspec_sql
- withingroup_sql
- between_sql
- bracket_offset_expressions
- all_sql
- any_sql
- exists_sql
- case_sql
- constraint_sql
- nextvaluefor_sql
- extract_sql
- trim_sql
- convert_concat_args
- concat_sql
- concatws_sql
- check_sql
- foreignkey_sql
- primarykey_sql
- if_sql
- jsonkeyvalue_sql
- jsonpath_sql
- json_path_part
- formatjson_sql
- jsonobject_sql
- jsonobjectagg_sql
- jsonarray_sql
- jsonarrayagg_sql
- jsoncolumndef_sql
- jsonschema_sql
- jsontable_sql
- openjsoncolumndef_sql
- openjson_sql
- in_sql
- in_unnest_op
- interval_sql
- return_sql
- reference_sql
- anonymous_sql
- paren_sql
- neg_sql
- not_sql
- alias_sql
- pivotalias_sql
- aliases_sql
- atindex_sql
- attimezone_sql
- fromtimezone_sql
- add_sql
- and_sql
- or_sql
- xor_sql
- connector_sql
- bitwiseand_sql
- bitwiseleftshift_sql
- bitwisenot_sql
- bitwiseor_sql
- bitwiserightshift_sql
- bitwisexor_sql
- currentdate_sql
- collate_sql
- command_sql
- comment_sql
- mergetreettlaction_sql
- mergetreettl_sql
- transaction_sql
- commit_sql
- rollback_sql
- altercolumn_sql
- alterdiststyle_sql
- altersortkey_sql
- renametable_sql
- renamecolumn_sql
- altertable_sql
- add_column_sql
- droppartition_sql
- addconstraint_sql
- distinct_sql
- ignorenulls_sql
- respectnulls_sql
- havingmax_sql
- intdiv_sql
- dpipe_sql
- div_sql
- overlaps_sql
- distance_sql
- dot_sql
- eq_sql
- propertyeq_sql
- escape_sql
- glob_sql
- gt_sql
- gte_sql
- ilike_sql
- ilikeany_sql
- is_sql
- like_sql
- likeany_sql
- similarto_sql
- lt_sql
- lte_sql
- mod_sql
- mul_sql
- neq_sql
- nullsafeeq_sql
- nullsafeneq_sql
- slice_sql
- sub_sql
- trycast_sql
- try_sql
- log_sql
- use_sql
- binary
- function_fallback_sql
- func
- format_args
- too_wide
- format_time
- expressions
- op_expressions
- naked_property
- tag_sql
- token_sql
- userdefinedfunction_sql
- joinhint_sql
- kwarg_sql
- when_sql
- merge_sql
- tochar_sql
- tonumber_sql
- dictproperty_sql
- dictrange_sql
- dictsubproperty_sql
- oncluster_sql
- clusteredbyproperty_sql
- anyvalue_sql
- querytransform_sql
- indexconstraintoption_sql
- checkcolumnconstraint_sql
- indexcolumnconstraint_sql
- nvl2_sql
- comprehension_sql
- columnprefix_sql
- opclass_sql
- predict_sql
- forin_sql
- refresh_sql
- operator_sql
- toarray_sql
- tsordstotime_sql
- tsordstotimestamp_sql
- tsordstodate_sql
- unixdate_sql
- lastday_sql
- dateadd_sql
- arrayany_sql
- struct_sql
- partitionrange_sql
- truncatetable_sql
- convert_sql
- copyparameter_sql
- credentials_sql
- copy_sql
- semicolon_sql
- datadeletionproperty_sql
- maskingpolicycolumnconstraint_sql
- gapfill_sql
- scope_resolution
- scoperesolution_sql
- parsejson_sql
- rand_sql
- changes_sql
- pad_sql
- summarize_sql
- explodinggenerateseries_sql
- arrayconcat_sql
- sqlglot.dialects.postgres.Postgres.Generator
- SINGLE_STRING_INTERVAL
- RENAME_TABLE_WITH_DB
- JOIN_HINTS
- TABLE_HINTS
- PARAMETER_TOKEN
- TABLESAMPLE_SIZE_IS_ROWS
- TABLESAMPLE_SEED_KEYWORD
- SUPPORTS_SELECT_INTO
- JSON_TYPE_REQUIRED_FOR_EXTRACTION
- SUPPORTS_UNLOGGED_TABLES
- LIKE_PROPERTY_INSIDE_SCHEMA
- COPY_HAS_INTO_KEYWORD
- SUPPORTED_JSON_PATH_PARTS
- PROPERTIES_LOCATION
- schemacommentproperty_sql
- commentcolumnconstraint_sql
- bracket_sql
- matchagainst_sql