summaryrefslogtreecommitdiffstats
path: root/sqlglot/parser.py
diff options
context:
space:
mode:
Diffstat (limited to 'sqlglot/parser.py')
-rw-r--r--sqlglot/parser.py157
1 files changed, 122 insertions, 35 deletions
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
index 90fdade..a36251e 100644
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -96,6 +96,7 @@ class Parser(metaclass=_Parser):
NO_PAREN_FUNCTIONS = {
TokenType.CURRENT_DATE: exp.CurrentDate,
TokenType.CURRENT_DATETIME: exp.CurrentDate,
+ TokenType.CURRENT_TIME: exp.CurrentTime,
TokenType.CURRENT_TIMESTAMP: exp.CurrentTimestamp,
}
@@ -198,7 +199,6 @@ class Parser(metaclass=_Parser):
TokenType.COMMIT,
TokenType.COMPOUND,
TokenType.CONSTRAINT,
- TokenType.CURRENT_TIME,
TokenType.DEFAULT,
TokenType.DELETE,
TokenType.DESCRIBE,
@@ -370,8 +370,9 @@ class Parser(metaclass=_Parser):
LAMBDAS = {
TokenType.ARROW: lambda self, expressions: self.expression(
exp.Lambda,
- this=self._parse_conjunction().transform(
- self._replace_lambda, {node.name for node in expressions}
+ this=self._replace_lambda(
+ self._parse_conjunction(),
+ {node.name for node in expressions},
),
expressions=expressions,
),
@@ -441,6 +442,7 @@ class Parser(metaclass=_Parser):
exp.With: lambda self: self._parse_with(),
exp.Window: lambda self: self._parse_named_window(),
exp.Qualify: lambda self: self._parse_qualify(),
+ exp.Returning: lambda self: self._parse_returning(),
"JOIN_TYPE": lambda self: self._parse_join_side_and_kind(),
}
@@ -460,6 +462,7 @@ class Parser(metaclass=_Parser):
TokenType.LOAD_DATA: lambda self: self._parse_load_data(),
TokenType.MERGE: lambda self: self._parse_merge(),
TokenType.ROLLBACK: lambda self: self._parse_commit_or_rollback(),
+ TokenType.SET: lambda self: self._parse_set(),
TokenType.UNCACHE: lambda self: self._parse_uncache(),
TokenType.UPDATE: lambda self: self._parse_update(),
TokenType.USE: lambda self: self.expression(
@@ -656,15 +659,15 @@ class Parser(metaclass=_Parser):
}
FUNCTION_PARSERS: t.Dict[str, t.Callable] = {
+ "CAST": lambda self: self._parse_cast(self.STRICT_CAST),
"CONVERT": lambda self: self._parse_convert(self.STRICT_CAST),
- "TRY_CONVERT": lambda self: self._parse_convert(False),
"EXTRACT": lambda self: self._parse_extract(),
"POSITION": lambda self: self._parse_position(),
+ "STRING_AGG": lambda self: self._parse_string_agg(),
"SUBSTRING": lambda self: self._parse_substring(),
"TRIM": lambda self: self._parse_trim(),
- "CAST": lambda self: self._parse_cast(self.STRICT_CAST),
"TRY_CAST": lambda self: self._parse_cast(False),
- "STRING_AGG": lambda self: self._parse_string_agg(),
+ "TRY_CONVERT": lambda self: self._parse_convert(False),
}
QUERY_MODIFIER_PARSERS = {
@@ -684,13 +687,28 @@ class Parser(metaclass=_Parser):
"sample": lambda self: self._parse_table_sample(as_modifier=True),
}
+ SET_PARSERS = {
+ "GLOBAL": lambda self: self._parse_set_item_assignment("GLOBAL"),
+ "LOCAL": lambda self: self._parse_set_item_assignment("LOCAL"),
+ "SESSION": lambda self: self._parse_set_item_assignment("SESSION"),
+ "TRANSACTION": lambda self: self._parse_set_transaction(),
+ }
+
SHOW_PARSERS: t.Dict[str, t.Callable] = {}
- SET_PARSERS: t.Dict[str, t.Callable] = {}
MODIFIABLES = (exp.Subquery, exp.Subqueryable, exp.Table)
TRANSACTION_KIND = {"DEFERRED", "IMMEDIATE", "EXCLUSIVE"}
+ TRANSACTION_CHARACTERISTICS = {
+ "ISOLATION LEVEL REPEATABLE READ",
+ "ISOLATION LEVEL READ COMMITTED",
+ "ISOLATION LEVEL READ UNCOMMITTED",
+ "ISOLATION LEVEL SERIALIZABLE",
+ "READ WRITE",
+ "READ ONLY",
+ }
+
INSERT_ALTERNATIVES = {"ABORT", "FAIL", "IGNORE", "REPLACE", "ROLLBACK"}
WINDOW_ALIAS_TOKENS = ID_VAR_TOKENS - {TokenType.ROWS}
@@ -1775,11 +1793,12 @@ class Parser(metaclass=_Parser):
self, alias_tokens: t.Optional[t.Collection[TokenType]] = None
) -> t.Optional[exp.Expression]:
any_token = self._match(TokenType.ALIAS)
- alias = self._parse_id_var(
- any_token=any_token, tokens=alias_tokens or self.TABLE_ALIAS_TOKENS
+ alias = (
+ self._parse_id_var(any_token=any_token, tokens=alias_tokens or self.TABLE_ALIAS_TOKENS)
+ or self._parse_string_as_identifier()
)
- index = self._index
+ index = self._index
if self._match(TokenType.L_PAREN):
columns = self._parse_csv(self._parse_function_parameter)
self._match_r_paren() if columns else self._retreat(index)
@@ -2046,7 +2065,12 @@ class Parser(metaclass=_Parser):
def _parse_table_parts(self, schema: bool = False) -> exp.Expression:
catalog = None
db = None
- table = (not schema and self._parse_function()) or self._parse_id_var(any_token=False)
+
+ table = (
+ (not schema and self._parse_function())
+ or self._parse_id_var(any_token=False)
+ or self._parse_string_as_identifier()
+ )
while self._match(TokenType.DOT):
if catalog:
@@ -2085,6 +2109,8 @@ class Parser(metaclass=_Parser):
subquery = self._parse_select(table=True)
if subquery:
+ if not subquery.args.get("pivots"):
+ subquery.set("pivots", self._parse_pivots())
return subquery
this = self._parse_table_parts(schema=schema)
@@ -3370,9 +3396,9 @@ class Parser(metaclass=_Parser):
def _parse_window(
self, this: t.Optional[exp.Expression], alias: bool = False
) -> t.Optional[exp.Expression]:
- if self._match(TokenType.FILTER):
- where = self._parse_wrapped(self._parse_where)
- this = self.expression(exp.Filter, this=this, expression=where)
+ if self._match_pair(TokenType.FILTER, TokenType.L_PAREN):
+ this = self.expression(exp.Filter, this=this, expression=self._parse_where())
+ self._match_r_paren()
# T-SQL allows the OVER (...) syntax after WITHIN GROUP.
# https://learn.microsoft.com/en-us/sql/t-sql/functions/percentile-disc-transact-sql?view=sql-server-ver16
@@ -3504,6 +3530,9 @@ class Parser(metaclass=_Parser):
return self.PRIMARY_PARSERS[TokenType.STRING](self, self._prev)
return self._parse_placeholder()
+ def _parse_string_as_identifier(self) -> t.Optional[exp.Expression]:
+ return exp.to_identifier(self._match(TokenType.STRING) and self._prev.text, quoted=True)
+
def _parse_number(self) -> t.Optional[exp.Expression]:
if self._match(TokenType.NUMBER):
return self.PRIMARY_PARSERS[TokenType.NUMBER](self, self._prev)
@@ -3778,23 +3807,6 @@ class Parser(metaclass=_Parser):
)
return self._parse_as_command(start)
- def _parse_show(self) -> t.Optional[exp.Expression]:
- parser = self._find_parser(self.SHOW_PARSERS, self._show_trie) # type: ignore
- if parser:
- return parser(self)
- self._advance()
- return self.expression(exp.Show, this=self._prev.text.upper())
-
- def _default_parse_set_item(self) -> exp.Expression:
- return self.expression(
- exp.SetItem,
- this=self._parse_statement(),
- )
-
- def _parse_set_item(self) -> t.Optional[exp.Expression]:
- parser = self._find_parser(self.SET_PARSERS, self._set_trie) # type: ignore
- return parser(self) if parser else self._default_parse_set_item()
-
def _parse_merge(self) -> exp.Expression:
self._match(TokenType.INTO)
target = self._parse_table()
@@ -3861,8 +3873,71 @@ class Parser(metaclass=_Parser):
expressions=whens,
)
+ def _parse_show(self) -> t.Optional[exp.Expression]:
+ parser = self._find_parser(self.SHOW_PARSERS, self._show_trie) # type: ignore
+ if parser:
+ return parser(self)
+ self._advance()
+ return self.expression(exp.Show, this=self._prev.text.upper())
+
+ def _parse_set_item_assignment(
+ self, kind: t.Optional[str] = None
+ ) -> t.Optional[exp.Expression]:
+ index = self._index
+
+ if kind in {"GLOBAL", "SESSION"} and self._match_text_seq("TRANSACTION"):
+ return self._parse_set_transaction(global_=kind == "GLOBAL")
+
+ left = self._parse_primary() or self._parse_id_var()
+
+ if not self._match_texts(("=", "TO")):
+ self._retreat(index)
+ return None
+
+ right = self._parse_statement() or self._parse_id_var()
+ this = self.expression(
+ exp.EQ,
+ this=left,
+ expression=right,
+ )
+
+ return self.expression(
+ exp.SetItem,
+ this=this,
+ kind=kind,
+ )
+
+ def _parse_set_transaction(self, global_: bool = False) -> exp.Expression:
+ self._match_text_seq("TRANSACTION")
+ characteristics = self._parse_csv(
+ lambda: self._parse_var_from_options(self.TRANSACTION_CHARACTERISTICS)
+ )
+ return self.expression(
+ exp.SetItem,
+ expressions=characteristics,
+ kind="TRANSACTION",
+ **{"global": global_}, # type: ignore
+ )
+
+ def _parse_set_item(self) -> t.Optional[exp.Expression]:
+ parser = self._find_parser(self.SET_PARSERS, self._set_trie) # type: ignore
+ return parser(self) if parser else self._parse_set_item_assignment(kind=None)
+
def _parse_set(self) -> exp.Expression:
- return self.expression(exp.Set, expressions=self._parse_csv(self._parse_set_item))
+ index = self._index
+ set_ = self.expression(exp.Set, expressions=self._parse_csv(self._parse_set_item))
+
+ if self._curr:
+ self._retreat(index)
+ return self._parse_as_command(self._prev)
+
+ return set_
+
+ def _parse_var_from_options(self, options: t.Collection[str]) -> t.Optional[exp.Expression]:
+ for option in options:
+ if self._match_text_seq(*option.split(" ")):
+ return exp.Var(this=option)
+ return None
def _parse_as_command(self, start: Token) -> exp.Command:
while self._curr:
@@ -3874,6 +3949,9 @@ class Parser(metaclass=_Parser):
def _find_parser(
self, parsers: t.Dict[str, t.Callable], trie: t.Dict
) -> t.Optional[t.Callable]:
+ if not self._curr:
+ return None
+
index = self._index
this = []
while True:
@@ -3973,7 +4051,16 @@ class Parser(metaclass=_Parser):
return this
def _replace_lambda(self, node, lambda_variables):
- if isinstance(node, exp.Column):
- if node.name in lambda_variables:
- return node.this
+ for column in node.find_all(exp.Column):
+ if column.parts[0].name in lambda_variables:
+ dot_or_id = column.to_dot() if column.table else column.this
+ parent = column.parent
+
+ while isinstance(parent, exp.Dot):
+ if not isinstance(parent.parent, exp.Dot):
+ parent.replace(dot_or_id)
+ break
+ parent = parent.parent
+ else:
+ column.replace(dot_or_id)
return node