1
0
Fork 0

Merging upstream version 11.4.1.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-13 15:46:19 +01:00
parent ecb42ec17f
commit 63746a3e92
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
89 changed files with 35352 additions and 33081 deletions

View file

@ -301,7 +301,7 @@ class Expression(metaclass=_Expression):
the specified types.
Args:
expression_types (type): the expression type(s) to match.
expression_types: the expression type(s) to match.
Returns:
The node which matches the criteria or None if no such node was found.
@ -314,7 +314,7 @@ class Expression(metaclass=_Expression):
yields those that match at least one of the specified expression types.
Args:
expression_types (type): the expression type(s) to match.
expression_types: the expression type(s) to match.
Returns:
The generator object.
@ -328,7 +328,7 @@ class Expression(metaclass=_Expression):
Returns a nearest parent matching expression_types.
Args:
expression_types (type): the expression type(s) to match.
expression_types: the expression type(s) to match.
Returns:
The parent node.
@ -336,8 +336,7 @@ class Expression(metaclass=_Expression):
ancestor = self.parent
while ancestor and not isinstance(ancestor, expression_types):
ancestor = ancestor.parent
# ignore type because mypy doesn't know that we're checking type in the loop
return ancestor # type: ignore[return-value]
return t.cast(E, ancestor)
@property
def parent_select(self):
@ -549,8 +548,12 @@ class Expression(metaclass=_Expression):
def pop(self):
"""
Remove this expression from its AST.
Returns:
The popped expression.
"""
self.replace(None)
return self
def assert_is(self, type_):
"""
@ -626,6 +629,7 @@ IntoType = t.Union[
t.Type[Expression],
t.Collection[t.Union[str, t.Type[Expression]]],
]
ExpOrStr = t.Union[str, Expression]
class Condition(Expression):
@ -809,7 +813,7 @@ class Describe(Expression):
class Set(Expression):
arg_types = {"expressions": True}
arg_types = {"expressions": False}
class SetItem(Expression):
@ -905,6 +909,23 @@ class Column(Condition):
def output_name(self) -> str:
return self.name
@property
def parts(self) -> t.List[Identifier]:
"""Return the parts of a column in order catalog, db, table, name."""
return [part for part in reversed(list(self.args.values())) if part]
def to_dot(self) -> Dot:
"""Converts the column into a dot expression."""
parts = self.parts
parent = self.parent
while parent:
if isinstance(parent, Dot):
parts.append(parent.expression)
parent = parent.parent
return Dot.build(parts)
class ColumnDef(Expression):
arg_types = {
@ -1033,6 +1054,113 @@ class Constraint(Expression):
class Delete(Expression):
arg_types = {"with": False, "this": False, "using": False, "where": False, "returning": False}
def delete(
self,
table: ExpOrStr,
dialect: DialectType = None,
copy: bool = True,
**opts,
) -> Delete:
"""
Create a DELETE expression or replace the table on an existing DELETE expression.
Example:
>>> delete("tbl").sql()
'DELETE FROM tbl'
Args:
table: the table from which to delete.
dialect: the dialect used to parse the input expression.
copy: if `False`, modify this expression instance in-place.
opts: other options to use to parse the input expressions.
Returns:
Delete: the modified expression.
"""
return _apply_builder(
expression=table,
instance=self,
arg="this",
dialect=dialect,
into=Table,
copy=copy,
**opts,
)
def where(
self,
*expressions: ExpOrStr,
append: bool = True,
dialect: DialectType = None,
copy: bool = True,
**opts,
) -> Delete:
"""
Append to or set the WHERE expressions.
Example:
>>> delete("tbl").where("x = 'a' OR x < 'b'").sql()
"DELETE FROM tbl WHERE x = 'a' OR x < 'b'"
Args:
*expressions: the SQL code strings to parse.
If an `Expression` instance is passed, it will be used as-is.
Multiple expressions are combined with an AND operator.
append: if `True`, AND the new expressions to any existing expression.
Otherwise, this resets the expression.
dialect: the dialect used to parse the input expressions.
copy: if `False`, modify this expression instance in-place.
opts: other options to use to parse the input expressions.
Returns:
Delete: the modified expression.
"""
return _apply_conjunction_builder(
*expressions,
instance=self,
arg="where",
append=append,
into=Where,
dialect=dialect,
copy=copy,
**opts,
)
def returning(
self,
expression: ExpOrStr,
dialect: DialectType = None,
copy: bool = True,
**opts,
) -> Delete:
"""
Set the RETURNING expression. Not supported by all dialects.
Example:
>>> delete("tbl").returning("*", dialect="postgres").sql()
'DELETE FROM tbl RETURNING *'
Args:
expression: the SQL code strings to parse.
If an `Expression` instance is passed, it will be used as-is.
dialect: the dialect used to parse the input expressions.
copy: if `False`, modify this expression instance in-place.
opts: other options to use to parse the input expressions.
Returns:
Delete: the modified expression.
"""
return _apply_builder(
expression=expression,
instance=self,
arg="returning",
prefix="RETURNING",
dialect=dialect,
copy=copy,
into=Returning,
**opts,
)
class Drop(Expression):
arg_types = {
@ -1824,7 +1952,7 @@ class Union(Subqueryable):
def select(
self,
*expressions: str | Expression,
*expressions: ExpOrStr,
append: bool = True,
dialect: DialectType = None,
copy: bool = True,
@ -2170,7 +2298,7 @@ class Select(Subqueryable):
def select(
self,
*expressions: str | Expression,
*expressions: ExpOrStr,
append: bool = True,
dialect: DialectType = None,
copy: bool = True,
@ -2875,6 +3003,20 @@ class Dot(Binary):
def name(self) -> str:
return self.expression.name
@classmethod
def build(self, expressions: t.Sequence[Expression]) -> Dot:
"""Build a Dot object with a sequence of expressions."""
if len(expressions) < 2:
raise ValueError(f"Dot requires >= 2 expressions.")
a, b, *expressions = expressions
dot = Dot(this=a, expression=b)
for expression in expressions:
dot = Dot(this=dot, expression=expression)
return dot
class DPipe(Binary):
pass
@ -3049,7 +3191,7 @@ class TimeUnit(Expression):
def __init__(self, **args):
unit = args.get("unit")
if isinstance(unit, Column):
if isinstance(unit, (Column, Literal)):
args["unit"] = Var(this=unit.name)
elif isinstance(unit, Week):
unit.set("this", Var(this=unit.this.name))
@ -3261,6 +3403,10 @@ class Count(AggFunc):
arg_types = {"this": False}
class CountIf(AggFunc):
pass
class CurrentDate(Func):
arg_types = {"this": False}
@ -3407,6 +3553,10 @@ class Explode(Func):
pass
class ExponentialTimeDecayedAvg(AggFunc):
arg_types = {"this": True, "time": False, "decay": False}
class Floor(Func):
arg_types = {"this": True, "decimals": False}
@ -3420,10 +3570,18 @@ class GroupConcat(Func):
arg_types = {"this": True, "separator": False}
class GroupUniqArray(AggFunc):
arg_types = {"this": True, "size": False}
class Hex(Func):
pass
class Histogram(AggFunc):
arg_types = {"this": True, "bins": False}
class If(Func):
arg_types = {"this": True, "true": True, "false": False}
@ -3493,7 +3651,11 @@ class Log10(Func):
class LogicalOr(AggFunc):
_sql_names = ["LOGICAL_OR", "BOOL_OR"]
_sql_names = ["LOGICAL_OR", "BOOL_OR", "BOOLOR_AGG"]
class LogicalAnd(AggFunc):
_sql_names = ["LOGICAL_AND", "BOOL_AND", "BOOLAND_AGG"]
class Lower(Func):
@ -3561,6 +3723,7 @@ class Quantile(AggFunc):
# https://clickhouse.com/docs/en/sql-reference/aggregate-functions/reference/quantiles/#quantiles
class Quantiles(AggFunc):
arg_types = {"parameters": True, "expressions": True}
is_var_len_args = True
class QuantileIf(AggFunc):
@ -3830,7 +3993,7 @@ ALL_FUNCTIONS = subclasses(__name__, Func, (AggFunc, Anonymous, Func))
# Helpers
def maybe_parse(
sql_or_expression: str | Expression,
sql_or_expression: ExpOrStr,
*,
into: t.Optional[IntoType] = None,
dialect: DialectType = None,
@ -4091,7 +4254,7 @@ def except_(left, right, distinct=True, dialect=None, **opts):
return Except(this=left, expression=right, distinct=distinct)
def select(*expressions: str | Expression, dialect: DialectType = None, **opts) -> Select:
def select(*expressions: ExpOrStr, dialect: DialectType = None, **opts) -> Select:
"""
Initializes a syntax tree from one or multiple SELECT expressions.
@ -4135,7 +4298,14 @@ def from_(*expressions, dialect=None, **opts) -> Select:
return Select().from_(*expressions, dialect=dialect, **opts)
def update(table, properties, where=None, from_=None, dialect=None, **opts) -> Update:
def update(
table: str | Table,
properties: dict,
where: t.Optional[ExpOrStr] = None,
from_: t.Optional[ExpOrStr] = None,
dialect: DialectType = None,
**opts,
) -> Update:
"""
Creates an update statement.
@ -4144,18 +4314,18 @@ def update(table, properties, where=None, from_=None, dialect=None, **opts) -> U
"UPDATE my_table SET x = 1, y = '2', z = NULL FROM baz WHERE id > 1"
Args:
*properties (Dict[str, Any]): dictionary of properties to set which are
*properties: dictionary of properties to set which are
auto converted to sql objects eg None -> NULL
where (str): sql conditional parsed into a WHERE statement
from_ (str): sql statement parsed into a FROM statement
dialect (str): the dialect used to parse the input expressions.
where: sql conditional parsed into a WHERE statement
from_: sql statement parsed into a FROM statement
dialect: the dialect used to parse the input expressions.
**opts: other options to use to parse the input expressions.
Returns:
Update: the syntax tree for the UPDATE statement.
"""
update = Update(this=maybe_parse(table, into=Table, dialect=dialect))
update.set(
update_expr = Update(this=maybe_parse(table, into=Table, dialect=dialect))
update_expr.set(
"expressions",
[
EQ(this=maybe_parse(k, dialect=dialect, **opts), expression=convert(v))
@ -4163,21 +4333,27 @@ def update(table, properties, where=None, from_=None, dialect=None, **opts) -> U
],
)
if from_:
update.set(
update_expr.set(
"from",
maybe_parse(from_, into=From, dialect=dialect, prefix="FROM", **opts),
)
if isinstance(where, Condition):
where = Where(this=where)
if where:
update.set(
update_expr.set(
"where",
maybe_parse(where, into=Where, dialect=dialect, prefix="WHERE", **opts),
)
return update
return update_expr
def delete(table, where=None, dialect=None, **opts) -> Delete:
def delete(
table: ExpOrStr,
where: t.Optional[ExpOrStr] = None,
returning: t.Optional[ExpOrStr] = None,
dialect: DialectType = None,
**opts,
) -> Delete:
"""
Builds a delete statement.
@ -4186,19 +4362,20 @@ def delete(table, where=None, dialect=None, **opts) -> Delete:
'DELETE FROM my_table WHERE id > 1'
Args:
where (str|Condition): sql conditional parsed into a WHERE statement
dialect (str): the dialect used to parse the input expressions.
where: sql conditional parsed into a WHERE statement
returning: sql conditional parsed into a RETURNING statement
dialect: the dialect used to parse the input expressions.
**opts: other options to use to parse the input expressions.
Returns:
Delete: the syntax tree for the DELETE statement.
"""
return Delete(
this=maybe_parse(table, into=Table, dialect=dialect, **opts),
where=Where(this=where)
if isinstance(where, Condition)
else maybe_parse(where, into=Where, dialect=dialect, prefix="WHERE", **opts),
)
delete_expr = Delete().delete(table, dialect=dialect, copy=False, **opts)
if where:
delete_expr = delete_expr.where(where, dialect=dialect, copy=False, **opts)
if returning:
delete_expr = delete_expr.returning(returning, dialect=dialect, copy=False, **opts)
return delete_expr
def condition(expression, dialect=None, **opts) -> Condition:
@ -4414,7 +4591,7 @@ def to_column(sql_path: str | Column, **kwargs) -> Column:
def alias_(
expression: str | Expression,
expression: ExpOrStr,
alias: str | Identifier,
table: bool | t.Sequence[str | Identifier] = False,
quoted: t.Optional[bool] = None,
@ -4516,7 +4693,7 @@ def column(
)
def cast(expression: str | Expression, to: str | DataType | DataType.Type, **opts) -> Cast:
def cast(expression: ExpOrStr, to: str | DataType | DataType.Type, **opts) -> Cast:
"""Cast an expression to a data type.
Example:
@ -4595,7 +4772,7 @@ def values(
)
def var(name: t.Optional[str | Expression]) -> Var:
def var(name: t.Optional[ExpOrStr]) -> Var:
"""Build a SQL variable.
Example:
@ -4612,7 +4789,7 @@ def var(name: t.Optional[str | Expression]) -> Var:
The new variable node.
"""
if not name:
raise ValueError(f"Cannot convert empty name into var.")
raise ValueError("Cannot convert empty name into var.")
if isinstance(name, Expression):
name = name.name
@ -4682,7 +4859,7 @@ def convert(value) -> Expression:
raise ValueError(f"Cannot convert {value}")
def replace_children(expression, fun):
def replace_children(expression, fun, *args, **kwargs):
"""
Replace children of an expression with the result of a lambda fun(child) -> exp.
"""
@ -4694,7 +4871,7 @@ def replace_children(expression, fun):
for cn in child_nodes:
if isinstance(cn, Expression):
for child_node in ensure_collection(fun(cn)):
for child_node in ensure_collection(fun(cn, *args, **kwargs)):
new_child_nodes.append(child_node)
child_node.parent = expression
child_node.arg_key = k