1
0
Fork 0

Merging upstream version 9.0.1.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-13 14:48:46 +01:00
parent ebb36a5fc5
commit 4483b8ff47
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
87 changed files with 7994 additions and 421 deletions

View file

@ -11,6 +11,7 @@ from sqlglot.helper import (
camel_to_snake_case,
ensure_list,
list_get,
split_num_words,
subclasses,
)
@ -108,6 +109,8 @@ class Expression(metaclass=_Expression):
@property
def alias_or_name(self):
if isinstance(self, Null):
return "NULL"
return self.alias or self.name
def __deepcopy__(self, memo):
@ -659,6 +662,10 @@ class HexString(Condition):
pass
class ByteString(Condition):
pass
class Column(Condition):
arg_types = {"this": True, "table": False}
@ -725,7 +732,7 @@ class Constraint(Expression):
class Delete(Expression):
arg_types = {"with": False, "this": True, "where": False}
arg_types = {"with": False, "this": True, "using": False, "where": False}
class Drop(Expression):
@ -1192,6 +1199,7 @@ QUERY_MODIFIERS = {
class Table(Expression):
arg_types = {
"this": True,
"alias": False,
"db": False,
"catalog": False,
"laterals": False,
@ -1323,6 +1331,7 @@ class Select(Subqueryable):
*expressions (str or Expression): the SQL code strings to parse.
If a `Group` instance is passed, this is used as-is.
If another `Expression` instance is passed, it will be wrapped in a `Group`.
If nothing is passed in then a group by is not applied to the expression
append (bool): if `True`, add to any existing expressions.
Otherwise, this flattens all the `Group` expression into a single expression.
dialect (str): the dialect used to parse the input expression.
@ -1332,6 +1341,8 @@ class Select(Subqueryable):
Returns:
Select: the modified expression.
"""
if not expressions:
return self if not copy else self.copy()
return _apply_child_list_builder(
*expressions,
instance=self,
@ -2239,6 +2250,11 @@ class ArrayAny(Func):
arg_types = {"this": True, "expression": True}
class ArrayConcat(Func):
arg_types = {"this": True, "expressions": False}
is_var_len_args = True
class ArrayContains(Func):
arg_types = {"this": True, "expression": True}
@ -2570,7 +2586,7 @@ class SortArray(Func):
class Split(Func):
arg_types = {"this": True, "expression": True}
arg_types = {"this": True, "expression": True, "limit": False}
# Start may be omitted in the case of postgres
@ -3209,29 +3225,49 @@ def to_identifier(alias, quoted=None):
return identifier
def to_table(sql_path, **kwargs):
def to_table(sql_path: str, **kwargs) -> Table:
"""
Create a table expression from a `[catalog].[schema].[table]` sql path. Catalog and schema are optional.
Example:
>>> to_table('catalog.db.table_name').sql()
'catalog.db.table_name'
If a table is passed in then that table is returned.
Args:
sql_path(str): `[catalog].[schema].[table]` string
sql_path(str|Table): `[catalog].[schema].[table]` string
Returns:
Table: A table expression
"""
table_parts = sql_path.split(".")
catalog, db, table_name = [
to_identifier(x) if x is not None else x for x in [None] * (3 - len(table_parts)) + table_parts
]
if sql_path is None or isinstance(sql_path, Table):
return sql_path
if not isinstance(sql_path, str):
raise ValueError(f"Invalid type provided for a table: {type(sql_path)}")
catalog, db, table_name = [to_identifier(x) for x in split_num_words(sql_path, ".", 3)]
return Table(this=table_name, db=db, catalog=catalog, **kwargs)
def to_column(sql_path: str, **kwargs) -> Column:
"""
Create a column from a `[table].[column]` sql path. Schema is optional.
If a column is passed in then that column is returned.
Args:
sql_path: `[table].[column]` string
Returns:
Table: A column expression
"""
if sql_path is None or isinstance(sql_path, Column):
return sql_path
if not isinstance(sql_path, str):
raise ValueError(f"Invalid type provided for column: {type(sql_path)}")
table_name, column_name = [to_identifier(x) for x in split_num_words(sql_path, ".", 2)]
return Column(this=column_name, table=table_name, **kwargs)
def alias_(expression, alias, table=False, dialect=None, quoted=None, **opts):
"""
Create an Alias expression.
Expample:
Example:
>>> alias_('foo', 'bar').sql()
'foo AS bar'
@ -3249,7 +3285,16 @@ def alias_(expression, alias, table=False, dialect=None, quoted=None, **opts):
"""
exp = maybe_parse(expression, dialect=dialect, **opts)
alias = to_identifier(alias, quoted=quoted)
alias = TableAlias(this=alias) if table else alias
if table:
expression.set("alias", TableAlias(this=alias))
return expression
# We don't set the "alias" arg for Window expressions, because that would add an IDENTIFIER node in
# the AST, representing a "named_window" [1] construct (eg. bigquery). What we want is an ALIAS node
# for the complete Window expression.
#
# [1]: https://cloud.google.com/bigquery/docs/reference/standard-sql/window-function-calls
if "alias" in exp.arg_types and not isinstance(exp, Window):
exp = exp.copy()
@ -3295,7 +3340,7 @@ def column(col, table=None, quoted=None):
)
def table_(table, db=None, catalog=None, quoted=None):
def table_(table, db=None, catalog=None, quoted=None, alias=None):
"""Build a Table.
Args:
@ -3310,6 +3355,7 @@ def table_(table, db=None, catalog=None, quoted=None):
this=to_identifier(table, quoted=quoted),
db=to_identifier(db, quoted=quoted),
catalog=to_identifier(catalog, quoted=quoted),
alias=TableAlias(this=to_identifier(alias)) if alias else None,
)
@ -3453,7 +3499,7 @@ def replace_tables(expression, mapping):
Examples:
>>> from sqlglot import exp, parse_one
>>> replace_tables(parse_one("select * from a.b"), {"a.b": "c"}).sql()
'SELECT * FROM "c"'
'SELECT * FROM c'
Returns:
The mapped expression
@ -3463,7 +3509,10 @@ def replace_tables(expression, mapping):
if isinstance(node, Table):
new_name = mapping.get(table_name(node))
if new_name:
return table_(*reversed(new_name.split(".")), quoted=True)
return to_table(
new_name,
**{k: v for k, v in node.args.items() if k not in ("this", "db", "catalog")},
)
return node
return expression.transform(_replace_tables)