summaryrefslogtreecommitdiffstats
path: root/sqlglot/generator.py
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--sqlglot/generator.py67
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