altimate-code 0.5.2 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/bin/altimate +6 -0
- package/bin/altimate-code +6 -0
- package/dbt-tools/bin/altimate-dbt +2 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/altimate/__init__.py +0 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/altimate/fetch_schema.py +35 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/altimate/utils.py +353 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/altimate/validate_sql.py +114 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/__init__.py +178 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/__main__.py +96 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/_typing.py +17 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/__init__.py +3 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/sql/__init__.py +18 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/sql/_typing.py +18 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/sql/column.py +332 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/sql/dataframe.py +866 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/sql/functions.py +1267 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/sql/group.py +59 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/sql/normalize.py +78 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/sql/operations.py +53 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/sql/readwriter.py +108 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/sql/session.py +190 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/sql/transforms.py +9 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/sql/types.py +212 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/sql/util.py +32 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dataframe/sql/window.py +134 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/__init__.py +118 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/athena.py +166 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/bigquery.py +1331 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/clickhouse.py +1393 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/databricks.py +131 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/dialect.py +1915 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/doris.py +561 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/drill.py +157 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/druid.py +20 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/duckdb.py +1159 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/dune.py +16 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/hive.py +787 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/materialize.py +94 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/mysql.py +1324 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/oracle.py +378 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/postgres.py +778 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/presto.py +788 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/prql.py +203 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/redshift.py +448 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/risingwave.py +78 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/snowflake.py +1464 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/spark.py +202 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/spark2.py +349 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/sqlite.py +320 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/starrocks.py +343 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/tableau.py +61 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/teradata.py +356 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/trino.py +115 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/dialects/tsql.py +1403 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/diff.py +456 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/errors.py +93 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/executor/__init__.py +95 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/executor/context.py +101 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/executor/env.py +246 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/executor/python.py +460 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/executor/table.py +155 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/expressions.py +8870 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/generator.py +4993 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/helper.py +582 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/jsonpath.py +227 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/lineage.py +423 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/__init__.py +11 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/annotate_types.py +589 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/canonicalize.py +222 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/eliminate_ctes.py +43 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/eliminate_joins.py +181 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/eliminate_subqueries.py +189 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/isolate_table_selects.py +50 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/merge_subqueries.py +415 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/normalize.py +200 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/normalize_identifiers.py +64 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/optimize_joins.py +91 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/optimizer.py +94 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/pushdown_predicates.py +222 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/pushdown_projections.py +172 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/qualify.py +104 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/qualify_columns.py +1024 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/qualify_tables.py +155 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/scope.py +904 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/simplify.py +1587 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/optimizer/unnest_subqueries.py +302 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/parser.py +8501 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/planner.py +463 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/schema.py +588 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/serde.py +68 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/time.py +687 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/tokens.py +1520 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/transforms.py +1020 -0
- package/dbt-tools/dist/altimate_python_packages/altimate_packages/sqlglot/trie.py +81 -0
- package/dbt-tools/dist/altimate_python_packages/dbt_core_integration.py +825 -0
- package/dbt-tools/dist/altimate_python_packages/dbt_utils.py +157 -0
- package/dbt-tools/dist/index.js +23859 -0
- package/package.json +13 -13
- package/postinstall.mjs +42 -0
- package/skills/altimate-setup/SKILL.md +31 -0
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import typing as t
|
|
4
|
+
|
|
5
|
+
from sqlglot.executor.env import ENV
|
|
6
|
+
|
|
7
|
+
if t.TYPE_CHECKING:
|
|
8
|
+
from sqlglot.executor.table import Table, TableIter
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class Context:
|
|
12
|
+
"""
|
|
13
|
+
Execution context for sql expressions.
|
|
14
|
+
|
|
15
|
+
Context is used to hold relevant data tables which can then be queried on with eval.
|
|
16
|
+
|
|
17
|
+
References to columns can either be scalar or vectors. When set_row is used, column references
|
|
18
|
+
evaluate to scalars while set_range evaluates to vectors. This allows convenient and efficient
|
|
19
|
+
evaluation of aggregation functions.
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
def __init__(self, tables: t.Dict[str, Table], env: t.Optional[t.Dict] = None) -> None:
|
|
23
|
+
"""
|
|
24
|
+
Args
|
|
25
|
+
tables: representing the scope of the current execution context.
|
|
26
|
+
env: dictionary of functions within the execution context.
|
|
27
|
+
"""
|
|
28
|
+
self.tables = tables
|
|
29
|
+
self._table: t.Optional[Table] = None
|
|
30
|
+
self.range_readers = {name: table.range_reader for name, table in self.tables.items()}
|
|
31
|
+
self.row_readers = {name: table.reader for name, table in tables.items()}
|
|
32
|
+
self.env = {**ENV, **(env or {}), "scope": self.row_readers}
|
|
33
|
+
|
|
34
|
+
def eval(self, code):
|
|
35
|
+
return eval(code, self.env)
|
|
36
|
+
|
|
37
|
+
def eval_tuple(self, codes):
|
|
38
|
+
return tuple(self.eval(code) for code in codes)
|
|
39
|
+
|
|
40
|
+
@property
|
|
41
|
+
def table(self) -> Table:
|
|
42
|
+
if self._table is None:
|
|
43
|
+
self._table = list(self.tables.values())[0]
|
|
44
|
+
|
|
45
|
+
for other in self.tables.values():
|
|
46
|
+
if self._table.columns != other.columns:
|
|
47
|
+
raise Exception("Columns are different.")
|
|
48
|
+
if len(self._table.rows) != len(other.rows):
|
|
49
|
+
raise Exception("Rows are different.")
|
|
50
|
+
|
|
51
|
+
return self._table
|
|
52
|
+
|
|
53
|
+
def add_columns(self, *columns: str) -> None:
|
|
54
|
+
for table in self.tables.values():
|
|
55
|
+
table.add_columns(*columns)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def columns(self) -> t.Tuple:
|
|
59
|
+
return self.table.columns
|
|
60
|
+
|
|
61
|
+
def __iter__(self):
|
|
62
|
+
self.env["scope"] = self.row_readers
|
|
63
|
+
for i in range(len(self.table.rows)):
|
|
64
|
+
for table in self.tables.values():
|
|
65
|
+
reader = table[i]
|
|
66
|
+
yield reader, self
|
|
67
|
+
|
|
68
|
+
def table_iter(self, table: str) -> TableIter:
|
|
69
|
+
self.env["scope"] = self.row_readers
|
|
70
|
+
return iter(self.tables[table])
|
|
71
|
+
|
|
72
|
+
def filter(self, condition) -> None:
|
|
73
|
+
rows = [reader.row for reader, _ in self if self.eval(condition)]
|
|
74
|
+
|
|
75
|
+
for table in self.tables.values():
|
|
76
|
+
table.rows = rows
|
|
77
|
+
|
|
78
|
+
def sort(self, key) -> None:
|
|
79
|
+
def sort_key(row: t.Tuple) -> t.Tuple:
|
|
80
|
+
self.set_row(row)
|
|
81
|
+
return tuple((t is None, t) for t in self.eval_tuple(key))
|
|
82
|
+
|
|
83
|
+
self.table.rows.sort(key=sort_key)
|
|
84
|
+
|
|
85
|
+
def set_row(self, row: t.Tuple) -> None:
|
|
86
|
+
for table in self.tables.values():
|
|
87
|
+
table.reader.row = row
|
|
88
|
+
self.env["scope"] = self.row_readers
|
|
89
|
+
|
|
90
|
+
def set_index(self, index: int) -> None:
|
|
91
|
+
for table in self.tables.values():
|
|
92
|
+
table[index]
|
|
93
|
+
self.env["scope"] = self.row_readers
|
|
94
|
+
|
|
95
|
+
def set_range(self, start: int, end: int) -> None:
|
|
96
|
+
for name in self.tables:
|
|
97
|
+
self.range_readers[name].range = range(start, end)
|
|
98
|
+
self.env["scope"] = self.range_readers
|
|
99
|
+
|
|
100
|
+
def __contains__(self, table: str) -> bool:
|
|
101
|
+
return table in self.tables
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
import inspect
|
|
3
|
+
import re
|
|
4
|
+
import statistics
|
|
5
|
+
from functools import wraps
|
|
6
|
+
|
|
7
|
+
from sqlglot import exp
|
|
8
|
+
from sqlglot.generator import Generator
|
|
9
|
+
from sqlglot.helper import PYTHON_VERSION, is_int, seq_get
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class reverse_key:
|
|
13
|
+
def __init__(self, obj):
|
|
14
|
+
self.obj = obj
|
|
15
|
+
|
|
16
|
+
def __eq__(self, other):
|
|
17
|
+
return other.obj == self.obj
|
|
18
|
+
|
|
19
|
+
def __lt__(self, other):
|
|
20
|
+
return other.obj < self.obj
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def filter_nulls(func, empty_null=True):
|
|
24
|
+
@wraps(func)
|
|
25
|
+
def _func(values):
|
|
26
|
+
filtered = tuple(v for v in values if v is not None)
|
|
27
|
+
if not filtered and empty_null:
|
|
28
|
+
return None
|
|
29
|
+
return func(filtered)
|
|
30
|
+
|
|
31
|
+
return _func
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def null_if_any(*required):
|
|
35
|
+
"""
|
|
36
|
+
Decorator that makes a function return `None` if any of the `required` arguments are `None`.
|
|
37
|
+
|
|
38
|
+
This also supports decoration with no arguments, e.g.:
|
|
39
|
+
|
|
40
|
+
@null_if_any
|
|
41
|
+
def foo(a, b): ...
|
|
42
|
+
|
|
43
|
+
In which case all arguments are required.
|
|
44
|
+
"""
|
|
45
|
+
f = None
|
|
46
|
+
if len(required) == 1 and callable(required[0]):
|
|
47
|
+
f = required[0]
|
|
48
|
+
required = ()
|
|
49
|
+
|
|
50
|
+
def decorator(func):
|
|
51
|
+
if required:
|
|
52
|
+
required_indices = [
|
|
53
|
+
i for i, param in enumerate(inspect.signature(func).parameters) if param in required
|
|
54
|
+
]
|
|
55
|
+
|
|
56
|
+
def predicate(*args):
|
|
57
|
+
return any(args[i] is None for i in required_indices)
|
|
58
|
+
|
|
59
|
+
else:
|
|
60
|
+
|
|
61
|
+
def predicate(*args):
|
|
62
|
+
return any(a is None for a in args)
|
|
63
|
+
|
|
64
|
+
@wraps(func)
|
|
65
|
+
def _func(*args):
|
|
66
|
+
if predicate(*args):
|
|
67
|
+
return None
|
|
68
|
+
return func(*args)
|
|
69
|
+
|
|
70
|
+
return _func
|
|
71
|
+
|
|
72
|
+
if f:
|
|
73
|
+
return decorator(f)
|
|
74
|
+
|
|
75
|
+
return decorator
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
@null_if_any("this", "substr")
|
|
79
|
+
def str_position(this, substr, position=None):
|
|
80
|
+
position = position - 1 if position is not None else position
|
|
81
|
+
return this.find(substr, position) + 1
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@null_if_any("this")
|
|
85
|
+
def substring(this, start=None, length=None):
|
|
86
|
+
if start is None:
|
|
87
|
+
return this
|
|
88
|
+
elif start == 0:
|
|
89
|
+
return ""
|
|
90
|
+
elif start < 0:
|
|
91
|
+
start = len(this) + start
|
|
92
|
+
else:
|
|
93
|
+
start -= 1
|
|
94
|
+
|
|
95
|
+
end = None if length is None else start + length
|
|
96
|
+
|
|
97
|
+
return this[start:end]
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@null_if_any
|
|
101
|
+
def cast(this, to):
|
|
102
|
+
if to == exp.DataType.Type.DATE:
|
|
103
|
+
if isinstance(this, datetime.datetime):
|
|
104
|
+
return this.date()
|
|
105
|
+
if isinstance(this, datetime.date):
|
|
106
|
+
return this
|
|
107
|
+
if isinstance(this, str):
|
|
108
|
+
return datetime.date.fromisoformat(this)
|
|
109
|
+
if to == exp.DataType.Type.TIME:
|
|
110
|
+
if isinstance(this, datetime.datetime):
|
|
111
|
+
return this.time()
|
|
112
|
+
if isinstance(this, datetime.time):
|
|
113
|
+
return this
|
|
114
|
+
if isinstance(this, str):
|
|
115
|
+
return datetime.time.fromisoformat(this)
|
|
116
|
+
if to in (exp.DataType.Type.DATETIME, exp.DataType.Type.TIMESTAMP):
|
|
117
|
+
if isinstance(this, datetime.datetime):
|
|
118
|
+
return this
|
|
119
|
+
if isinstance(this, datetime.date):
|
|
120
|
+
return datetime.datetime(this.year, this.month, this.day)
|
|
121
|
+
if isinstance(this, str):
|
|
122
|
+
return datetime.datetime.fromisoformat(this)
|
|
123
|
+
if to == exp.DataType.Type.BOOLEAN:
|
|
124
|
+
return bool(this)
|
|
125
|
+
if to in exp.DataType.TEXT_TYPES:
|
|
126
|
+
return str(this)
|
|
127
|
+
if to in {exp.DataType.Type.FLOAT, exp.DataType.Type.DOUBLE}:
|
|
128
|
+
return float(this)
|
|
129
|
+
if to in exp.DataType.NUMERIC_TYPES:
|
|
130
|
+
return int(this)
|
|
131
|
+
raise NotImplementedError(f"Casting {this} to '{to}' not implemented.")
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def ordered(this, desc, nulls_first):
|
|
135
|
+
if desc:
|
|
136
|
+
return reverse_key(this)
|
|
137
|
+
return this
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
@null_if_any
|
|
141
|
+
def interval(this, unit):
|
|
142
|
+
plural = unit + "S"
|
|
143
|
+
if plural in Generator.TIME_PART_SINGULARS:
|
|
144
|
+
unit = plural
|
|
145
|
+
return datetime.timedelta(**{unit.lower(): float(this)})
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@null_if_any("this", "expression")
|
|
149
|
+
def arraytostring(this, expression, null=None):
|
|
150
|
+
return expression.join(x for x in (x if x is not None else null for x in this) if x is not None)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@null_if_any("this", "expression")
|
|
154
|
+
def jsonextract(this, expression):
|
|
155
|
+
for path_segment in expression:
|
|
156
|
+
if isinstance(this, dict):
|
|
157
|
+
this = this.get(path_segment)
|
|
158
|
+
elif isinstance(this, list) and is_int(path_segment):
|
|
159
|
+
this = seq_get(this, int(path_segment))
|
|
160
|
+
else:
|
|
161
|
+
raise NotImplementedError(f"Unable to extract value for {this} at {path_segment}.")
|
|
162
|
+
|
|
163
|
+
if this is None:
|
|
164
|
+
break
|
|
165
|
+
|
|
166
|
+
return this
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
ENV = {
|
|
170
|
+
"exp": exp,
|
|
171
|
+
# aggs
|
|
172
|
+
"ARRAYAGG": list,
|
|
173
|
+
"ARRAYUNIQUEAGG": filter_nulls(lambda acc: list(set(acc))),
|
|
174
|
+
"AVG": filter_nulls(statistics.fmean if PYTHON_VERSION >= (3, 8) else statistics.mean), # type: ignore
|
|
175
|
+
"COUNT": filter_nulls(lambda acc: sum(1 for _ in acc), False),
|
|
176
|
+
"MAX": filter_nulls(max),
|
|
177
|
+
"MIN": filter_nulls(min),
|
|
178
|
+
"SUM": filter_nulls(sum),
|
|
179
|
+
# scalar functions
|
|
180
|
+
"ABS": null_if_any(lambda this: abs(this)),
|
|
181
|
+
"ADD": null_if_any(lambda e, this: e + this),
|
|
182
|
+
"ARRAYANY": null_if_any(lambda arr, func: any(func(e) for e in arr)),
|
|
183
|
+
"ARRAYTOSTRING": arraytostring,
|
|
184
|
+
"BETWEEN": null_if_any(lambda this, low, high: low <= this and this <= high),
|
|
185
|
+
"BITWISEAND": null_if_any(lambda this, e: this & e),
|
|
186
|
+
"BITWISELEFTSHIFT": null_if_any(lambda this, e: this << e),
|
|
187
|
+
"BITWISEOR": null_if_any(lambda this, e: this | e),
|
|
188
|
+
"BITWISERIGHTSHIFT": null_if_any(lambda this, e: this >> e),
|
|
189
|
+
"BITWISEXOR": null_if_any(lambda this, e: this ^ e),
|
|
190
|
+
"CAST": cast,
|
|
191
|
+
"COALESCE": lambda *args: next((a for a in args if a is not None), None),
|
|
192
|
+
"CONCAT": null_if_any(lambda *args: "".join(args)),
|
|
193
|
+
"SAFECONCAT": null_if_any(lambda *args: "".join(str(arg) for arg in args)),
|
|
194
|
+
"CONCATWS": null_if_any(lambda this, *args: this.join(args)),
|
|
195
|
+
"DATEDIFF": null_if_any(lambda this, expression, *_: (this - expression).days),
|
|
196
|
+
"DATESTRTODATE": null_if_any(lambda arg: datetime.date.fromisoformat(arg)),
|
|
197
|
+
"DIV": null_if_any(lambda e, this: e / this),
|
|
198
|
+
"DOT": null_if_any(lambda e, this: e[this]),
|
|
199
|
+
"EQ": null_if_any(lambda this, e: this == e),
|
|
200
|
+
"EXTRACT": null_if_any(lambda this, e: getattr(e, this)),
|
|
201
|
+
"GT": null_if_any(lambda this, e: this > e),
|
|
202
|
+
"GTE": null_if_any(lambda this, e: this >= e),
|
|
203
|
+
"IF": lambda predicate, true, false: true if predicate else false,
|
|
204
|
+
"INTDIV": null_if_any(lambda e, this: e // this),
|
|
205
|
+
"INTERVAL": interval,
|
|
206
|
+
"JSONEXTRACT": jsonextract,
|
|
207
|
+
"LEFT": null_if_any(lambda this, e: this[:e]),
|
|
208
|
+
"LIKE": null_if_any(
|
|
209
|
+
lambda this, e: bool(re.match(e.replace("_", ".").replace("%", ".*"), this))
|
|
210
|
+
),
|
|
211
|
+
"LOWER": null_if_any(lambda arg: arg.lower()),
|
|
212
|
+
"LT": null_if_any(lambda this, e: this < e),
|
|
213
|
+
"LTE": null_if_any(lambda this, e: this <= e),
|
|
214
|
+
"MAP": null_if_any(lambda *args: dict(zip(*args))), # type: ignore
|
|
215
|
+
"MOD": null_if_any(lambda e, this: e % this),
|
|
216
|
+
"MUL": null_if_any(lambda e, this: e * this),
|
|
217
|
+
"NEQ": null_if_any(lambda this, e: this != e),
|
|
218
|
+
"ORD": null_if_any(ord),
|
|
219
|
+
"ORDERED": ordered,
|
|
220
|
+
"POW": pow,
|
|
221
|
+
"RIGHT": null_if_any(lambda this, e: this[-e:]),
|
|
222
|
+
"ROUND": null_if_any(lambda this, decimals=None, truncate=None: round(this, ndigits=decimals)),
|
|
223
|
+
"STRPOSITION": str_position,
|
|
224
|
+
"SUB": null_if_any(lambda e, this: e - this),
|
|
225
|
+
"SUBSTRING": substring,
|
|
226
|
+
"TIMESTRTOTIME": null_if_any(lambda arg: datetime.datetime.fromisoformat(arg)),
|
|
227
|
+
"UPPER": null_if_any(lambda arg: arg.upper()),
|
|
228
|
+
"YEAR": null_if_any(lambda arg: arg.year),
|
|
229
|
+
"MONTH": null_if_any(lambda arg: arg.month),
|
|
230
|
+
"DAY": null_if_any(lambda arg: arg.day),
|
|
231
|
+
"CURRENTDATETIME": datetime.datetime.now,
|
|
232
|
+
"CURRENTTIMESTAMP": datetime.datetime.now,
|
|
233
|
+
"CURRENTTIME": datetime.datetime.now,
|
|
234
|
+
"CURRENTDATE": datetime.date.today,
|
|
235
|
+
"STRFTIME": null_if_any(lambda fmt, arg: datetime.datetime.fromisoformat(arg).strftime(fmt)),
|
|
236
|
+
"STRTOTIME": null_if_any(lambda arg, format: datetime.datetime.strptime(arg, format)),
|
|
237
|
+
"TRIM": null_if_any(lambda this, e=None: this.strip(e)),
|
|
238
|
+
"STRUCT": lambda *args: {
|
|
239
|
+
args[x]: args[x + 1]
|
|
240
|
+
for x in range(0, len(args), 2)
|
|
241
|
+
if (args[x + 1] is not None and args[x] is not None)
|
|
242
|
+
},
|
|
243
|
+
"UNIXTOTIME": null_if_any(
|
|
244
|
+
lambda arg: datetime.datetime.fromtimestamp(arg, datetime.timezone.utc)
|
|
245
|
+
),
|
|
246
|
+
}
|