summaryrefslogtreecommitdiffstats
path: root/sqlglot/generator.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2023-07-24 08:03:48 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2023-07-24 08:03:48 +0000
commit36db14f4c6c28209371d563d76697df0172e337f (patch)
tree527347be54a38fc9b14fe1fa04c27a4bbae44016 /sqlglot/generator.py
parentReleasing debian version 17.4.1-1. (diff)
downloadsqlglot-36db14f4c6c28209371d563d76697df0172e337f.tar.xz
sqlglot-36db14f4c6c28209371d563d76697df0172e337f.zip
Merging upstream version 17.7.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sqlglot/generator.py')
-rw-r--r--sqlglot/generator.py79
1 files changed, 67 insertions, 12 deletions
diff --git a/sqlglot/generator.py b/sqlglot/generator.py
index 857eff1..40ba88e 100644
--- a/sqlglot/generator.py
+++ b/sqlglot/generator.py
@@ -164,6 +164,11 @@ class Generator:
# https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax
SELECT_KINDS: t.Tuple[str, ...] = ("STRUCT", "VALUE")
+ # Whether or not VALUES statements can be used as derived tables.
+ # MySQL 5 and Redshift do not allow this, so when False, it will convert
+ # SELECT * VALUES into SELECT UNION
+ VALUES_AS_TABLE = True
+
TYPE_MAPPING = {
exp.DataType.Type.NCHAR: "CHAR",
exp.DataType.Type.NVARCHAR: "VARCHAR",
@@ -260,8 +265,9 @@ class Generator:
# Expressions whose comments are separated from them for better formatting
WITH_SEPARATED_COMMENTS: t.Tuple[t.Type[exp.Expression], ...] = (
- exp.Select,
+ exp.Drop,
exp.From,
+ exp.Select,
exp.Where,
exp.With,
)
@@ -818,7 +824,11 @@ class Generator:
def datatype_sql(self, expression: exp.DataType) -> str:
type_value = expression.this
- type_sql = self.TYPE_MAPPING.get(type_value, type_value.value)
+ type_sql = (
+ self.TYPE_MAPPING.get(type_value, type_value.value)
+ if isinstance(type_value, exp.DataType.Type)
+ else type_value
+ )
nested = ""
interior = self.expressions(expression, flat=True)
values = ""
@@ -1307,15 +1317,45 @@ class Generator:
return self.prepend_ctes(expression, sql)
def values_sql(self, expression: exp.Values) -> str:
- args = self.expressions(expression)
- alias = self.sql(expression, "alias")
- values = f"VALUES{self.seg('')}{args}"
- values = (
- f"({values})"
- if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
- else values
- )
- return f"{values} AS {alias}" if alias else values
+ # The VALUES clause is still valid in an `INSERT INTO ..` statement, for example
+ if self.VALUES_AS_TABLE or not expression.find_ancestor(exp.From, exp.Join):
+ args = self.expressions(expression)
+ alias = self.sql(expression, "alias")
+ values = f"VALUES{self.seg('')}{args}"
+ values = (
+ f"({values})"
+ if self.WRAP_DERIVED_VALUES and (alias or isinstance(expression.parent, exp.From))
+ else values
+ )
+ return f"{values} AS {alias}" if alias else values
+
+ # Converts `VALUES...` expression into a series of select 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.
+ expression = expression.copy()
+ column_names = expression.alias and expression.args["alias"].columns
+
+ selects = []
+
+ for i, tup in enumerate(expression.expressions):
+ row = tup.expressions
+
+ if i == 0 and column_names:
+ row = [
+ exp.alias_(value, column_name) for value, column_name in zip(row, column_names)
+ ]
+
+ selects.append(exp.Select(expressions=row))
+
+ subquery_expression: exp.Select | exp.Union = selects[0]
+ if len(selects) > 1:
+ for select in selects[1:]:
+ subquery_expression = exp.union(
+ subquery_expression, select, distinct=False, copy=False
+ )
+
+ return self.subquery_sql(subquery_expression.subquery(expression.alias, copy=False))
def var_sql(self, expression: exp.Var) -> str:
return self.sql(expression, "this")
@@ -2043,7 +2083,7 @@ class Generator:
def and_sql(self, expression: exp.And) -> str:
return self.connector_sql(expression, "AND")
- def xor_sql(self, expression: exp.And) -> str:
+ def xor_sql(self, expression: exp.Xor) -> str:
return self.connector_sql(expression, "XOR")
def connector_sql(self, expression: exp.Connector, op: str) -> str:
@@ -2507,6 +2547,21 @@ class Generator:
return self.func("ANY_VALUE", this)
+ def querytransform_sql(self, expression: exp.QueryTransform) -> str:
+ transform = self.func("TRANSFORM", *expression.expressions)
+ row_format_before = self.sql(expression, "row_format_before")
+ row_format_before = f" {row_format_before}" if row_format_before else ""
+ record_writer = self.sql(expression, "record_writer")
+ record_writer = f" RECORDWRITER {record_writer}" if record_writer else ""
+ using = f" USING {self.sql(expression, 'command_script')}"
+ schema = self.sql(expression, "schema")
+ schema = f" AS {schema}" if schema else ""
+ row_format_after = self.sql(expression, "row_format_after")
+ row_format_after = f" {row_format_after}" if row_format_after else ""
+ record_reader = self.sql(expression, "record_reader")
+ record_reader = f" RECORDREADER {record_reader}" if record_reader else ""
+ return f"{transform}{row_format_before}{record_writer}{using}{schema}{row_format_after}{record_reader}"
+
def cached_generator(
cache: t.Optional[t.Dict[int, str]] = None