diff options
Diffstat (limited to '')
-rw-r--r-- | sqlglot/expressions.py | 233 |
1 files changed, 203 insertions, 30 deletions
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py index 6800cd5..42652a6 100644 --- a/sqlglot/expressions.py +++ b/sqlglot/expressions.py @@ -128,7 +128,7 @@ class Expression(metaclass=_Expression): """ return self.args.get("expressions") or [] - def text(self, key): + def text(self, key) -> str: """ Returns a textual representation of the argument corresponding to "key". This can only be used for args that are strings or leaf Expression instances, such as identifiers and literals. @@ -143,21 +143,21 @@ class Expression(metaclass=_Expression): return "" @property - def is_string(self): + def is_string(self) -> bool: """ Checks whether a Literal expression is a string. """ return isinstance(self, Literal) and self.args["is_string"] @property - def is_number(self): + def is_number(self) -> bool: """ Checks whether a Literal expression is a number. """ return isinstance(self, Literal) and not self.args["is_string"] @property - def is_int(self): + def is_int(self) -> bool: """ Checks whether a Literal expression is an integer. """ @@ -170,7 +170,12 @@ class Expression(metaclass=_Expression): return False @property - def alias(self): + def is_star(self) -> bool: + """Checks whether an expression is a star.""" + return isinstance(self, Star) or (isinstance(self, Column) and isinstance(self.this, Star)) + + @property + def alias(self) -> str: """ Returns the alias of the expression, or an empty string if it's not aliased. """ @@ -825,10 +830,6 @@ class UserDefinedFunction(Expression): arg_types = {"this": True, "expressions": False, "wrapped": False} -class UserDefinedFunctionKwarg(Expression): - arg_types = {"this": True, "kind": True, "default": False} - - class CharacterSet(Expression): arg_types = {"this": True, "default": False} @@ -870,14 +871,22 @@ class ByteString(Condition): class Column(Condition): - arg_types = {"this": True, "table": False} + arg_types = {"this": True, "table": False, "db": False, "catalog": False} @property - def table(self): + def table(self) -> str: return self.text("table") @property - def output_name(self): + def db(self) -> str: + return self.text("db") + + @property + def catalog(self) -> str: + return self.text("catalog") + + @property + def output_name(self) -> str: return self.name @@ -917,6 +926,14 @@ class AutoIncrementColumnConstraint(ColumnConstraintKind): pass +class CaseSpecificColumnConstraint(ColumnConstraintKind): + arg_types = {"not_": True} + + +class CharacterSetColumnConstraint(ColumnConstraintKind): + arg_types = {"this": True} + + class CheckColumnConstraint(ColumnConstraintKind): pass @@ -929,6 +946,10 @@ class CommentColumnConstraint(ColumnConstraintKind): pass +class DateFormatColumnConstraint(ColumnConstraintKind): + arg_types = {"this": True} + + class DefaultColumnConstraint(ColumnConstraintKind): pass @@ -939,7 +960,14 @@ class EncodeColumnConstraint(ColumnConstraintKind): class GeneratedAsIdentityColumnConstraint(ColumnConstraintKind): # this: True -> ALWAYS, this: False -> BY DEFAULT - arg_types = {"this": False, "start": False, "increment": False} + arg_types = { + "this": False, + "start": False, + "increment": False, + "minvalue": False, + "maxvalue": False, + "cycle": False, + } class NotNullColumnConstraint(ColumnConstraintKind): @@ -950,7 +978,19 @@ class PrimaryKeyColumnConstraint(ColumnConstraintKind): arg_types = {"desc": False} +class TitleColumnConstraint(ColumnConstraintKind): + pass + + class UniqueColumnConstraint(ColumnConstraintKind): + arg_types: t.Dict[str, t.Any] = {} + + +class UppercaseColumnConstraint(ColumnConstraintKind): + arg_types: t.Dict[str, t.Any] = {} + + +class PathColumnConstraint(ColumnConstraintKind): pass @@ -1063,6 +1103,7 @@ class Insert(Expression): "overwrite": False, "exists": False, "partition": False, + "alternative": False, } @@ -1438,6 +1479,16 @@ class IsolatedLoadingProperty(Property): } +class LockingProperty(Property): + arg_types = { + "this": False, + "kind": True, + "for_or_in": True, + "lock_type": True, + "override": False, + } + + class Properties(Expression): arg_types = {"expressions": True} @@ -1463,12 +1514,26 @@ class Properties(Expression): PROPERTY_TO_NAME = {v: k for k, v in NAME_TO_PROPERTY.items()} + # CREATE property locations + # Form: schema specified + # create [POST_CREATE] + # table a [POST_NAME] + # (b int) [POST_SCHEMA] + # with ([POST_WITH]) + # index (b) [POST_INDEX] + # + # Form: alias selection + # create [POST_CREATE] + # table a [POST_NAME] + # as [POST_ALIAS] (select * from b) + # index (c) [POST_INDEX] class Location(AutoName): POST_CREATE = auto() - PRE_SCHEMA = auto() + POST_NAME = auto() + POST_SCHEMA = auto() + POST_WITH = auto() + POST_ALIAS = auto() POST_INDEX = auto() - POST_SCHEMA_ROOT = auto() - POST_SCHEMA_WITH = auto() UNSUPPORTED = auto() @classmethod @@ -1633,6 +1698,14 @@ class Table(Expression): "system_time": False, } + @property + def db(self) -> str: + return self.text("db") + + @property + def catalog(self) -> str: + return self.text("catalog") + # See the TSQL "Querying data in a system-versioned temporal table" page class SystemTime(Expression): @@ -1678,6 +1751,40 @@ class Union(Subqueryable): .limit(expression, dialect=dialect, copy=False, **opts) ) + def select( + self, + *expressions: str | Expression, + append: bool = True, + dialect: DialectType = None, + copy: bool = True, + **opts, + ) -> Union: + """Append to or set the SELECT of the union recursively. + + Example: + >>> from sqlglot import parse_one + >>> parse_one("select a from x union select a from y union select a from z").select("b").sql() + 'SELECT a, b FROM x UNION SELECT a, b FROM y UNION SELECT a, b FROM z' + + Args: + *expressions: the SQL code strings to parse. + If an `Expression` instance is passed, it will be used as-is. + append: if `True`, add to any existing expressions. + Otherwise, this resets the expressions. + 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: + Union: the modified expression. + """ + this = self.copy() if copy else self + this.this.unnest().select(*expressions, append=append, dialect=dialect, copy=False, **opts) + this.expression.unnest().select( + *expressions, append=append, dialect=dialect, copy=False, **opts + ) + return this + @property def named_selects(self): return self.this.unnest().named_selects @@ -1985,7 +2092,14 @@ class Select(Subqueryable): **opts, ) - def select(self, *expressions, append=True, dialect=None, copy=True, **opts) -> Select: + def select( + self, + *expressions: str | Expression, + append: bool = True, + dialect: DialectType = None, + copy: bool = True, + **opts, + ) -> Select: """ Append to or set the SELECT expressions. @@ -1994,13 +2108,13 @@ class Select(Subqueryable): 'SELECT x, y' Args: - *expressions (str | Expression): the SQL code strings to parse. + *expressions: the SQL code strings to parse. If an `Expression` instance is passed, it will be used as-is. - append (bool): if `True`, add to any existing expressions. + append: if `True`, add to any existing expressions. Otherwise, this resets the expressions. - dialect (str): the dialect used to parse the input expressions. - copy (bool): if `False`, modify this expression instance in-place. - opts (kwargs): other options to use to parse the input expressions. + 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: Select: the modified expression. @@ -2399,7 +2513,7 @@ class Star(Expression): class Parameter(Expression): - pass + arg_types = {"this": True, "wrapped": False} class SessionParameter(Expression): @@ -2428,6 +2542,7 @@ class DataType(Expression): "expressions": False, "nested": False, "values": False, + "prefix": False, } class Type(AutoName): @@ -2693,6 +2808,10 @@ class ILike(Binary, Predicate): pass +class ILikeAny(Binary, Predicate): + pass + + class IntDiv(Binary): pass @@ -2709,6 +2828,10 @@ class Like(Binary, Predicate): pass +class LikeAny(Binary, Predicate): + pass + + class LT(Binary, Predicate): pass @@ -3042,7 +3165,7 @@ class DateDiff(Func, TimeUnit): class DateTrunc(Func): - arg_types = {"this": True, "expression": True, "zone": False} + arg_types = {"unit": True, "this": True, "zone": False} class DatetimeAdd(Func, TimeUnit): @@ -3330,6 +3453,16 @@ class Reduce(Func): arg_types = {"this": True, "initial": True, "merge": True, "finish": False} +class RegexpExtract(Func): + arg_types = { + "this": True, + "expression": True, + "position": False, + "occurrence": False, + "group": False, + } + + class RegexpLike(Func): arg_types = {"this": True, "expression": True, "flag": False} @@ -3519,6 +3652,10 @@ class Week(Func): arg_types = {"this": True, "mode": False} +class XMLTable(Func): + arg_types = {"this": True, "passing": False, "columns": False, "by_ref": False} + + class Year(Func): pass @@ -3566,6 +3703,7 @@ def maybe_parse( into: t.Optional[IntoType] = None, dialect: DialectType = None, prefix: t.Optional[str] = None, + copy: bool = False, **opts, ) -> Expression: """Gracefully handle a possible string or expression. @@ -3583,6 +3721,7 @@ def maybe_parse( input expression is a SQL string). prefix: a string to prefix the sql with before it gets parsed (automatically includes a space) + copy: whether or not to copy the expression. **opts: other options to use to parse the input expressions (again, in the case that an input expression is a SQL string). @@ -3590,6 +3729,8 @@ def maybe_parse( Expression: the parsed or given expression. """ if isinstance(sql_or_expression, Expression): + if copy: + return sql_or_expression.copy() return sql_or_expression import sqlglot @@ -3818,7 +3959,7 @@ def except_(left, right, distinct=True, dialect=None, **opts): return Except(this=left, expression=right, distinct=distinct) -def select(*expressions, dialect=None, **opts) -> Select: +def select(*expressions: str | Expression, dialect: DialectType = None, **opts) -> Select: """ Initializes a syntax tree from one or multiple SELECT expressions. @@ -3827,9 +3968,9 @@ def select(*expressions, dialect=None, **opts) -> Select: 'SELECT col1, col2 FROM tbl' Args: - *expressions (str | Expression): the SQL code string to parse as the expressions of a + *expressions: the SQL code string to parse as the expressions of a SELECT statement. If an Expression instance is passed, this is used as-is. - dialect (str): the dialect used to parse the input expressions (in the case that an + dialect: the dialect used to parse the input expressions (in the case that an input expression is a SQL string). **opts: other options to use to parse the input expressions (again, in the case that an input expression is a SQL string). @@ -4219,19 +4360,27 @@ def subquery(expression, alias=None, dialect=None, **opts): return Select().from_(expression, dialect=dialect, **opts) -def column(col, table=None, quoted=None) -> Column: +def column( + col: str | Identifier, + table: t.Optional[str | Identifier] = None, + schema: t.Optional[str | Identifier] = None, + quoted: t.Optional[bool] = None, +) -> Column: """ Build a Column. Args: - col (str | Expression): column name - table (str | Expression): table name + col: column name + table: table name + schema: schema name + quoted: whether or not to force quote each part Returns: Column: column instance """ return Column( this=to_identifier(col, quoted=quoted), table=to_identifier(table, quoted=quoted), + schema=to_identifier(schema, quoted=quoted), ) @@ -4314,6 +4463,30 @@ def values( ) +def var(name: t.Optional[str | Expression]) -> Var: + """Build a SQL variable. + + Example: + >>> repr(var('x')) + '(VAR this: x)' + + >>> repr(var(column('x', table='y'))) + '(VAR this: x)' + + Args: + name: The name of the var or an expression who's name will become the var. + + Returns: + The new variable node. + """ + if not name: + raise ValueError(f"Cannot convert empty name into var.") + + if isinstance(name, Expression): + name = name.name + return Var(this=name) + + def rename_table(old_name: str | Table, new_name: str | Table) -> AlterTable: """Build ALTER TABLE... RENAME... expression |