summaryrefslogtreecommitdiffstats
path: root/sqlglot/expressions.py
diff options
context:
space:
mode:
Diffstat (limited to 'sqlglot/expressions.py')
-rw-r--r--sqlglot/expressions.py233
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