diff options
Diffstat (limited to '')
-rw-r--r-- | sqlglot/generator.py | 67 |
1 files changed, 53 insertions, 14 deletions
diff --git a/sqlglot/generator.py b/sqlglot/generator.py index b1ee783..edc6939 100644 --- a/sqlglot/generator.py +++ b/sqlglot/generator.py @@ -86,6 +86,7 @@ class Generator: exp.OnUpdateColumnConstraint: lambda self, e: f"ON UPDATE {self.sql(e, 'this')}", exp.PathColumnConstraint: lambda self, e: f"PATH {self.sql(e, 'this')}", exp.ReturnsProperty: lambda self, e: self.naked_property(e), + exp.SampleProperty: lambda self, e: f"SAMPLE BY {self.sql(e, 'this')}", exp.SetProperty: lambda self, e: f"{'MULTI' if e.args.get('multi') else ''}SET", exp.SettingsProperty: lambda self, e: f"SETTINGS{self.seg('')}{(self.expressions(e))}", exp.SqlSecurityProperty: lambda self, e: f"SQL SECURITY {'DEFINER' if e.args.get('definer') else 'INVOKER'}", @@ -204,6 +205,21 @@ class Generator: # Whether or not session variables / parameters are supported, e.g. @x in T-SQL SUPPORTS_PARAMETERS = True + # Whether or not to include the type of a computed column in the CREATE DDL + COMPUTED_COLUMN_WITH_TYPE = True + + # Whether or not CREATE TABLE .. COPY .. is supported. False means we'll generate CLONE instead of COPY + SUPPORTS_TABLE_COPY = True + + # Whether or not parentheses are required around the table sample's expression + TABLESAMPLE_REQUIRES_PARENS = True + + # Whether or not COLLATE is a function instead of a binary operator + COLLATE_IS_FUNC = False + + # Whether or not data types support additional specifiers like e.g. CHAR or BYTE (oracle) + DATA_TYPE_SPECIFIERS_ALLOWED = False + TYPE_MAPPING = { exp.DataType.Type.NCHAR: "CHAR", exp.DataType.Type.NVARCHAR: "VARCHAR", @@ -282,6 +298,7 @@ class Generator: exp.RowFormatProperty: exp.Properties.Location.POST_SCHEMA, exp.RowFormatDelimitedProperty: exp.Properties.Location.POST_SCHEMA, exp.RowFormatSerdeProperty: exp.Properties.Location.POST_SCHEMA, + exp.SampleProperty: exp.Properties.Location.POST_SCHEMA, exp.SchemaCommentProperty: exp.Properties.Location.POST_SCHEMA, exp.SerdeProperties: exp.Properties.Location.POST_SCHEMA, exp.Set: exp.Properties.Location.POST_SCHEMA, @@ -324,13 +341,12 @@ class Generator: exp.Paren, ) - UNESCAPED_SEQUENCE_TABLE = None # type: ignore - SENTINEL_LINE_BREAK = "__SQLGLOT__LB__" # Autofilled INVERSE_TIME_MAPPING: t.Dict[str, str] = {} INVERSE_TIME_TRIE: t.Dict = {} + INVERSE_ESCAPE_SEQUENCES: t.Dict[str, str] = {} INDEX_OFFSET = 0 UNNEST_COLUMN_ONLY = False ALIAS_POST_TABLESAMPLE = False @@ -480,8 +496,7 @@ class Generator: if not comments or isinstance(expression, exp.Binary): return sql - sep = "\n" if self.pretty else " " - comments_sql = sep.join( + comments_sql = " ".join( f"/*{self.pad_comment(comment)}*/" for comment in comments if comment ) @@ -649,6 +664,9 @@ class Generator: position = self.sql(expression, "position") position = f" {position}" if position else "" + if expression.find(exp.ComputedColumnConstraint) and not self.COMPUTED_COLUMN_WITH_TYPE: + kind = "" + return f"{exists}{column}{kind}{constraints}{position}" def columnconstraint_sql(self, expression: exp.ColumnConstraint) -> str: @@ -750,9 +768,11 @@ class Generator: ) begin = " BEGIN" if expression.args.get("begin") else "" + end = " END" if expression.args.get("end") else "" + expression_sql = self.sql(expression, "expression") if expression_sql: - expression_sql = f"{begin}{self.sep()}{expression_sql}" + expression_sql = f"{begin}{self.sep()}{expression_sql}{end}" if self.CREATE_FUNCTION_RETURN_AS or not isinstance(expression.expression, exp.Return): if properties_locs.get(exp.Properties.Location.POST_ALIAS): @@ -817,7 +837,8 @@ class Generator: def clone_sql(self, expression: exp.Clone) -> str: this = self.sql(expression, "this") shallow = "SHALLOW " if expression.args.get("shallow") else "" - this = f"{shallow}CLONE {this}" + keyword = "COPY" if expression.args.get("copy") and self.SUPPORTS_TABLE_COPY else "CLONE" + this = f"{shallow}{keyword} {this}" when = self.sql(expression, "when") if when: @@ -877,7 +898,7 @@ class Generator: def datatypeparam_sql(self, expression: exp.DataTypeParam) -> str: this = self.sql(expression, "this") specifier = self.sql(expression, "expression") - specifier = f" {specifier}" if specifier else "" + specifier = f" {specifier}" if specifier and self.DATA_TYPE_SPECIFIERS_ALLOWED else "" return f"{this}{specifier}" def datatype_sql(self, expression: exp.DataType) -> str: @@ -1329,8 +1350,13 @@ class Generator: pivots = f" {pivots}" if pivots else "" joins = self.expressions(expression, key="joins", sep="", skip_first=True) laterals = self.expressions(expression, key="laterals", sep="") + file_format = self.sql(expression, "format") + if file_format: + pattern = self.sql(expression, "pattern") + pattern = f", PATTERN => {pattern}" if pattern else "" + file_format = f" (FILE_FORMAT => {file_format}{pattern})" - return f"{table}{version}{alias}{hints}{pivots}{joins}{laterals}" + return f"{table}{version}{file_format}{alias}{hints}{pivots}{joins}{laterals}" def tablesample_sql( self, expression: exp.TableSample, seed_prefix: str = "SEED", sep=" AS " @@ -1343,6 +1369,7 @@ class Generator: else: this = self.sql(expression, "this") alias = "" + method = self.sql(expression, "method") method = f"{method.upper()} " if method and self.TABLESAMPLE_WITH_METHOD else "" numerator = self.sql(expression, "bucket_numerator") @@ -1354,13 +1381,20 @@ class Generator: percent = f"{percent} PERCENT" if percent else "" rows = self.sql(expression, "rows") rows = f"{rows} ROWS" if rows else "" + size = self.sql(expression, "size") if size and self.TABLESAMPLE_SIZE_IS_PERCENT: size = f"{size} PERCENT" + seed = self.sql(expression, "seed") seed = f" {seed_prefix} ({seed})" if seed else "" kind = expression.args.get("kind", "TABLESAMPLE") - return f"{this} {kind} {method}({bucket}{percent}{rows}{size}){seed}{alias}" + + expr = f"{bucket}{percent}{rows}{size}" + if self.TABLESAMPLE_REQUIRES_PARENS: + expr = f"({expr})" + + return f"{this} {kind} {method}{expr}{seed}{alias}" def pivot_sql(self, expression: exp.Pivot) -> str: expressions = self.expressions(expression, flat=True) @@ -1638,8 +1672,8 @@ class Generator: def escape_str(self, text: str) -> str: text = text.replace(self.QUOTE_END, self._escaped_quote_end) - if self.UNESCAPED_SEQUENCE_TABLE: - text = text.translate(self.UNESCAPED_SEQUENCE_TABLE) + if self.INVERSE_ESCAPE_SEQUENCES: + text = "".join(self.INVERSE_ESCAPE_SEQUENCES.get(ch, ch) for ch in text) elif self.pretty: text = text.replace("\n", self.SENTINEL_LINE_BREAK) return text @@ -2301,6 +2335,8 @@ class Generator: return f"CURRENT_DATE({zone})" if zone else "CURRENT_DATE" def collate_sql(self, expression: exp.Collate) -> str: + if self.COLLATE_IS_FUNC: + return self.function_fallback_sql(expression) return self.binary(expression, "COLLATE") def command_sql(self, expression: exp.Command) -> str: @@ -2359,7 +2395,7 @@ class Generator: collate = f" COLLATE {collate}" if collate else "" using = self.sql(expression, "using") using = f" USING {using}" if using else "" - return f"ALTER COLUMN {this} TYPE {dtype}{collate}{using}" + return f"ALTER COLUMN {this} SET DATA TYPE {dtype}{collate}{using}" default = self.sql(expression, "default") if default: @@ -2396,7 +2432,7 @@ class Generator: elif isinstance(actions[0], exp.Delete): actions = self.expressions(expression, key="actions", flat=True) else: - actions = self.expressions(expression, key="actions") + actions = self.expressions(expression, key="actions", flat=True) exists = " IF EXISTS" if expression.args.get("exists") else "" only = " ONLY" if expression.args.get("only") else "" @@ -2593,7 +2629,7 @@ class Generator: self, expression: t.Optional[exp.Expression] = None, key: t.Optional[str] = None, - sqls: t.Optional[t.List[str]] = None, + sqls: t.Optional[t.Collection[str | exp.Expression]] = None, flat: bool = False, indent: bool = True, skip_first: bool = False, @@ -2841,6 +2877,9 @@ class Generator: def columnprefix_sql(self, expression: exp.ColumnPrefix) -> str: return f"{self.sql(expression, 'this')}({self.sql(expression, 'expression')})" + def opclass_sql(self, expression: exp.Opclass) -> str: + return f"{self.sql(expression, 'this')} {self.sql(expression, 'expression')}" + def cached_generator( cache: t.Optional[t.Dict[int, str]] = None |