Edit on GitHub

sqlglot.dialects.redshift

  1from __future__ import annotations
  2
  3import typing as t
  4
  5from sqlglot import exp, transforms
  6from sqlglot.dialects.dialect import concat_to_dpipe_sql, rename_func
  7from sqlglot.dialects.postgres import Postgres
  8from sqlglot.helper import seq_get
  9from sqlglot.tokens import TokenType
 10
 11
 12def _json_sql(self: Postgres.Generator, expression: exp.JSONExtract | exp.JSONExtractScalar) -> str:
 13    return f'{self.sql(expression, "this")}."{expression.expression.name}"'
 14
 15
 16class Redshift(Postgres):
 17    # https://docs.aws.amazon.com/redshift/latest/dg/r_names.html
 18    RESOLVES_IDENTIFIERS_AS_UPPERCASE = None
 19
 20    TIME_FORMAT = "'YYYY-MM-DD HH:MI:SS'"
 21    TIME_MAPPING = {
 22        **Postgres.TIME_MAPPING,
 23        "MON": "%b",
 24        "HH": "%H",
 25    }
 26
 27    class Parser(Postgres.Parser):
 28        FUNCTIONS = {
 29            **Postgres.Parser.FUNCTIONS,
 30            "DATEADD": lambda args: exp.DateAdd(
 31                this=exp.TsOrDsToDate(this=seq_get(args, 2)),
 32                expression=seq_get(args, 1),
 33                unit=seq_get(args, 0),
 34            ),
 35            "DATEDIFF": lambda args: exp.DateDiff(
 36                this=exp.TsOrDsToDate(this=seq_get(args, 2)),
 37                expression=exp.TsOrDsToDate(this=seq_get(args, 1)),
 38                unit=seq_get(args, 0),
 39            ),
 40            "NVL": exp.Coalesce.from_arg_list,
 41            "STRTOL": exp.FromBase.from_arg_list,
 42        }
 43
 44        CONVERT_TYPE_FIRST = True
 45
 46        def _parse_types(
 47            self, check_func: bool = False, schema: bool = False
 48        ) -> t.Optional[exp.Expression]:
 49            this = super()._parse_types(check_func=check_func, schema=schema)
 50
 51            if (
 52                isinstance(this, exp.DataType)
 53                and this.is_type("varchar")
 54                and this.expressions
 55                and this.expressions[0].this == exp.column("MAX")
 56            ):
 57                this.set("expressions", [exp.var("MAX")])
 58
 59            return this
 60
 61    class Tokenizer(Postgres.Tokenizer):
 62        BIT_STRINGS = []
 63        HEX_STRINGS = []
 64        STRING_ESCAPES = ["\\"]
 65
 66        KEYWORDS = {
 67            **Postgres.Tokenizer.KEYWORDS,
 68            "HLLSKETCH": TokenType.HLLSKETCH,
 69            "SUPER": TokenType.SUPER,
 70            "SYSDATE": TokenType.CURRENT_TIMESTAMP,
 71            "TIME": TokenType.TIMESTAMP,
 72            "TIMETZ": TokenType.TIMESTAMPTZ,
 73            "TOP": TokenType.TOP,
 74            "UNLOAD": TokenType.COMMAND,
 75            "VARBYTE": TokenType.VARBINARY,
 76        }
 77
 78        # Redshift allows # to appear as a table identifier prefix
 79        SINGLE_TOKENS = Postgres.Tokenizer.SINGLE_TOKENS.copy()
 80        SINGLE_TOKENS.pop("#")
 81
 82    class Generator(Postgres.Generator):
 83        LOCKING_READS_SUPPORTED = False
 84        RENAME_TABLE_WITH_DB = False
 85
 86        TYPE_MAPPING = {
 87            **Postgres.Generator.TYPE_MAPPING,
 88            exp.DataType.Type.BINARY: "VARBYTE",
 89            exp.DataType.Type.VARBINARY: "VARBYTE",
 90            exp.DataType.Type.INT: "INTEGER",
 91        }
 92
 93        PROPERTIES_LOCATION = {
 94            **Postgres.Generator.PROPERTIES_LOCATION,
 95            exp.LikeProperty: exp.Properties.Location.POST_WITH,
 96        }
 97
 98        TRANSFORMS = {
 99            **Postgres.Generator.TRANSFORMS,
100            exp.Concat: concat_to_dpipe_sql,
101            exp.CurrentTimestamp: lambda self, e: "SYSDATE",
102            exp.DateAdd: lambda self, e: self.func(
103                "DATEADD", exp.var(e.text("unit") or "day"), e.expression, e.this
104            ),
105            exp.DateDiff: lambda self, e: self.func(
106                "DATEDIFF", exp.var(e.text("unit") or "day"), e.expression, e.this
107            ),
108            exp.DistKeyProperty: lambda self, e: f"DISTKEY({e.name})",
109            exp.DistStyleProperty: lambda self, e: self.naked_property(e),
110            exp.FromBase: rename_func("STRTOL"),
111            exp.JSONExtract: _json_sql,
112            exp.JSONExtractScalar: _json_sql,
113            exp.SafeConcat: concat_to_dpipe_sql,
114            exp.Select: transforms.preprocess([transforms.eliminate_distinct_on]),
115            exp.SortKeyProperty: lambda self, e: f"{'COMPOUND ' if e.args['compound'] else ''}SORTKEY({self.format_args(*e.this)})",
116            exp.TsOrDsToDate: lambda self, e: self.sql(e.this),
117        }
118
119        # Postgres maps exp.Pivot to no_pivot_sql, but Redshift support pivots
120        TRANSFORMS.pop(exp.Pivot)
121
122        # Redshift uses the POW | POWER (expr1, expr2) syntax instead of expr1 ^ expr2 (postgres)
123        TRANSFORMS.pop(exp.Pow)
124
125        RESERVED_KEYWORDS = {*Postgres.Generator.RESERVED_KEYWORDS, "snapshot", "type"}
126
127        def values_sql(self, expression: exp.Values) -> str:
128            """
129            Converts `VALUES...` expression into a series of unions.
130
131            Note: If you have a lot of unions then this will result in a large number of recursive statements to
132            evaluate the expression. You may need to increase `sys.setrecursionlimit` to run and it can also be
133            very slow.
134            """
135
136            # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
137            if not expression.find_ancestor(exp.From, exp.Join):
138                return super().values_sql(expression)
139
140            column_names = expression.alias and expression.args["alias"].columns
141
142            selects = []
143            rows = [tuple_exp.expressions for tuple_exp in expression.expressions]
144
145            for i, row in enumerate(rows):
146                if i == 0 and column_names:
147                    row = [
148                        exp.alias_(value, column_name)
149                        for value, column_name in zip(row, column_names)
150                    ]
151
152                selects.append(exp.Select(expressions=row))
153
154            subquery_expression: exp.Select | exp.Union = selects[0]
155            if len(selects) > 1:
156                for select in selects[1:]:
157                    subquery_expression = exp.union(subquery_expression, select, distinct=False)
158
159            return self.subquery_sql(subquery_expression.subquery(expression.alias))
160
161        def with_properties(self, properties: exp.Properties) -> str:
162            """Redshift doesn't have `WITH` as part of their with_properties so we remove it"""
163            return self.properties(properties, prefix=" ", suffix="")
164
165        def datatype_sql(self, expression: exp.DataType) -> str:
166            """
167            Redshift converts the `TEXT` data type to `VARCHAR(255)` by default when people more generally mean
168            VARCHAR of max length which is `VARCHAR(max)` in Redshift. Therefore if we get a `TEXT` data type
169            without precision we convert it to `VARCHAR(max)` and if it does have precision then we just convert
170            `TEXT` to `VARCHAR`.
171            """
172            if expression.is_type("text"):
173                expression = expression.copy()
174                expression.set("this", exp.DataType.Type.VARCHAR)
175                precision = expression.args.get("expressions")
176
177                if not precision:
178                    expression.append("expressions", exp.var("MAX"))
179
180            return super().datatype_sql(expression)
class Redshift(sqlglot.dialects.postgres.Postgres):
 17class Redshift(Postgres):
 18    # https://docs.aws.amazon.com/redshift/latest/dg/r_names.html
 19    RESOLVES_IDENTIFIERS_AS_UPPERCASE = None
 20
 21    TIME_FORMAT = "'YYYY-MM-DD HH:MI:SS'"
 22    TIME_MAPPING = {
 23        **Postgres.TIME_MAPPING,
 24        "MON": "%b",
 25        "HH": "%H",
 26    }
 27
 28    class Parser(Postgres.Parser):
 29        FUNCTIONS = {
 30            **Postgres.Parser.FUNCTIONS,
 31            "DATEADD": lambda args: exp.DateAdd(
 32                this=exp.TsOrDsToDate(this=seq_get(args, 2)),
 33                expression=seq_get(args, 1),
 34                unit=seq_get(args, 0),
 35            ),
 36            "DATEDIFF": lambda args: exp.DateDiff(
 37                this=exp.TsOrDsToDate(this=seq_get(args, 2)),
 38                expression=exp.TsOrDsToDate(this=seq_get(args, 1)),
 39                unit=seq_get(args, 0),
 40            ),
 41            "NVL": exp.Coalesce.from_arg_list,
 42            "STRTOL": exp.FromBase.from_arg_list,
 43        }
 44
 45        CONVERT_TYPE_FIRST = True
 46
 47        def _parse_types(
 48            self, check_func: bool = False, schema: bool = False
 49        ) -> t.Optional[exp.Expression]:
 50            this = super()._parse_types(check_func=check_func, schema=schema)
 51
 52            if (
 53                isinstance(this, exp.DataType)
 54                and this.is_type("varchar")
 55                and this.expressions
 56                and this.expressions[0].this == exp.column("MAX")
 57            ):
 58                this.set("expressions", [exp.var("MAX")])
 59
 60            return this
 61
 62    class Tokenizer(Postgres.Tokenizer):
 63        BIT_STRINGS = []
 64        HEX_STRINGS = []
 65        STRING_ESCAPES = ["\\"]
 66
 67        KEYWORDS = {
 68            **Postgres.Tokenizer.KEYWORDS,
 69            "HLLSKETCH": TokenType.HLLSKETCH,
 70            "SUPER": TokenType.SUPER,
 71            "SYSDATE": TokenType.CURRENT_TIMESTAMP,
 72            "TIME": TokenType.TIMESTAMP,
 73            "TIMETZ": TokenType.TIMESTAMPTZ,
 74            "TOP": TokenType.TOP,
 75            "UNLOAD": TokenType.COMMAND,
 76            "VARBYTE": TokenType.VARBINARY,
 77        }
 78
 79        # Redshift allows # to appear as a table identifier prefix
 80        SINGLE_TOKENS = Postgres.Tokenizer.SINGLE_TOKENS.copy()
 81        SINGLE_TOKENS.pop("#")
 82
 83    class Generator(Postgres.Generator):
 84        LOCKING_READS_SUPPORTED = False
 85        RENAME_TABLE_WITH_DB = False
 86
 87        TYPE_MAPPING = {
 88            **Postgres.Generator.TYPE_MAPPING,
 89            exp.DataType.Type.BINARY: "VARBYTE",
 90            exp.DataType.Type.VARBINARY: "VARBYTE",
 91            exp.DataType.Type.INT: "INTEGER",
 92        }
 93
 94        PROPERTIES_LOCATION = {
 95            **Postgres.Generator.PROPERTIES_LOCATION,
 96            exp.LikeProperty: exp.Properties.Location.POST_WITH,
 97        }
 98
 99        TRANSFORMS = {
100            **Postgres.Generator.TRANSFORMS,
101            exp.Concat: concat_to_dpipe_sql,
102            exp.CurrentTimestamp: lambda self, e: "SYSDATE",
103            exp.DateAdd: lambda self, e: self.func(
104                "DATEADD", exp.var(e.text("unit") or "day"), e.expression, e.this
105            ),
106            exp.DateDiff: lambda self, e: self.func(
107                "DATEDIFF", exp.var(e.text("unit") or "day"), e.expression, e.this
108            ),
109            exp.DistKeyProperty: lambda self, e: f"DISTKEY({e.name})",
110            exp.DistStyleProperty: lambda self, e: self.naked_property(e),
111            exp.FromBase: rename_func("STRTOL"),
112            exp.JSONExtract: _json_sql,
113            exp.JSONExtractScalar: _json_sql,
114            exp.SafeConcat: concat_to_dpipe_sql,
115            exp.Select: transforms.preprocess([transforms.eliminate_distinct_on]),
116            exp.SortKeyProperty: lambda self, e: f"{'COMPOUND ' if e.args['compound'] else ''}SORTKEY({self.format_args(*e.this)})",
117            exp.TsOrDsToDate: lambda self, e: self.sql(e.this),
118        }
119
120        # Postgres maps exp.Pivot to no_pivot_sql, but Redshift support pivots
121        TRANSFORMS.pop(exp.Pivot)
122
123        # Redshift uses the POW | POWER (expr1, expr2) syntax instead of expr1 ^ expr2 (postgres)
124        TRANSFORMS.pop(exp.Pow)
125
126        RESERVED_KEYWORDS = {*Postgres.Generator.RESERVED_KEYWORDS, "snapshot", "type"}
127
128        def values_sql(self, expression: exp.Values) -> str:
129            """
130            Converts `VALUES...` expression into a series of unions.
131
132            Note: If you have a lot of unions then this will result in a large number of recursive statements to
133            evaluate the expression. You may need to increase `sys.setrecursionlimit` to run and it can also be
134            very slow.
135            """
136
137            # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
138            if not expression.find_ancestor(exp.From, exp.Join):
139                return super().values_sql(expression)
140
141            column_names = expression.alias and expression.args["alias"].columns
142
143            selects = []
144            rows = [tuple_exp.expressions for tuple_exp in expression.expressions]
145
146            for i, row in enumerate(rows):
147                if i == 0 and column_names:
148                    row = [
149                        exp.alias_(value, column_name)
150                        for value, column_name in zip(row, column_names)
151                    ]
152
153                selects.append(exp.Select(expressions=row))
154
155            subquery_expression: exp.Select | exp.Union = selects[0]
156            if len(selects) > 1:
157                for select in selects[1:]:
158                    subquery_expression = exp.union(subquery_expression, select, distinct=False)
159
160            return self.subquery_sql(subquery_expression.subquery(expression.alias))
161
162        def with_properties(self, properties: exp.Properties) -> str:
163            """Redshift doesn't have `WITH` as part of their with_properties so we remove it"""
164            return self.properties(properties, prefix=" ", suffix="")
165
166        def datatype_sql(self, expression: exp.DataType) -> str:
167            """
168            Redshift converts the `TEXT` data type to `VARCHAR(255)` by default when people more generally mean
169            VARCHAR of max length which is `VARCHAR(max)` in Redshift. Therefore if we get a `TEXT` data type
170            without precision we convert it to `VARCHAR(max)` and if it does have precision then we just convert
171            `TEXT` to `VARCHAR`.
172            """
173            if expression.is_type("text"):
174                expression = expression.copy()
175                expression.set("this", exp.DataType.Type.VARCHAR)
176                precision = expression.args.get("expressions")
177
178                if not precision:
179                    expression.append("expressions", exp.var("MAX"))
180
181            return super().datatype_sql(expression)
class Redshift.Parser(sqlglot.dialects.postgres.Postgres.Parser):
28    class Parser(Postgres.Parser):
29        FUNCTIONS = {
30            **Postgres.Parser.FUNCTIONS,
31            "DATEADD": lambda args: exp.DateAdd(
32                this=exp.TsOrDsToDate(this=seq_get(args, 2)),
33                expression=seq_get(args, 1),
34                unit=seq_get(args, 0),
35            ),
36            "DATEDIFF": lambda args: exp.DateDiff(
37                this=exp.TsOrDsToDate(this=seq_get(args, 2)),
38                expression=exp.TsOrDsToDate(this=seq_get(args, 1)),
39                unit=seq_get(args, 0),
40            ),
41            "NVL": exp.Coalesce.from_arg_list,
42            "STRTOL": exp.FromBase.from_arg_list,
43        }
44
45        CONVERT_TYPE_FIRST = True
46
47        def _parse_types(
48            self, check_func: bool = False, schema: bool = False
49        ) -> t.Optional[exp.Expression]:
50            this = super()._parse_types(check_func=check_func, schema=schema)
51
52            if (
53                isinstance(this, exp.DataType)
54                and this.is_type("varchar")
55                and this.expressions
56                and this.expressions[0].this == exp.column("MAX")
57            ):
58                this.set("expressions", [exp.var("MAX")])
59
60            return this

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: Determines 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
class Redshift.Tokenizer(sqlglot.dialects.postgres.Postgres.Tokenizer):
62    class Tokenizer(Postgres.Tokenizer):
63        BIT_STRINGS = []
64        HEX_STRINGS = []
65        STRING_ESCAPES = ["\\"]
66
67        KEYWORDS = {
68            **Postgres.Tokenizer.KEYWORDS,
69            "HLLSKETCH": TokenType.HLLSKETCH,
70            "SUPER": TokenType.SUPER,
71            "SYSDATE": TokenType.CURRENT_TIMESTAMP,
72            "TIME": TokenType.TIMESTAMP,
73            "TIMETZ": TokenType.TIMESTAMPTZ,
74            "TOP": TokenType.TOP,
75            "UNLOAD": TokenType.COMMAND,
76            "VARBYTE": TokenType.VARBINARY,
77        }
78
79        # Redshift allows # to appear as a table identifier prefix
80        SINGLE_TOKENS = Postgres.Tokenizer.SINGLE_TOKENS.copy()
81        SINGLE_TOKENS.pop("#")
class Redshift.Generator(sqlglot.dialects.postgres.Postgres.Generator):
 83    class Generator(Postgres.Generator):
 84        LOCKING_READS_SUPPORTED = False
 85        RENAME_TABLE_WITH_DB = False
 86
 87        TYPE_MAPPING = {
 88            **Postgres.Generator.TYPE_MAPPING,
 89            exp.DataType.Type.BINARY: "VARBYTE",
 90            exp.DataType.Type.VARBINARY: "VARBYTE",
 91            exp.DataType.Type.INT: "INTEGER",
 92        }
 93
 94        PROPERTIES_LOCATION = {
 95            **Postgres.Generator.PROPERTIES_LOCATION,
 96            exp.LikeProperty: exp.Properties.Location.POST_WITH,
 97        }
 98
 99        TRANSFORMS = {
100            **Postgres.Generator.TRANSFORMS,
101            exp.Concat: concat_to_dpipe_sql,
102            exp.CurrentTimestamp: lambda self, e: "SYSDATE",
103            exp.DateAdd: lambda self, e: self.func(
104                "DATEADD", exp.var(e.text("unit") or "day"), e.expression, e.this
105            ),
106            exp.DateDiff: lambda self, e: self.func(
107                "DATEDIFF", exp.var(e.text("unit") or "day"), e.expression, e.this
108            ),
109            exp.DistKeyProperty: lambda self, e: f"DISTKEY({e.name})",
110            exp.DistStyleProperty: lambda self, e: self.naked_property(e),
111            exp.FromBase: rename_func("STRTOL"),
112            exp.JSONExtract: _json_sql,
113            exp.JSONExtractScalar: _json_sql,
114            exp.SafeConcat: concat_to_dpipe_sql,
115            exp.Select: transforms.preprocess([transforms.eliminate_distinct_on]),
116            exp.SortKeyProperty: lambda self, e: f"{'COMPOUND ' if e.args['compound'] else ''}SORTKEY({self.format_args(*e.this)})",
117            exp.TsOrDsToDate: lambda self, e: self.sql(e.this),
118        }
119
120        # Postgres maps exp.Pivot to no_pivot_sql, but Redshift support pivots
121        TRANSFORMS.pop(exp.Pivot)
122
123        # Redshift uses the POW | POWER (expr1, expr2) syntax instead of expr1 ^ expr2 (postgres)
124        TRANSFORMS.pop(exp.Pow)
125
126        RESERVED_KEYWORDS = {*Postgres.Generator.RESERVED_KEYWORDS, "snapshot", "type"}
127
128        def values_sql(self, expression: exp.Values) -> str:
129            """
130            Converts `VALUES...` expression into a series of unions.
131
132            Note: If you have a lot of unions then this will result in a large number of recursive statements to
133            evaluate the expression. You may need to increase `sys.setrecursionlimit` to run and it can also be
134            very slow.
135            """
136
137            # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
138            if not expression.find_ancestor(exp.From, exp.Join):
139                return super().values_sql(expression)
140
141            column_names = expression.alias and expression.args["alias"].columns
142
143            selects = []
144            rows = [tuple_exp.expressions for tuple_exp in expression.expressions]
145
146            for i, row in enumerate(rows):
147                if i == 0 and column_names:
148                    row = [
149                        exp.alias_(value, column_name)
150                        for value, column_name in zip(row, column_names)
151                    ]
152
153                selects.append(exp.Select(expressions=row))
154
155            subquery_expression: exp.Select | exp.Union = selects[0]
156            if len(selects) > 1:
157                for select in selects[1:]:
158                    subquery_expression = exp.union(subquery_expression, select, distinct=False)
159
160            return self.subquery_sql(subquery_expression.subquery(expression.alias))
161
162        def with_properties(self, properties: exp.Properties) -> str:
163            """Redshift doesn't have `WITH` as part of their with_properties so we remove it"""
164            return self.properties(properties, prefix=" ", suffix="")
165
166        def datatype_sql(self, expression: exp.DataType) -> str:
167            """
168            Redshift converts the `TEXT` data type to `VARCHAR(255)` by default when people more generally mean
169            VARCHAR of max length which is `VARCHAR(max)` in Redshift. Therefore if we get a `TEXT` data type
170            without precision we convert it to `VARCHAR(max)` and if it does have precision then we just convert
171            `TEXT` to `VARCHAR`.
172            """
173            if expression.is_type("text"):
174                expression = expression.copy()
175                expression.set("this", exp.DataType.Type.VARCHAR)
176                precision = expression.args.get("expressions")
177
178                if not precision:
179                    expression.append("expressions", exp.var("MAX"))
180
181            return super().datatype_sql(expression)

Generator converts a given syntax tree to the corresponding SQL string.

Arguments:
  • pretty: Whether or not to format the produced SQL string. Default: False.
  • identify: Determines when an identifier should be quoted. Possible values are: False (default): Never quote, except in cases where it's mandatory by the dialect. True or 'always': Always quote. 'safe': Only quote identifiers that are case insensitive.
  • normalize: Whether or not to normalize identifiers to lowercase. Default: False.
  • pad: Determines the pad size in a formatted string. Default: 2.
  • indent: Determines the indentation size in a formatted string. Default: 2.
  • normalize_functions: Whether or not to normalize all function names. Possible values are: "upper" or True (default): Convert names to uppercase. "lower": Convert names to lowercase. False: Disables function name normalization.
  • unsupported_level: Determines the generator's behavior when it encounters unsupported expressions. Default ErrorLevel.WARN.
  • max_unsupported: Maximum number of unsupported messages to include in a raised UnsupportedError. This is only relevant if unsupported_level is ErrorLevel.RAISE. Default: 3
  • leading_comma: Determines whether or not the comma is leading or trailing in select expressions. This is only relevant when generating in pretty mode. Default: False
  • max_text_width: The max number of characters in a segment before creating new lines in pretty mode. The default is on the smaller end because the length only represents a segment and not the true line length. Default: 80
  • comments: Whether or not to preserve comments in the output SQL code. Default: True
def values_sql(self, expression: sqlglot.expressions.Values) -> str:
128        def values_sql(self, expression: exp.Values) -> str:
129            """
130            Converts `VALUES...` expression into a series of unions.
131
132            Note: If you have a lot of unions then this will result in a large number of recursive statements to
133            evaluate the expression. You may need to increase `sys.setrecursionlimit` to run and it can also be
134            very slow.
135            """
136
137            # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
138            if not expression.find_ancestor(exp.From, exp.Join):
139                return super().values_sql(expression)
140
141            column_names = expression.alias and expression.args["alias"].columns
142
143            selects = []
144            rows = [tuple_exp.expressions for tuple_exp in expression.expressions]
145
146            for i, row in enumerate(rows):
147                if i == 0 and column_names:
148                    row = [
149                        exp.alias_(value, column_name)
150                        for value, column_name in zip(row, column_names)
151                    ]
152
153                selects.append(exp.Select(expressions=row))
154
155            subquery_expression: exp.Select | exp.Union = selects[0]
156            if len(selects) > 1:
157                for select in selects[1:]:
158                    subquery_expression = exp.union(subquery_expression, select, distinct=False)
159
160            return self.subquery_sql(subquery_expression.subquery(expression.alias))

Converts VALUES... expression into a series of unions.

Note: If you have a lot of unions then this will result in a large number of recursive statements to evaluate the expression. You may need to increase sys.setrecursionlimit to run and it can also be very slow.

def with_properties(self, properties: sqlglot.expressions.Properties) -> str:
162        def with_properties(self, properties: exp.Properties) -> str:
163            """Redshift doesn't have `WITH` as part of their with_properties so we remove it"""
164            return self.properties(properties, prefix=" ", suffix="")

Redshift doesn't have WITH as part of their with_properties so we remove it

def datatype_sql(self, expression: sqlglot.expressions.DataType) -> str:
166        def datatype_sql(self, expression: exp.DataType) -> str:
167            """
168            Redshift converts the `TEXT` data type to `VARCHAR(255)` by default when people more generally mean
169            VARCHAR of max length which is `VARCHAR(max)` in Redshift. Therefore if we get a `TEXT` data type
170            without precision we convert it to `VARCHAR(max)` and if it does have precision then we just convert
171            `TEXT` to `VARCHAR`.
172            """
173            if expression.is_type("text"):
174                expression = expression.copy()
175                expression.set("this", exp.DataType.Type.VARCHAR)
176                precision = expression.args.get("expressions")
177
178                if not precision:
179                    expression.append("expressions", exp.var("MAX"))
180
181            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.

@classmethod
def can_identify(text: str, identify: str | bool = 'safe') -> bool:
247    @classmethod
248    def can_identify(cls, text: str, identify: str | bool = "safe") -> bool:
249        """Checks if text can be identified given an identify option.
250
251        Args:
252            text: The text to check.
253            identify:
254                "always" or `True`: Always returns true.
255                "safe": True if the identifier is case-insensitive.
256
257        Returns:
258            Whether or not the given text can be identified.
259        """
260        if identify is True or identify == "always":
261            return True
262
263        if identify == "safe":
264            return not cls.case_sensitive(text)
265
266        return False

Checks if text can be identified given an identify option.

Arguments:
  • text: The text to check.
  • identify: "always" or True: Always returns true. "safe": True if the identifier is case-insensitive.
Returns:

Whether or not the given text can be identified.

Inherited Members
sqlglot.generator.Generator
Generator
generate
unsupported
sep
seg
pad_comment
maybe_comment
wrap
no_identify
normalize_func
indent
sql
uncache_sql
cache_sql
characterset_sql
column_sql
columnposition_sql
columndef_sql
columnconstraint_sql
autoincrementcolumnconstraint_sql
compresscolumnconstraint_sql
generatedasidentitycolumnconstraint_sql
notnullcolumnconstraint_sql
primarykeycolumnconstraint_sql
uniquecolumnconstraint_sql
createable_sql
create_sql
clone_sql
describe_sql
prepend_ctes
with_sql
cte_sql
tablealias_sql
bitstring_sql
hexstring_sql
bytestring_sql
rawstring_sql
datatypesize_sql
directory_sql
delete_sql
drop_sql
except_sql
except_op
fetch_sql
filter_sql
hint_sql
index_sql
identifier_sql
inputoutputformat_sql
national_sql
partition_sql
properties_sql
root_properties
properties
locate_properties
property_sql
likeproperty_sql
fallbackproperty_sql
journalproperty_sql
freespaceproperty_sql
checksumproperty_sql
mergeblockratioproperty_sql
datablocksizeproperty_sql
blockcompressionproperty_sql
isolatedloadingproperty_sql
lockingproperty_sql
withdataproperty_sql
insert_sql
intersect_sql
intersect_op
introducer_sql
pseudotype_sql
onconflict_sql
returning_sql
rowformatdelimitedproperty_sql
table_sql
tablesample_sql
pivot_sql
tuple_sql
update_sql
var_sql
into_sql
from_sql
group_sql
having_sql
join_sql
lambda_sql
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
cluster_sql
distribute_sql
sort_sql
ordered_sql
matchrecognize_sql
query_modifiers
offset_limit_modifiers
after_having_modifiers
after_limit_modifiers
select_sql
schema_sql
schema_columns_sql
star_sql
parameter_sql
sessionparameter_sql
placeholder_sql
subquery_sql
qualify_sql
union_sql
union_op
unnest_sql
where_sql
window_sql
partition_by_sql
windowspec_sql
withingroup_sql
between_sql
bracket_sql
all_sql
any_sql
exists_sql
case_sql
constraint_sql
nextvaluefor_sql
extract_sql
trim_sql
safeconcat_sql
check_sql
foreignkey_sql
primarykey_sql
if_sql
matchagainst_sql
jsonkeyvalue_sql
jsonobject_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
aliases_sql
attimezone_sql
add_sql
and_sql
connector_sql
bitwiseand_sql
bitwiseleftshift_sql
bitwisenot_sql
bitwiseor_sql
bitwiserightshift_sql
bitwisexor_sql
cast_sql
currentdate_sql
collate_sql
command_sql
comment_sql
mergetreettlaction_sql
mergetreettl_sql
transaction_sql
commit_sql
rollback_sql
altercolumn_sql
renametable_sql
altertable_sql
droppartition_sql
addconstraint_sql
distinct_sql
ignorenulls_sql
respectnulls_sql
intdiv_sql
dpipe_sql
safedpipe_sql
div_sql
overlaps_sql
distance_sql
dot_sql
eq_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
or_sql
slice_sql
sub_sql
trycast_sql
use_sql
binary
function_fallback_sql
func
format_args
text_width
format_time
expressions
op_expressions
naked_property
set_operation
tag_sql
token_sql
userdefinedfunction_sql
joinhint_sql
kwarg_sql
when_sql
merge_sql
tochar_sql
dictproperty_sql
dictrange_sql
dictsubproperty_sql
oncluster_sql