diff options
Diffstat (limited to 'sqlglot/expressions.py')
-rw-r--r-- | sqlglot/expressions.py | 137 |
1 files changed, 131 insertions, 6 deletions
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py index f2ffd12..39f4452 100644 --- a/sqlglot/expressions.py +++ b/sqlglot/expressions.py @@ -1,3 +1,4 @@ +import datetime import numbers import re from collections import deque @@ -508,7 +509,7 @@ class DerivedTable(Expression): return [select.alias_or_name for select in self.selects] -class Unionable: +class Unionable(Expression): def union(self, expression, distinct=True, dialect=None, **opts): """ Builds a UNION expression. @@ -614,6 +615,10 @@ class Create(Expression): } +class Describe(Expression): + pass + + class UserDefinedFunction(Expression): arg_types = {"this": True, "expressions": False} @@ -741,6 +746,11 @@ class Check(Expression): pass +class Directory(Expression): + # https://spark.apache.org/docs/3.0.0-preview/sql-ref-syntax-dml-insert-overwrite-directory-hive.html + arg_types = {"this": True, "local": False, "row_format": False} + + class ForeignKey(Expression): arg_types = { "expressions": True, @@ -804,6 +814,18 @@ class Introducer(Expression): arg_types = {"this": True, "expression": True} +class LoadData(Expression): + arg_types = { + "this": True, + "local": False, + "overwrite": False, + "inpath": True, + "partition": False, + "input_format": False, + "serde": False, + } + + class Partition(Expression): pass @@ -1037,6 +1059,18 @@ class Reference(Expression): arg_types = {"this": True, "expressions": True} +class RowFormat(Expression): + # https://cwiki.apache.org/confluence/display/hive/languagemanual+dml + arg_types = { + "fields": False, + "escaped": False, + "collection_items": False, + "map_keys": False, + "lines": False, + "null": False, + } + + class Tuple(Expression): arg_types = {"expressions": False} @@ -1071,6 +1105,14 @@ class Subqueryable(Unionable): return [] return with_.expressions + @property + def selects(self): + raise NotImplementedError("Subqueryable objects must implement `selects`") + + @property + def named_selects(self): + raise NotImplementedError("Subqueryable objects must implement `named_selects`") + def with_( self, alias, @@ -1158,7 +1200,7 @@ class Table(Expression): } -class Union(Subqueryable, Expression): +class Union(Subqueryable): arg_types = { "with": False, "this": True, @@ -1169,7 +1211,11 @@ class Union(Subqueryable, Expression): @property def named_selects(self): - return self.args["this"].unnest().named_selects + return self.this.unnest().named_selects + + @property + def selects(self): + return self.this.unnest().selects @property def left(self): @@ -1222,7 +1268,7 @@ class Schema(Expression): arg_types = {"this": False, "expressions": True} -class Select(Subqueryable, Expression): +class Select(Subqueryable): arg_types = { "with": False, "expressions": False, @@ -2075,7 +2121,7 @@ class Bracket(Condition): class Distinct(Expression): - arg_types = {"this": False, "on": False} + arg_types = {"expressions": False, "on": False} class In(Predicate): @@ -2233,6 +2279,14 @@ class Case(Func): class Cast(Func): arg_types = {"this": True, "to": True} + @property + def name(self): + return self.this.name + + @property + def to(self): + return self.args["to"] + class TryCast(Cast): pass @@ -2666,7 +2720,7 @@ def _norm_args(expression): else: arg = _norm_arg(arg) - if arg is not None: + if arg is not None and arg is not False: args[k] = arg return args @@ -3012,6 +3066,30 @@ def update(table, properties, where=None, from_=None, dialect=None, **opts): return update +def delete(table, where=None, dialect=None, **opts): + """ + Builds a delete statement. + + Example: + >>> delete("my_table", where="id > 1").sql() + '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. + **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), + ) + + def condition(expression, dialect=None, **opts): """ Initialize a logical condition expression. @@ -3131,6 +3209,25 @@ def to_identifier(alias, quoted=None): return identifier +def to_table(sql_path, **kwargs): + """ + 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' + + Args: + sql_path(str): `[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 + ] + return Table(this=table_name, db=db, catalog=catalog, **kwargs) + + def alias_(expression, alias, table=False, dialect=None, quoted=None, **opts): """ Create an Alias expression. @@ -3216,6 +3313,28 @@ def table_(table, db=None, catalog=None, quoted=None): ) +def values(values, alias=None): + """Build VALUES statement. + + Example: + >>> values([(1, '2')]).sql() + "VALUES (1, '2')" + + Args: + values (list[tuple[str | Expression]]): values statements that will be converted to SQL + alias (str): optional alias + dialect (str): the dialect used to parse the input expression. + **opts: other options to use to parse the input expressions. + + Returns: + Values: the Values expression object + """ + return Values( + expressions=[convert(tup) for tup in values], + alias=to_identifier(alias) if alias else None, + ) + + def convert(value): """Convert a python value into an expression object. @@ -3246,6 +3365,12 @@ def convert(value): keys=[convert(k) for k in value.keys()], values=[convert(v) for v in value.values()], ) + if isinstance(value, datetime.datetime): + datetime_literal = Literal.string(value.strftime("%Y-%m-%d %H:%M:%S")) + return TimeStrToTime(this=datetime_literal) + if isinstance(value, datetime.date): + date_literal = Literal.string(value.strftime("%Y-%m-%d")) + return DateStrToDate(this=date_literal) raise ValueError(f"Cannot convert {value}") |