diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-03-03 14:11:07 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-03-03 14:11:07 +0000 |
commit | 42a1548cecf48d18233f56e3385cf9c89abcb9c2 (patch) | |
tree | 5e0fff4ecbd1fd7dd1022a7580139038df2a824c /sqlglot/dialects/duckdb.py | |
parent | Releasing debian version 21.1.2-1. (diff) | |
download | sqlglot-42a1548cecf48d18233f56e3385cf9c89abcb9c2.tar.xz sqlglot-42a1548cecf48d18233f56e3385cf9c89abcb9c2.zip |
Merging upstream version 22.2.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'sqlglot/dialects/duckdb.py')
-rw-r--r-- | sqlglot/dialects/duckdb.py | 64 |
1 files changed, 48 insertions, 16 deletions
diff --git a/sqlglot/dialects/duckdb.py b/sqlglot/dialects/duckdb.py index 925c5ae..f74dc97 100644 --- a/sqlglot/dialects/duckdb.py +++ b/sqlglot/dialects/duckdb.py @@ -79,6 +79,21 @@ def _build_date_diff(args: t.List) -> exp.Expression: return exp.DateDiff(this=seq_get(args, 2), expression=seq_get(args, 1), unit=seq_get(args, 0)) +def _build_generate_series(end_exclusive: bool = False) -> t.Callable[[t.List], exp.GenerateSeries]: + def _builder(args: t.List) -> exp.GenerateSeries: + # Check https://duckdb.org/docs/sql/functions/nested.html#range-functions + if len(args) == 1: + # DuckDB uses 0 as a default for the series' start when it's omitted + args.insert(0, exp.Literal.number("0")) + + gen_series = exp.GenerateSeries.from_arg_list(args) + gen_series.set("is_end_exclusive", end_exclusive) + + return gen_series + + return _builder + + def _build_make_timestamp(args: t.List) -> exp.Expression: if len(args) == 1: return exp.UnixToTime(this=seq_get(args, 0), scale=exp.UnixToTime.MICROS) @@ -95,13 +110,13 @@ def _build_make_timestamp(args: t.List) -> exp.Expression: def _struct_sql(self: DuckDB.Generator, expression: exp.Struct) -> str: args: t.List[str] = [] - for expr in expression.expressions: - if isinstance(expr, exp.Alias): - key = expr.alias - value = expr.this - else: - key = expr.name or expr.this.name + for i, expr in enumerate(expression.expressions): + if isinstance(expr, exp.PropertyEQ): + key = expr.name value = expr.expression + else: + key = f"_{i}" + value = expr args.append(f"{self.sql(exp.Literal.string(key))}: {self.sql(value)}") @@ -148,13 +163,6 @@ def _rename_unless_within_group( ) -def _build_struct_pack(args: t.List) -> exp.Struct: - args_with_columns_as_identifiers = [ - exp.PropertyEQ(this=arg.this.this, expression=arg.expression) for arg in args - ] - return exp.Struct.from_arg_list(args_with_columns_as_identifiers) - - class DuckDB(Dialect): NULL_ORDERING = "nulls_are_last" SUPPORTS_USER_DEFINED_TYPES = False @@ -189,6 +197,7 @@ class DuckDB(Dialect): "CHARACTER VARYING": TokenType.TEXT, "EXCLUDE": TokenType.EXCEPT, "LOGICAL": TokenType.BOOLEAN, + "ONLY": TokenType.ONLY, "PIVOT_WIDER": TokenType.PIVOT, "SIGNED": TokenType.INT, "STRING": TokenType.VARCHAR, @@ -213,6 +222,8 @@ class DuckDB(Dialect): TokenType.TILDA: exp.RegexpLike, } + FUNCTIONS_WITH_ALIASED_ARGS = {*parser.Parser.FUNCTIONS_WITH_ALIASED_ARGS, "STRUCT_PACK"} + FUNCTIONS = { **parser.Parser.FUNCTIONS, "ARRAY_HAS": exp.ArrayContains.from_arg_list, @@ -261,12 +272,14 @@ class DuckDB(Dialect): "STRING_SPLIT_REGEX": exp.RegexpSplit.from_arg_list, "STRING_TO_ARRAY": exp.Split.from_arg_list, "STRPTIME": build_formatted_time(exp.StrToTime, "duckdb"), - "STRUCT_PACK": _build_struct_pack, + "STRUCT_PACK": exp.Struct.from_arg_list, "STR_SPLIT": exp.Split.from_arg_list, "STR_SPLIT_REGEX": exp.RegexpSplit.from_arg_list, "TO_TIMESTAMP": exp.UnixToTime.from_arg_list, "UNNEST": exp.Explode.from_arg_list, "XOR": binary_from_function(exp.BitwiseXor), + "GENERATE_SERIES": _build_generate_series(), + "RANGE": _build_generate_series(end_exclusive=True), } FUNCTION_PARSERS = parser.Parser.FUNCTION_PARSERS.copy() @@ -313,6 +326,8 @@ class DuckDB(Dialect): return pivot_column_names(aggregations, dialect="duckdb") class Generator(generator.Generator): + PARAMETER_TOKEN = "$" + NAMED_PLACEHOLDER_TOKEN = "$" JOIN_HINTS = False TABLE_HINTS = False QUERY_HINTS = False @@ -535,5 +550,22 @@ class DuckDB(Dialect): return self.sql(expression, "this") return super().columndef_sql(expression, sep) - def placeholder_sql(self, expression: exp.Placeholder) -> str: - return f"${expression.name}" if expression.name else "?" + def join_sql(self, expression: exp.Join) -> str: + if ( + expression.side == "LEFT" + and not expression.args.get("on") + and isinstance(expression.this, exp.Unnest) + ): + # Some dialects support `LEFT JOIN UNNEST(...)` without an explicit ON clause + # DuckDB doesn't, but we can just add a dummy ON clause that is always true + return super().join_sql(expression.on(exp.true())) + + return super().join_sql(expression) + + def generateseries_sql(self, expression: exp.GenerateSeries) -> str: + # GENERATE_SERIES(a, b) -> [a, b], RANGE(a, b) -> [a, b) + if expression.args.get("is_end_exclusive"): + expression.set("is_end_exclusive", None) + return rename_func("RANGE")(self, expression) + + return super().generateseries_sql(expression) |