diff options
Diffstat (limited to 'sqlglot/dialects')
-rw-r--r-- | sqlglot/dialects/__init__.py | 2 | ||||
-rw-r--r-- | sqlglot/dialects/bigquery.py | 1 | ||||
-rw-r--r-- | sqlglot/dialects/clickhouse.py | 14 | ||||
-rw-r--r-- | sqlglot/dialects/dialect.py | 6 | ||||
-rw-r--r-- | sqlglot/dialects/doris.py | 6 | ||||
-rw-r--r-- | sqlglot/dialects/duckdb.py | 84 | ||||
-rw-r--r-- | sqlglot/dialects/hive.py | 5 | ||||
-rw-r--r-- | sqlglot/dialects/materialize.py | 94 | ||||
-rw-r--r-- | sqlglot/dialects/mysql.py | 6 | ||||
-rw-r--r-- | sqlglot/dialects/postgres.py | 18 | ||||
-rw-r--r-- | sqlglot/dialects/prql.py | 10 | ||||
-rw-r--r-- | sqlglot/dialects/risingwave.py | 6 | ||||
-rw-r--r-- | sqlglot/dialects/snowflake.py | 6 | ||||
-rw-r--r-- | sqlglot/dialects/teradata.py | 6 | ||||
-rw-r--r-- | sqlglot/dialects/tsql.py | 2 |
15 files changed, 234 insertions, 32 deletions
diff --git a/sqlglot/dialects/__init__.py b/sqlglot/dialects/__init__.py index 29c6580..c85b607 100644 --- a/sqlglot/dialects/__init__.py +++ b/sqlglot/dialects/__init__.py @@ -70,12 +70,14 @@ from sqlglot.dialects.doris import Doris from sqlglot.dialects.drill import Drill from sqlglot.dialects.duckdb import DuckDB from sqlglot.dialects.hive import Hive +from sqlglot.dialects.materialize import Materialize from sqlglot.dialects.mysql import MySQL from sqlglot.dialects.oracle import Oracle from sqlglot.dialects.postgres import Postgres from sqlglot.dialects.presto import Presto from sqlglot.dialects.prql import PRQL from sqlglot.dialects.redshift import Redshift +from sqlglot.dialects.risingwave import RisingWave from sqlglot.dialects.snowflake import Snowflake from sqlglot.dialects.spark import Spark from sqlglot.dialects.spark2 import Spark2 diff --git a/sqlglot/dialects/bigquery.py b/sqlglot/dialects/bigquery.py index 47fb0ce..d3db319 100644 --- a/sqlglot/dialects/bigquery.py +++ b/sqlglot/dialects/bigquery.py @@ -705,7 +705,6 @@ class BigQuery(Dialect): # from: https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#reserved_keywords RESERVED_KEYWORDS = { - *generator.Generator.RESERVED_KEYWORDS, "all", "and", "any", diff --git a/sqlglot/dialects/clickhouse.py b/sqlglot/dialects/clickhouse.py index 7615203..f5c4e30 100644 --- a/sqlglot/dialects/clickhouse.py +++ b/sqlglot/dialects/clickhouse.py @@ -367,7 +367,7 @@ class ClickHouse(Dialect): **parser.Parser.QUERY_MODIFIER_PARSERS, TokenType.SETTINGS: lambda self: ( "settings", - self._advance() or self._parse_csv(self._parse_conjunction), + self._advance() or self._parse_csv(self._parse_assignment), ), TokenType.FORMAT: lambda self: ("format", self._advance() or self._parse_id_var()), } @@ -388,15 +388,15 @@ class ClickHouse(Dialect): "INDEX", } - def _parse_conjunction(self) -> t.Optional[exp.Expression]: - this = super()._parse_conjunction() + def _parse_assignment(self) -> t.Optional[exp.Expression]: + this = super()._parse_assignment() if self._match(TokenType.PLACEHOLDER): return self.expression( exp.If, this=this, - true=self._parse_conjunction(), - false=self._match(TokenType.COLON) and self._parse_conjunction(), + true=self._parse_assignment(), + false=self._match(TokenType.COLON) and self._parse_assignment(), ) return this @@ -461,7 +461,7 @@ class ClickHouse(Dialect): # WITH <expression> AS <identifier> cte = self.expression( exp.CTE, - this=self._parse_conjunction(), + this=self._parse_assignment(), alias=self._parse_table_alias(), scalar=True, ) @@ -592,7 +592,7 @@ class ClickHouse(Dialect): ) -> exp.IndexColumnConstraint: # INDEX name1 expr TYPE type1(args) GRANULARITY value this = self._parse_id_var() - expression = self._parse_conjunction() + expression = self._parse_assignment() index_type = self._match_text_seq("TYPE") and ( self._parse_function() or self._parse_var() diff --git a/sqlglot/dialects/dialect.py b/sqlglot/dialects/dialect.py index 0421e4e..aea792a 100644 --- a/sqlglot/dialects/dialect.py +++ b/sqlglot/dialects/dialect.py @@ -50,12 +50,14 @@ class Dialects(str, Enum): DRILL = "drill" DUCKDB = "duckdb" HIVE = "hive" + MATERIALIZE = "materialize" MYSQL = "mysql" ORACLE = "oracle" POSTGRES = "postgres" PRESTO = "presto" PRQL = "prql" REDSHIFT = "redshift" + RISINGWAVE = "risingwave" SNOWFLAKE = "snowflake" SPARK = "spark" SPARK2 = "spark2" @@ -593,7 +595,9 @@ def inline_array_unless_query(self: Generator, expression: exp.Array) -> str: def no_ilike_sql(self: Generator, expression: exp.ILike) -> str: return self.like_sql( - exp.Like(this=exp.Lower(this=expression.this), expression=expression.expression) + exp.Like( + this=exp.Lower(this=expression.this), expression=exp.Lower(this=expression.expression) + ) ) diff --git a/sqlglot/dialects/doris.py b/sqlglot/dialects/doris.py index 5f156cc..9fb073e 100644 --- a/sqlglot/dialects/doris.py +++ b/sqlglot/dialects/doris.py @@ -26,6 +26,9 @@ class Doris(MySQL): "TO_DATE": exp.TsOrDsToDate.from_arg_list, } + FUNCTION_PARSERS = MySQL.Parser.FUNCTION_PARSERS.copy() + FUNCTION_PARSERS.pop("GROUP_CONCAT") + class Generator(MySQL.Generator): LAST_DAY_SUPPORTS_DATE_PART = False @@ -49,6 +52,9 @@ class Doris(MySQL): exp.ArrayUniqueAgg: rename_func("COLLECT_SET"), exp.CurrentTimestamp: lambda self, _: self.func("NOW"), exp.DateTrunc: lambda self, e: self.func("DATE_TRUNC", e.this, unit_to_str(e)), + exp.GroupConcat: lambda self, e: self.func( + "GROUP_CONCAT", e.this, e.args.get("separator") or exp.Literal.string(",") + ), exp.JSONExtractScalar: lambda self, e: self.func("JSON_EXTRACT", e.this, e.expression), exp.Map: rename_func("ARRAY_MAP"), exp.RegexpLike: rename_func("REGEXP"), diff --git a/sqlglot/dialects/duckdb.py b/sqlglot/dialects/duckdb.py index 24c2b38..1316460 100644 --- a/sqlglot/dialects/duckdb.py +++ b/sqlglot/dialects/duckdb.py @@ -341,7 +341,7 @@ class DuckDB(Dialect): if self._match(TokenType.L_BRACE, advance=False): return self.expression(exp.ToMap, this=self._parse_bracket()) - args = self._parse_wrapped_csv(self._parse_conjunction) + args = self._parse_wrapped_csv(self._parse_assignment) return self.expression(exp.Map, keys=seq_get(args, 0), values=seq_get(args, 1)) def _parse_struct_types(self, type_required: bool = False) -> t.Optional[exp.Expression]: @@ -503,11 +503,93 @@ class DuckDB(Dialect): exp.DataType.Type.VARBINARY: "BLOB", exp.DataType.Type.ROWVERSION: "BLOB", exp.DataType.Type.VARCHAR: "TEXT", + exp.DataType.Type.TIMESTAMPNTZ: "TIMESTAMP", exp.DataType.Type.TIMESTAMP_S: "TIMESTAMP_S", exp.DataType.Type.TIMESTAMP_MS: "TIMESTAMP_MS", exp.DataType.Type.TIMESTAMP_NS: "TIMESTAMP_NS", } + # https://github.com/duckdb/duckdb/blob/ff7f24fd8e3128d94371827523dae85ebaf58713/third_party/libpg_query/grammar/keywords/reserved_keywords.list#L1-L77 + RESERVED_KEYWORDS = { + "array", + "analyse", + "union", + "all", + "when", + "in_p", + "default", + "create_p", + "window", + "asymmetric", + "to", + "else", + "localtime", + "from", + "end_p", + "select", + "current_date", + "foreign", + "with", + "grant", + "session_user", + "or", + "except", + "references", + "fetch", + "limit", + "group_p", + "leading", + "into", + "collate", + "offset", + "do", + "then", + "localtimestamp", + "check_p", + "lateral_p", + "current_role", + "where", + "asc_p", + "placing", + "desc_p", + "user", + "unique", + "initially", + "column", + "both", + "some", + "as", + "any", + "only", + "deferrable", + "null_p", + "current_time", + "true_p", + "table", + "case", + "trailing", + "variadic", + "for", + "on", + "distinct", + "false_p", + "not", + "constraint", + "current_timestamp", + "returning", + "primary", + "intersect", + "having", + "analyze", + "current_user", + "and", + "cast", + "symmetric", + "using", + "order", + "current_catalog", + } + UNWRAPPED_INTERVAL_VALUES = (exp.Literal, exp.Paren) # DuckDB doesn't generally support CREATE TABLE .. properties diff --git a/sqlglot/dialects/hive.py b/sqlglot/dialects/hive.py index 48e032d..f79c274 100644 --- a/sqlglot/dialects/hive.py +++ b/sqlglot/dialects/hive.py @@ -29,6 +29,7 @@ from sqlglot.dialects.dialect import ( struct_extract_sql, time_format, timestrtotime_sql, + unit_to_str, var_map_sql, ) from sqlglot.transforms import ( @@ -318,6 +319,7 @@ class Hive(Dialect): ), "TO_DATE": build_formatted_time(exp.TsOrDsToDate, "hive"), "TO_JSON": exp.JSONFormat.from_arg_list, + "TRUNC": exp.TimestampTrunc.from_arg_list, "UNBASE64": exp.FromBase64.from_arg_list, "UNIX_TIMESTAMP": lambda args: build_formatted_time(exp.StrToUnix, "hive", True)( args or [exp.CurrentTimestamp()] @@ -415,7 +417,7 @@ class Hive(Dialect): ) -> t.Tuple[t.List[exp.Expression], t.Optional[exp.Expression]]: return ( ( - self._parse_csv(self._parse_conjunction) + self._parse_csv(self._parse_assignment) if self._match_set({TokenType.PARTITION_BY, TokenType.DISTRIBUTE_BY}) else [] ), @@ -548,6 +550,7 @@ class Hive(Dialect): exp.TimeStrToDate: rename_func("TO_DATE"), exp.TimeStrToTime: timestrtotime_sql, exp.TimeStrToUnix: rename_func("UNIX_TIMESTAMP"), + exp.TimestampTrunc: lambda self, e: self.func("TRUNC", e.this, unit_to_str(e)), exp.TimeToStr: lambda self, e: self.func("DATE_FORMAT", e.this, self.format_time(e)), exp.TimeToUnix: rename_func("UNIX_TIMESTAMP"), exp.ToBase64: rename_func("BASE64"), diff --git a/sqlglot/dialects/materialize.py b/sqlglot/dialects/materialize.py new file mode 100644 index 0000000..4534c21 --- /dev/null +++ b/sqlglot/dialects/materialize.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +from sqlglot import exp +from sqlglot.helper import seq_get +from sqlglot.dialects.postgres import Postgres + +from sqlglot.tokens import TokenType +from sqlglot.transforms import ( + remove_unique_constraints, + ctas_with_tmp_tables_to_create_tmp_view, + preprocess, +) +import typing as t + + +class Materialize(Postgres): + class Parser(Postgres.Parser): + NO_PAREN_FUNCTION_PARSERS = { + **Postgres.Parser.NO_PAREN_FUNCTION_PARSERS, + "MAP": lambda self: self._parse_map(), + } + + LAMBDAS = { + **Postgres.Parser.LAMBDAS, + TokenType.FARROW: lambda self, expressions: self.expression( + exp.Kwarg, this=seq_get(expressions, 0), expression=self._parse_assignment() + ), + } + + def _parse_lambda_arg(self) -> t.Optional[exp.Expression]: + return self._parse_field() + + def _parse_map(self) -> exp.ToMap: + if self._match(TokenType.L_PAREN): + to_map = self.expression(exp.ToMap, this=self._parse_select()) + self._match_r_paren() + return to_map + + if not self._match(TokenType.L_BRACKET): + self.raise_error("Expecting [") + + entries = [ + exp.PropertyEQ(this=e.this, expression=e.expression) + for e in self._parse_csv(self._parse_lambda) + ] + + if not self._match(TokenType.R_BRACKET): + self.raise_error("Expecting ]") + + return self.expression(exp.ToMap, this=self.expression(exp.Struct, expressions=entries)) + + class Generator(Postgres.Generator): + SUPPORTS_CREATE_TABLE_LIKE = False + + TRANSFORMS = { + **Postgres.Generator.TRANSFORMS, + exp.AutoIncrementColumnConstraint: lambda self, e: "", + exp.Create: preprocess( + [ + remove_unique_constraints, + ctas_with_tmp_tables_to_create_tmp_view, + ] + ), + exp.GeneratedAsIdentityColumnConstraint: lambda self, e: "", + exp.OnConflict: lambda self, e: "", + exp.PrimaryKeyColumnConstraint: lambda self, e: "", + } + TRANSFORMS.pop(exp.ToMap) + + def propertyeq_sql(self, expression: exp.PropertyEQ) -> str: + return self.binary(expression, "=>") + + def datatype_sql(self, expression: exp.DataType) -> str: + if expression.is_type(exp.DataType.Type.LIST): + if expression.expressions: + return f"{self.expressions(expression, flat=True)} LIST" + return "LIST" + + if expression.is_type(exp.DataType.Type.MAP) and len(expression.expressions) == 2: + key, value = expression.expressions + return f"MAP[{self.sql(key)} => {self.sql(value)}]" + + return super().datatype_sql(expression) + + def list_sql(self, expression: exp.List) -> str: + if isinstance(seq_get(expression.expressions, 0), exp.Select): + return self.func("LIST", seq_get(expression.expressions, 0)) + + return f"{self.normalize_func('LIST')}[{self.expressions(expression, flat=True)}]" + + def tomap_sql(self, expression: exp.ToMap) -> str: + if isinstance(expression.this, exp.Select): + return self.func("MAP", expression.this) + return f"{self.normalize_func('MAP')}[{self.expressions(expression.this)}]" diff --git a/sqlglot/dialects/mysql.py b/sqlglot/dialects/mysql.py index 611c527..26c7ad2 100644 --- a/sqlglot/dialects/mysql.py +++ b/sqlglot/dialects/mysql.py @@ -279,6 +279,10 @@ class MySQL(Dialect): **parser.Parser.CONJUNCTION, TokenType.DAMP: exp.And, TokenType.XOR: exp.Xor, + } + + DISJUNCTION = { + **parser.Parser.DISJUNCTION, TokenType.DPIPE: exp.Or, } @@ -625,7 +629,7 @@ class MySQL(Dialect): ) def _parse_chr(self) -> t.Optional[exp.Expression]: - expressions = self._parse_csv(self._parse_conjunction) + expressions = self._parse_csv(self._parse_assignment) kwargs: t.Dict[str, t.Any] = {"this": seq_get(expressions, 0)} if len(expressions) > 1: diff --git a/sqlglot/dialects/postgres.py b/sqlglot/dialects/postgres.py index 249a04c..25a02b0 100644 --- a/sqlglot/dialects/postgres.py +++ b/sqlglot/dialects/postgres.py @@ -114,15 +114,6 @@ def _string_agg_sql(self: Postgres.Generator, expression: exp.GroupConcat) -> st return f"STRING_AGG({self.format_args(this, separator)}{order})" -def _datatype_sql(self: Postgres.Generator, expression: exp.DataType) -> str: - if expression.is_type("array"): - if expression.expressions: - values = self.expressions(expression, key="values", flat=True) - return f"{self.expressions(expression, flat=True)}[{values}]" - return "ARRAY" - return self.datatype_sql(expression) - - def _auto_increment_to_serial(expression: exp.Expression) -> exp.Expression: auto = expression.find(exp.AutoIncrementColumnConstraint) @@ -500,7 +491,6 @@ class Postgres(Dialect): exp.DateAdd: _date_add_sql("+"), exp.DateDiff: _date_diff_sql, exp.DateStrToDate: datestrtodate_sql, - exp.DataType: _datatype_sql, exp.DateSub: _date_add_sql("-"), exp.Explode: rename_func("UNNEST"), exp.GroupConcat: _string_agg_sql, @@ -623,3 +613,11 @@ class Postgres(Dialect): option = self.sql(expression, "option") return f"SET {exprs}{access_method}{tablespace}{option}" + + def datatype_sql(self, expression: exp.DataType) -> str: + if expression.is_type(exp.DataType.Type.ARRAY): + if expression.expressions: + values = self.expressions(expression, key="values", flat=True) + return f"{self.expressions(expression, flat=True)}[{values}]" + return "ARRAY" + return super().datatype_sql(expression) diff --git a/sqlglot/dialects/prql.py b/sqlglot/dialects/prql.py index ad0c647..2fc2594 100644 --- a/sqlglot/dialects/prql.py +++ b/sqlglot/dialects/prql.py @@ -36,6 +36,10 @@ class PRQL(Dialect): CONJUNCTION = { **parser.Parser.CONJUNCTION, TokenType.DAMP: exp.And, + } + + DISJUNCTION = { + **parser.Parser.DISJUNCTION, TokenType.DPIPE: exp.Or, } @@ -43,7 +47,7 @@ class PRQL(Dialect): "DERIVE": lambda self, query: self._parse_selection(query), "SELECT": lambda self, query: self._parse_selection(query, append=False), "TAKE": lambda self, query: self._parse_take(query), - "FILTER": lambda self, query: query.where(self._parse_conjunction()), + "FILTER": lambda self, query: query.where(self._parse_assignment()), "APPEND": lambda self, query: query.union( _select_all(self._parse_table()), distinct=False, copy=False ), @@ -174,8 +178,8 @@ class PRQL(Dialect): if self._next and self._next.token_type == TokenType.ALIAS: alias = self._parse_id_var(True) self._match(TokenType.ALIAS) - return self.expression(exp.Alias, this=self._parse_conjunction(), alias=alias) - return self._parse_conjunction() + return self.expression(exp.Alias, this=self._parse_assignment(), alias=alias) + return self._parse_assignment() def _parse_table( self, diff --git a/sqlglot/dialects/risingwave.py b/sqlglot/dialects/risingwave.py new file mode 100644 index 0000000..18906aa --- /dev/null +++ b/sqlglot/dialects/risingwave.py @@ -0,0 +1,6 @@ +from sqlglot.dialects.postgres import Postgres + + +class RisingWave(Postgres): + class Generator(Postgres.Generator): + LOCKING_READS_SUPPORTED = False diff --git a/sqlglot/dialects/snowflake.py b/sqlglot/dialects/snowflake.py index f72b29e..8c3c471 100644 --- a/sqlglot/dialects/snowflake.py +++ b/sqlglot/dialects/snowflake.py @@ -498,7 +498,7 @@ class Snowflake(Dialect): TokenType.ARROW: lambda self, expressions: self.expression( exp.Lambda, this=self._replace_lambda( - self._parse_conjunction(), + self._parse_assignment(), expressions, ), expressions=[e.this if isinstance(e, exp.Cast) else e for e in expressions], @@ -576,7 +576,7 @@ class Snowflake(Dialect): # - https://docs.snowflake.com/en/sql-reference/functions/object_construct return self._parse_slice(self._parse_string()) - return self._parse_slice(self._parse_alias(self._parse_conjunction(), explicit=True)) + return self._parse_slice(self._parse_alias(self._parse_assignment(), explicit=True)) def _parse_lateral(self) -> t.Optional[exp.Lateral]: lateral = super()._parse_lateral() @@ -714,7 +714,7 @@ class Snowflake(Dialect): def _parse_file_location(self) -> t.Optional[exp.Expression]: # Parse either a subquery or a staged file return ( - self._parse_select(table=True) + self._parse_select(table=True, parse_subquery_alias=False) if self._match(TokenType.L_PAREN, advance=False) else self._parse_table_parts() ) diff --git a/sqlglot/dialects/teradata.py b/sqlglot/dialects/teradata.py index feb2097..90a0bf4 100644 --- a/sqlglot/dialects/teradata.py +++ b/sqlglot/dialects/teradata.py @@ -164,7 +164,7 @@ class Teradata(Dialect): } def _parse_translate(self, strict: bool) -> exp.Expression: - this = self._parse_conjunction() + this = self._parse_assignment() if not self._match(TokenType.USING): self.raise_error("Expected USING in TRANSLATE") @@ -195,8 +195,8 @@ class Teradata(Dialect): this = self._parse_id_var() self._match(TokenType.BETWEEN) - expressions = self._parse_csv(self._parse_conjunction) - each = self._match_text_seq("EACH") and self._parse_conjunction() + expressions = self._parse_csv(self._parse_assignment) + each = self._match_text_seq("EACH") and self._parse_assignment() return self.expression(exp.RangeN, this=this, expressions=expressions, each=each) diff --git a/sqlglot/dialects/tsql.py b/sqlglot/dialects/tsql.py index f1a61dd..ee403a7 100644 --- a/sqlglot/dialects/tsql.py +++ b/sqlglot/dialects/tsql.py @@ -625,7 +625,7 @@ class TSQL(Dialect): ) -> t.Optional[exp.Expression]: this = self._parse_types() self._match(TokenType.COMMA) - args = [this, *self._parse_csv(self._parse_conjunction)] + args = [this, *self._parse_csv(self._parse_assignment)] convert = exp.Convert.from_arg_list(args) convert.set("safe", safe) convert.set("strict", strict) |