sqlspec 0.26.0__py3-none-any.whl → 0.27.0__py3-none-any.whl
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.
Potentially problematic release.
This version of sqlspec might be problematic. Click here for more details.
- sqlspec/__init__.py +7 -15
- sqlspec/_serialization.py +55 -25
- sqlspec/_typing.py +62 -52
- sqlspec/adapters/adbc/_types.py +1 -1
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +870 -0
- sqlspec/adapters/adbc/config.py +62 -12
- sqlspec/adapters/adbc/data_dictionary.py +52 -2
- sqlspec/adapters/adbc/driver.py +144 -45
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +504 -0
- sqlspec/adapters/adbc/type_converter.py +44 -50
- sqlspec/adapters/aiosqlite/_types.py +1 -1
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +527 -0
- sqlspec/adapters/aiosqlite/config.py +86 -16
- sqlspec/adapters/aiosqlite/data_dictionary.py +34 -2
- sqlspec/adapters/aiosqlite/driver.py +127 -38
- sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
- sqlspec/adapters/aiosqlite/pool.py +7 -7
- sqlspec/adapters/asyncmy/__init__.py +7 -1
- sqlspec/adapters/asyncmy/_types.py +1 -1
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +493 -0
- sqlspec/adapters/asyncmy/config.py +59 -17
- sqlspec/adapters/asyncmy/data_dictionary.py +41 -2
- sqlspec/adapters/asyncmy/driver.py +293 -62
- sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncmy/litestar/store.py +296 -0
- sqlspec/adapters/asyncpg/__init__.py +2 -1
- sqlspec/adapters/asyncpg/_type_handlers.py +71 -0
- sqlspec/adapters/asyncpg/_types.py +11 -7
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +450 -0
- sqlspec/adapters/asyncpg/config.py +57 -36
- sqlspec/adapters/asyncpg/data_dictionary.py +41 -2
- sqlspec/adapters/asyncpg/driver.py +153 -23
- sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncpg/litestar/store.py +253 -0
- sqlspec/adapters/bigquery/_types.py +1 -1
- sqlspec/adapters/bigquery/adk/__init__.py +5 -0
- sqlspec/adapters/bigquery/adk/store.py +576 -0
- sqlspec/adapters/bigquery/config.py +25 -11
- sqlspec/adapters/bigquery/data_dictionary.py +42 -2
- sqlspec/adapters/bigquery/driver.py +352 -144
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +327 -0
- sqlspec/adapters/bigquery/type_converter.py +55 -23
- sqlspec/adapters/duckdb/_types.py +2 -2
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +553 -0
- sqlspec/adapters/duckdb/config.py +79 -21
- sqlspec/adapters/duckdb/data_dictionary.py +41 -2
- sqlspec/adapters/duckdb/driver.py +138 -43
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +332 -0
- sqlspec/adapters/duckdb/pool.py +5 -5
- sqlspec/adapters/duckdb/type_converter.py +51 -21
- sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
- sqlspec/adapters/oracledb/_types.py +20 -2
- sqlspec/adapters/oracledb/adk/__init__.py +5 -0
- sqlspec/adapters/oracledb/adk/store.py +1745 -0
- sqlspec/adapters/oracledb/config.py +120 -36
- sqlspec/adapters/oracledb/data_dictionary.py +87 -20
- sqlspec/adapters/oracledb/driver.py +292 -84
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +767 -0
- sqlspec/adapters/oracledb/migrations.py +316 -25
- sqlspec/adapters/oracledb/type_converter.py +91 -16
- sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
- sqlspec/adapters/psqlpy/_types.py +2 -1
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +482 -0
- sqlspec/adapters/psqlpy/config.py +45 -19
- sqlspec/adapters/psqlpy/data_dictionary.py +41 -2
- sqlspec/adapters/psqlpy/driver.py +101 -31
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +272 -0
- sqlspec/adapters/psqlpy/type_converter.py +40 -11
- sqlspec/adapters/psycopg/_type_handlers.py +80 -0
- sqlspec/adapters/psycopg/_types.py +2 -1
- sqlspec/adapters/psycopg/adk/__init__.py +5 -0
- sqlspec/adapters/psycopg/adk/store.py +944 -0
- sqlspec/adapters/psycopg/config.py +65 -37
- sqlspec/adapters/psycopg/data_dictionary.py +77 -3
- sqlspec/adapters/psycopg/driver.py +200 -78
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +554 -0
- sqlspec/adapters/sqlite/__init__.py +2 -1
- sqlspec/adapters/sqlite/_type_handlers.py +86 -0
- sqlspec/adapters/sqlite/_types.py +1 -1
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +572 -0
- sqlspec/adapters/sqlite/config.py +85 -16
- sqlspec/adapters/sqlite/data_dictionary.py +34 -2
- sqlspec/adapters/sqlite/driver.py +120 -52
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +318 -0
- sqlspec/adapters/sqlite/pool.py +5 -5
- sqlspec/base.py +45 -26
- sqlspec/builder/__init__.py +73 -4
- sqlspec/builder/_base.py +91 -58
- sqlspec/builder/_column.py +5 -5
- sqlspec/builder/_ddl.py +98 -89
- sqlspec/builder/_delete.py +5 -4
- sqlspec/builder/_dml.py +388 -0
- sqlspec/{_sql.py → builder/_factory.py} +41 -44
- sqlspec/builder/_insert.py +5 -82
- sqlspec/builder/{mixins/_join_operations.py → _join.py} +145 -143
- sqlspec/builder/_merge.py +446 -11
- sqlspec/builder/_parsing_utils.py +9 -11
- sqlspec/builder/_select.py +1313 -25
- sqlspec/builder/_update.py +11 -42
- sqlspec/cli.py +76 -69
- sqlspec/config.py +231 -60
- sqlspec/core/__init__.py +5 -4
- sqlspec/core/cache.py +18 -18
- sqlspec/core/compiler.py +6 -8
- sqlspec/core/filters.py +37 -37
- sqlspec/core/hashing.py +9 -9
- sqlspec/core/parameters.py +76 -45
- sqlspec/core/result.py +102 -46
- sqlspec/core/splitter.py +16 -17
- sqlspec/core/statement.py +32 -31
- sqlspec/core/type_conversion.py +3 -2
- sqlspec/driver/__init__.py +1 -3
- sqlspec/driver/_async.py +95 -161
- sqlspec/driver/_common.py +133 -80
- sqlspec/driver/_sync.py +95 -162
- sqlspec/driver/mixins/_result_tools.py +20 -236
- sqlspec/driver/mixins/_sql_translator.py +4 -4
- sqlspec/exceptions.py +70 -7
- sqlspec/extensions/adk/__init__.py +53 -0
- sqlspec/extensions/adk/_types.py +51 -0
- sqlspec/extensions/adk/converters.py +172 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +181 -0
- sqlspec/extensions/adk/store.py +536 -0
- sqlspec/extensions/aiosql/adapter.py +73 -53
- sqlspec/extensions/litestar/__init__.py +21 -4
- sqlspec/extensions/litestar/cli.py +54 -10
- sqlspec/extensions/litestar/config.py +59 -266
- sqlspec/extensions/litestar/handlers.py +46 -17
- sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
- sqlspec/extensions/litestar/migrations/__init__.py +3 -0
- sqlspec/extensions/litestar/plugin.py +324 -223
- sqlspec/extensions/litestar/providers.py +25 -25
- sqlspec/extensions/litestar/store.py +265 -0
- sqlspec/loader.py +30 -49
- sqlspec/migrations/base.py +200 -76
- sqlspec/migrations/commands.py +591 -62
- sqlspec/migrations/context.py +6 -9
- sqlspec/migrations/fix.py +199 -0
- sqlspec/migrations/loaders.py +47 -19
- sqlspec/migrations/runner.py +241 -75
- sqlspec/migrations/tracker.py +237 -21
- sqlspec/migrations/utils.py +51 -3
- sqlspec/migrations/validation.py +177 -0
- sqlspec/protocols.py +66 -36
- sqlspec/storage/_utils.py +98 -0
- sqlspec/storage/backends/fsspec.py +134 -106
- sqlspec/storage/backends/local.py +78 -51
- sqlspec/storage/backends/obstore.py +278 -162
- sqlspec/storage/registry.py +75 -39
- sqlspec/typing.py +14 -84
- sqlspec/utils/config_resolver.py +6 -6
- sqlspec/utils/correlation.py +4 -5
- sqlspec/utils/data_transformation.py +3 -2
- sqlspec/utils/deprecation.py +9 -8
- sqlspec/utils/fixtures.py +4 -4
- sqlspec/utils/logging.py +46 -6
- sqlspec/utils/module_loader.py +2 -2
- sqlspec/utils/schema.py +288 -0
- sqlspec/utils/serializers.py +3 -3
- sqlspec/utils/sync_tools.py +21 -17
- sqlspec/utils/text.py +1 -2
- sqlspec/utils/type_guards.py +111 -20
- sqlspec/utils/version.py +433 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.27.0.dist-info}/METADATA +40 -21
- sqlspec-0.27.0.dist-info/RECORD +207 -0
- sqlspec/builder/mixins/__init__.py +0 -55
- sqlspec/builder/mixins/_cte_and_set_ops.py +0 -253
- sqlspec/builder/mixins/_delete_operations.py +0 -50
- sqlspec/builder/mixins/_insert_operations.py +0 -282
- sqlspec/builder/mixins/_merge_operations.py +0 -698
- sqlspec/builder/mixins/_order_limit_operations.py +0 -145
- sqlspec/builder/mixins/_pivot_operations.py +0 -157
- sqlspec/builder/mixins/_select_operations.py +0 -930
- sqlspec/builder/mixins/_update_operations.py +0 -199
- sqlspec/builder/mixins/_where_clause.py +0 -1298
- sqlspec-0.26.0.dist-info/RECORD +0 -157
- sqlspec-0.26.0.dist-info/licenses/NOTICE +0 -29
- {sqlspec-0.26.0.dist-info → sqlspec-0.27.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.27.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.27.0.dist-info}/licenses/LICENSE +0 -0
sqlspec/builder/_dml.py
ADDED
|
@@ -0,0 +1,388 @@
|
|
|
1
|
+
"""Reusable mixins for INSERT/UPDATE/DELETE builders."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Mapping, Sequence
|
|
4
|
+
from typing import Any, cast
|
|
5
|
+
|
|
6
|
+
from mypy_extensions import trait
|
|
7
|
+
from sqlglot import exp
|
|
8
|
+
from typing_extensions import Self
|
|
9
|
+
|
|
10
|
+
from sqlspec.builder._parsing_utils import extract_sql_object_expression
|
|
11
|
+
from sqlspec.exceptions import SQLBuilderError
|
|
12
|
+
from sqlspec.protocols import SQLBuilderProtocol
|
|
13
|
+
from sqlspec.utils.type_guards import has_expression_and_sql, has_query_builder_parameters
|
|
14
|
+
|
|
15
|
+
__all__ = (
|
|
16
|
+
"DeleteFromClauseMixin",
|
|
17
|
+
"InsertFromSelectMixin",
|
|
18
|
+
"InsertIntoClauseMixin",
|
|
19
|
+
"InsertValuesMixin",
|
|
20
|
+
"UpdateFromClauseMixin",
|
|
21
|
+
"UpdateSetClauseMixin",
|
|
22
|
+
"UpdateTableClauseMixin",
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
ARG_PAIR_COUNT = 2
|
|
26
|
+
SINGLE_VALUE_COUNT = 1
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
# DELETE helpers
|
|
31
|
+
# ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@trait
|
|
35
|
+
class DeleteFromClauseMixin:
|
|
36
|
+
"""Mixin providing FROM clause support for DELETE builders."""
|
|
37
|
+
|
|
38
|
+
__slots__ = ()
|
|
39
|
+
|
|
40
|
+
def get_expression(self) -> exp.Expression | None: ...
|
|
41
|
+
def set_expression(self, expression: exp.Expression) -> None: ...
|
|
42
|
+
|
|
43
|
+
def from_(self, table: str) -> Self:
|
|
44
|
+
current_expr = self.get_expression()
|
|
45
|
+
if current_expr is None:
|
|
46
|
+
self.set_expression(exp.Delete())
|
|
47
|
+
current_expr = self.get_expression()
|
|
48
|
+
|
|
49
|
+
if not isinstance(current_expr, exp.Delete):
|
|
50
|
+
msg = f"Base expression for Delete is {type(current_expr).__name__}, expected Delete."
|
|
51
|
+
raise SQLBuilderError(msg)
|
|
52
|
+
|
|
53
|
+
assert current_expr is not None
|
|
54
|
+
setattr(self, "_table", table)
|
|
55
|
+
current_expr.set("this", exp.to_table(table))
|
|
56
|
+
return self
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
# ---------------------------------------------------------------------------
|
|
60
|
+
# INSERT helpers
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@trait
|
|
65
|
+
class InsertIntoClauseMixin:
|
|
66
|
+
__slots__ = ()
|
|
67
|
+
|
|
68
|
+
def get_expression(self) -> exp.Expression | None: ...
|
|
69
|
+
def set_expression(self, expression: exp.Expression) -> None: ...
|
|
70
|
+
|
|
71
|
+
def into(self, table: str) -> Self:
|
|
72
|
+
current_expr = self.get_expression()
|
|
73
|
+
if current_expr is None:
|
|
74
|
+
self.set_expression(exp.Insert())
|
|
75
|
+
current_expr = self.get_expression()
|
|
76
|
+
|
|
77
|
+
if not isinstance(current_expr, exp.Insert):
|
|
78
|
+
msg = "Cannot set target table on a non-INSERT expression."
|
|
79
|
+
raise SQLBuilderError(msg)
|
|
80
|
+
|
|
81
|
+
assert current_expr is not None
|
|
82
|
+
setattr(self, "_table", table)
|
|
83
|
+
current_expr.set("this", exp.to_table(table))
|
|
84
|
+
return self
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
@trait
|
|
88
|
+
class InsertValuesMixin:
|
|
89
|
+
__slots__ = ()
|
|
90
|
+
|
|
91
|
+
def get_expression(self) -> exp.Expression | None: ...
|
|
92
|
+
def set_expression(self, expression: exp.Expression) -> None: ...
|
|
93
|
+
|
|
94
|
+
_columns: Any
|
|
95
|
+
|
|
96
|
+
def add_parameter(self, value: Any, name: str | None = None) -> tuple[Any, str]:
|
|
97
|
+
msg = "Method must be provided by QueryBuilder subclass"
|
|
98
|
+
raise NotImplementedError(msg)
|
|
99
|
+
|
|
100
|
+
def _generate_unique_parameter_name(self, base_name: str) -> str:
|
|
101
|
+
msg = "Method must be provided by QueryBuilder subclass"
|
|
102
|
+
raise NotImplementedError(msg)
|
|
103
|
+
|
|
104
|
+
def columns(self, *columns: str | exp.Expression) -> Self:
|
|
105
|
+
current_expr = self.get_expression()
|
|
106
|
+
if current_expr is None:
|
|
107
|
+
self.set_expression(exp.Insert())
|
|
108
|
+
current_expr = self.get_expression()
|
|
109
|
+
|
|
110
|
+
if not isinstance(current_expr, exp.Insert):
|
|
111
|
+
msg = "Cannot set columns on a non-INSERT expression."
|
|
112
|
+
raise SQLBuilderError(msg)
|
|
113
|
+
|
|
114
|
+
assert current_expr is not None
|
|
115
|
+
current_this = current_expr.args.get("this")
|
|
116
|
+
if current_this is None:
|
|
117
|
+
msg = "Table must be set using .into() before setting columns."
|
|
118
|
+
raise SQLBuilderError(msg)
|
|
119
|
+
|
|
120
|
+
if columns:
|
|
121
|
+
identifiers = [exp.to_identifier(col) if isinstance(col, str) else col for col in columns]
|
|
122
|
+
table_name = current_this.this
|
|
123
|
+
current_expr.set("this", exp.Schema(this=table_name, expressions=identifiers))
|
|
124
|
+
elif isinstance(current_this, exp.Schema):
|
|
125
|
+
table_name = current_this.this
|
|
126
|
+
current_expr.set("this", exp.Table(this=table_name))
|
|
127
|
+
|
|
128
|
+
try:
|
|
129
|
+
cols = self._columns
|
|
130
|
+
if not columns:
|
|
131
|
+
cols.clear()
|
|
132
|
+
else:
|
|
133
|
+
cols[:] = [col if isinstance(col, str) else str(col) for col in columns]
|
|
134
|
+
except AttributeError:
|
|
135
|
+
pass
|
|
136
|
+
return self
|
|
137
|
+
|
|
138
|
+
def values(self, *values: Any, **kwargs: Any) -> Self:
|
|
139
|
+
current_expr = self.get_expression()
|
|
140
|
+
if current_expr is None:
|
|
141
|
+
self.set_expression(exp.Insert())
|
|
142
|
+
current_expr = self.get_expression()
|
|
143
|
+
|
|
144
|
+
if not isinstance(current_expr, exp.Insert):
|
|
145
|
+
msg = "Cannot add values to a non-INSERT expression."
|
|
146
|
+
raise SQLBuilderError(msg)
|
|
147
|
+
|
|
148
|
+
assert current_expr is not None
|
|
149
|
+
table_name = cast("str | None", self._table) # type: ignore[attr-defined]
|
|
150
|
+
if not table_name:
|
|
151
|
+
msg = "The target table must be set using .into() before adding values."
|
|
152
|
+
raise SQLBuilderError(msg)
|
|
153
|
+
|
|
154
|
+
positional_values = list(values)
|
|
155
|
+
if len(positional_values) == SINGLE_VALUE_COUNT and hasattr(positional_values[0], "items") and not kwargs:
|
|
156
|
+
kwargs = cast("dict[str, Any]", positional_values[0])
|
|
157
|
+
positional_values = []
|
|
158
|
+
|
|
159
|
+
if kwargs and positional_values:
|
|
160
|
+
msg = "Cannot mix positional values with keyword values."
|
|
161
|
+
raise SQLBuilderError(msg)
|
|
162
|
+
|
|
163
|
+
row_expressions: list[exp.Expression] = []
|
|
164
|
+
column_defs = cast("list[str]", self._columns or [])
|
|
165
|
+
|
|
166
|
+
if kwargs:
|
|
167
|
+
if not column_defs:
|
|
168
|
+
self.columns(*kwargs.keys())
|
|
169
|
+
column_defs = cast("list[str]", self._columns)
|
|
170
|
+
for col, val in kwargs.items():
|
|
171
|
+
if isinstance(val, exp.Expression):
|
|
172
|
+
row_expressions.append(val)
|
|
173
|
+
continue
|
|
174
|
+
if has_expression_and_sql(val):
|
|
175
|
+
row_expressions.append(extract_sql_object_expression(val, builder=self))
|
|
176
|
+
continue
|
|
177
|
+
column_name = str(col).split(".")[-1]
|
|
178
|
+
unique_name = self._generate_unique_parameter_name(column_name)
|
|
179
|
+
_, unique_name = self.add_parameter(val, name=unique_name)
|
|
180
|
+
row_expressions.append(exp.Placeholder(this=unique_name))
|
|
181
|
+
else:
|
|
182
|
+
if column_defs and len(positional_values) != len(column_defs):
|
|
183
|
+
msg = (
|
|
184
|
+
f"Number of values ({len(positional_values)}) does not match the number of specified columns "
|
|
185
|
+
f"({len(column_defs)})."
|
|
186
|
+
)
|
|
187
|
+
raise SQLBuilderError(msg)
|
|
188
|
+
|
|
189
|
+
for index, raw_value in enumerate(positional_values):
|
|
190
|
+
if isinstance(raw_value, exp.Expression):
|
|
191
|
+
row_expressions.append(raw_value)
|
|
192
|
+
elif has_expression_and_sql(raw_value):
|
|
193
|
+
row_expressions.append(extract_sql_object_expression(raw_value, builder=self))
|
|
194
|
+
else:
|
|
195
|
+
if column_defs and index < len(column_defs):
|
|
196
|
+
column_token = column_defs[index]
|
|
197
|
+
column_name = column_token.rsplit(".", maxsplit=1)[-1]
|
|
198
|
+
else:
|
|
199
|
+
column_name = f"value_{index + 1}"
|
|
200
|
+
unique_name = self._generate_unique_parameter_name(column_name)
|
|
201
|
+
_, unique_name = self.add_parameter(raw_value, name=unique_name)
|
|
202
|
+
row_expressions.append(exp.Placeholder(this=unique_name))
|
|
203
|
+
|
|
204
|
+
values_node = current_expr.args.get("expression")
|
|
205
|
+
tuple_expression = exp.Tuple(expressions=row_expressions)
|
|
206
|
+
if isinstance(values_node, exp.Values):
|
|
207
|
+
values_node.expressions.append(tuple_expression)
|
|
208
|
+
else:
|
|
209
|
+
current_expr.set("expression", exp.Values(expressions=[tuple_expression]))
|
|
210
|
+
return self
|
|
211
|
+
|
|
212
|
+
def add_values(self, values: Sequence[Any]) -> Self:
|
|
213
|
+
return self.values(*values)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
@trait
|
|
217
|
+
class InsertFromSelectMixin:
|
|
218
|
+
__slots__ = ()
|
|
219
|
+
|
|
220
|
+
def get_expression(self) -> exp.Expression | None: ...
|
|
221
|
+
def set_expression(self, expression: exp.Expression) -> None: ...
|
|
222
|
+
|
|
223
|
+
_table: Any
|
|
224
|
+
|
|
225
|
+
def add_parameter(self, value: Any, name: str | None = None) -> tuple[Any, str]:
|
|
226
|
+
msg = "Method must be provided by QueryBuilder subclass"
|
|
227
|
+
raise NotImplementedError(msg)
|
|
228
|
+
|
|
229
|
+
def from_select(self, select_builder: SQLBuilderProtocol) -> Self:
|
|
230
|
+
if not getattr(self, "_table", None):
|
|
231
|
+
msg = "The target table must be set using .into() before adding values."
|
|
232
|
+
raise SQLBuilderError(msg)
|
|
233
|
+
|
|
234
|
+
current_expr = self.get_expression()
|
|
235
|
+
if current_expr is None:
|
|
236
|
+
self.set_expression(exp.Insert())
|
|
237
|
+
current_expr = self.get_expression()
|
|
238
|
+
|
|
239
|
+
if not isinstance(current_expr, exp.Insert):
|
|
240
|
+
msg = "Cannot set INSERT source on a non-INSERT expression."
|
|
241
|
+
raise SQLBuilderError(msg)
|
|
242
|
+
|
|
243
|
+
assert current_expr is not None
|
|
244
|
+
subquery_parameters = getattr(select_builder, "_parameters", None)
|
|
245
|
+
if isinstance(subquery_parameters, dict):
|
|
246
|
+
builder_with_params = cast("SQLBuilderProtocol", self)
|
|
247
|
+
for param_name, param_value in subquery_parameters.items():
|
|
248
|
+
builder_with_params.add_parameter(param_value, name=param_name)
|
|
249
|
+
|
|
250
|
+
select_expr = getattr(select_builder, "_expression", None)
|
|
251
|
+
if select_expr and isinstance(select_expr, exp.Select):
|
|
252
|
+
current_expr.set("expression", select_expr.copy())
|
|
253
|
+
else:
|
|
254
|
+
msg = "SelectBuilder must have a valid SELECT expression."
|
|
255
|
+
raise SQLBuilderError(msg)
|
|
256
|
+
return self
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
# ---------------------------------------------------------------------------
|
|
260
|
+
# UPDATE helpers
|
|
261
|
+
# ---------------------------------------------------------------------------
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
@trait
|
|
265
|
+
class UpdateTableClauseMixin:
|
|
266
|
+
__slots__ = ()
|
|
267
|
+
|
|
268
|
+
def get_expression(self) -> exp.Expression | None: ...
|
|
269
|
+
def set_expression(self, expression: exp.Expression) -> None: ...
|
|
270
|
+
|
|
271
|
+
def table(self, table_name: str, alias: str | None = None) -> Self:
|
|
272
|
+
current_expr = self.get_expression()
|
|
273
|
+
if current_expr is None or not isinstance(current_expr, exp.Update):
|
|
274
|
+
self.set_expression(exp.Update(this=None, expressions=[], joins=[]))
|
|
275
|
+
current_expr = self.get_expression()
|
|
276
|
+
|
|
277
|
+
assert current_expr is not None
|
|
278
|
+
|
|
279
|
+
table_expr: exp.Expression = exp.to_table(table_name, alias=alias)
|
|
280
|
+
current_expr.set("this", table_expr)
|
|
281
|
+
setattr(self, "_table", table_name)
|
|
282
|
+
return self
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
@trait
|
|
286
|
+
class UpdateSetClauseMixin:
|
|
287
|
+
__slots__ = ()
|
|
288
|
+
|
|
289
|
+
def get_expression(self) -> exp.Expression | None: ...
|
|
290
|
+
def set_expression(self, expression: exp.Expression) -> None: ...
|
|
291
|
+
|
|
292
|
+
def add_parameter(self, value: Any, name: str | None = None) -> tuple[Any, str]:
|
|
293
|
+
msg = "Method must be provided by QueryBuilder subclass"
|
|
294
|
+
raise NotImplementedError(msg)
|
|
295
|
+
|
|
296
|
+
def _generate_unique_parameter_name(self, base_name: str) -> str:
|
|
297
|
+
msg = "Method must be provided by QueryBuilder subclass"
|
|
298
|
+
raise NotImplementedError(msg)
|
|
299
|
+
|
|
300
|
+
def _process_update_value(self, val: Any, col: Any) -> exp.Expression:
|
|
301
|
+
if isinstance(val, exp.Expression):
|
|
302
|
+
return val
|
|
303
|
+
if has_query_builder_parameters(val):
|
|
304
|
+
subquery = val.build()
|
|
305
|
+
sql_text = subquery.sql if hasattr(subquery, "sql") and not callable(subquery.sql) else str(subquery)
|
|
306
|
+
value_expr = exp.paren(exp.maybe_parse(sql_text, dialect=getattr(self, "dialect", None)))
|
|
307
|
+
for p_name, p_value in getattr(val, "parameters", {}).items():
|
|
308
|
+
self.add_parameter(p_value, name=p_name)
|
|
309
|
+
return value_expr
|
|
310
|
+
if hasattr(val, "expression") and hasattr(val, "sql"):
|
|
311
|
+
return extract_sql_object_expression(val, builder=self)
|
|
312
|
+
column_name = col if isinstance(col, str) else str(col)
|
|
313
|
+
if "." in column_name:
|
|
314
|
+
column_name = column_name.split(".")[-1]
|
|
315
|
+
param_name = self._generate_unique_parameter_name(column_name)
|
|
316
|
+
param_name = self.add_parameter(val, name=param_name)[1]
|
|
317
|
+
return exp.Placeholder(this=param_name)
|
|
318
|
+
|
|
319
|
+
def set(self, *args: Any, **kwargs: Any) -> Self:
|
|
320
|
+
current_expr = self.get_expression()
|
|
321
|
+
if current_expr is None:
|
|
322
|
+
self.set_expression(exp.Update())
|
|
323
|
+
current_expr = self.get_expression()
|
|
324
|
+
|
|
325
|
+
if not isinstance(current_expr, exp.Update):
|
|
326
|
+
msg = "Cannot add SET clause to non-UPDATE expression."
|
|
327
|
+
raise SQLBuilderError(msg)
|
|
328
|
+
|
|
329
|
+
assert current_expr is not None
|
|
330
|
+
|
|
331
|
+
assignments: list[exp.Expression] = []
|
|
332
|
+
if len(args) == ARG_PAIR_COUNT and not kwargs:
|
|
333
|
+
col, val = args
|
|
334
|
+
col_expr = col if isinstance(col, exp.Column) else exp.column(col)
|
|
335
|
+
assignments.append(exp.EQ(this=col_expr, expression=self._process_update_value(val, col)))
|
|
336
|
+
elif (len(args) == SINGLE_VALUE_COUNT and isinstance(args[0], Mapping)) or kwargs:
|
|
337
|
+
all_values = dict(args[0] if args else {}, **kwargs)
|
|
338
|
+
for col, val in all_values.items():
|
|
339
|
+
assignments.append(exp.EQ(this=exp.column(col), expression=self._process_update_value(val, col)))
|
|
340
|
+
else:
|
|
341
|
+
msg = "Invalid arguments for set(): use (column, value), mapping, or kwargs."
|
|
342
|
+
raise SQLBuilderError(msg)
|
|
343
|
+
|
|
344
|
+
existing = current_expr.args.get("expressions", [])
|
|
345
|
+
current_expr.set("expressions", existing + assignments)
|
|
346
|
+
return self
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
@trait
|
|
350
|
+
class UpdateFromClauseMixin:
|
|
351
|
+
__slots__ = ()
|
|
352
|
+
|
|
353
|
+
def get_expression(self) -> exp.Expression | None: ...
|
|
354
|
+
def set_expression(self, expression: exp.Expression) -> None: ...
|
|
355
|
+
|
|
356
|
+
def from_(self, table: str | exp.Expression | Any, alias: str | None = None) -> Self:
|
|
357
|
+
current_expr = self.get_expression()
|
|
358
|
+
if current_expr is None or not isinstance(current_expr, exp.Update):
|
|
359
|
+
msg = "Cannot add FROM clause to non-UPDATE expression. Set the main table first."
|
|
360
|
+
raise SQLBuilderError(msg)
|
|
361
|
+
|
|
362
|
+
assert current_expr is not None
|
|
363
|
+
table_expr: exp.Expression
|
|
364
|
+
if isinstance(table, str):
|
|
365
|
+
table_expr = exp.to_table(table, alias=alias)
|
|
366
|
+
elif has_query_builder_parameters(table):
|
|
367
|
+
subquery_params = getattr(table, "_parameters", None)
|
|
368
|
+
if isinstance(subquery_params, dict):
|
|
369
|
+
builder_with_params = cast("SQLBuilderProtocol", self)
|
|
370
|
+
for param_name, param_value in subquery_params.items():
|
|
371
|
+
builder_with_params.add_parameter(param_value, name=param_name)
|
|
372
|
+
raw_expression = getattr(table, "_expression", None)
|
|
373
|
+
subquery_source = raw_expression if isinstance(raw_expression, exp.Expression) else exp.select()
|
|
374
|
+
subquery_exp = exp.paren(subquery_source)
|
|
375
|
+
table_expr = exp.alias_(subquery_exp, alias) if alias else subquery_exp
|
|
376
|
+
elif isinstance(table, exp.Expression):
|
|
377
|
+
table_expr = exp.alias_(table, alias) if alias else table
|
|
378
|
+
else:
|
|
379
|
+
msg = f"Unsupported table type for FROM clause: {type(table)}"
|
|
380
|
+
raise SQLBuilderError(msg)
|
|
381
|
+
|
|
382
|
+
from_clause = current_expr.args.get("from")
|
|
383
|
+
if from_clause is None:
|
|
384
|
+
from_clause = exp.From(expressions=[])
|
|
385
|
+
current_expr.set("from", from_clause)
|
|
386
|
+
|
|
387
|
+
from_clause.append("expressions", table_expr)
|
|
388
|
+
return self
|
|
@@ -4,16 +4,16 @@ Provides statement builders (select, insert, update, etc.) and column expression
|
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
6
|
import logging
|
|
7
|
-
from typing import TYPE_CHECKING, Any,
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Union
|
|
8
8
|
|
|
9
9
|
import sqlglot
|
|
10
10
|
from sqlglot import exp
|
|
11
11
|
from sqlglot.dialects.dialect import DialectType
|
|
12
12
|
from sqlglot.errors import ParseError as SQLGlotParseError
|
|
13
13
|
|
|
14
|
-
from sqlspec.builder import
|
|
14
|
+
from sqlspec.builder._column import Column
|
|
15
|
+
from sqlspec.builder._ddl import (
|
|
15
16
|
AlterTable,
|
|
16
|
-
Column,
|
|
17
17
|
CommentOn,
|
|
18
18
|
CreateIndex,
|
|
19
19
|
CreateMaterializedView,
|
|
@@ -21,18 +21,14 @@ from sqlspec.builder import (
|
|
|
21
21
|
CreateTable,
|
|
22
22
|
CreateTableAsSelect,
|
|
23
23
|
CreateView,
|
|
24
|
-
Delete,
|
|
25
24
|
DropIndex,
|
|
26
25
|
DropSchema,
|
|
27
26
|
DropTable,
|
|
28
27
|
DropView,
|
|
29
|
-
Insert,
|
|
30
|
-
Merge,
|
|
31
28
|
RenameTable,
|
|
32
|
-
Select,
|
|
33
29
|
Truncate,
|
|
34
|
-
Update,
|
|
35
30
|
)
|
|
31
|
+
from sqlspec.builder._delete import Delete
|
|
36
32
|
from sqlspec.builder._expression_wrappers import (
|
|
37
33
|
AggregateExpression,
|
|
38
34
|
ConversionExpression,
|
|
@@ -40,9 +36,12 @@ from sqlspec.builder._expression_wrappers import (
|
|
|
40
36
|
MathExpression,
|
|
41
37
|
StringExpression,
|
|
42
38
|
)
|
|
39
|
+
from sqlspec.builder._insert import Insert
|
|
40
|
+
from sqlspec.builder._join import JoinBuilder
|
|
41
|
+
from sqlspec.builder._merge import Merge
|
|
43
42
|
from sqlspec.builder._parsing_utils import extract_expression, to_expression
|
|
44
|
-
from sqlspec.builder.
|
|
45
|
-
from sqlspec.builder.
|
|
43
|
+
from sqlspec.builder._select import Case, Select, SubqueryBuilder, WindowFunctionBuilder
|
|
44
|
+
from sqlspec.builder._update import Update
|
|
46
45
|
from sqlspec.core.statement import SQL
|
|
47
46
|
from sqlspec.exceptions import SQLBuilderError
|
|
48
47
|
|
|
@@ -205,7 +204,7 @@ class SQLFactory:
|
|
|
205
204
|
select_builder.select(*columns_or_sql)
|
|
206
205
|
return select_builder
|
|
207
206
|
|
|
208
|
-
def insert(self, table_or_sql:
|
|
207
|
+
def insert(self, table_or_sql: str | None = None, dialect: DialectType = None) -> "Insert":
|
|
209
208
|
builder_dialect = dialect or self.dialect
|
|
210
209
|
builder = Insert(dialect=builder_dialect)
|
|
211
210
|
if table_or_sql:
|
|
@@ -222,7 +221,7 @@ class SQLFactory:
|
|
|
222
221
|
return builder.into(table_or_sql)
|
|
223
222
|
return builder
|
|
224
223
|
|
|
225
|
-
def update(self, table_or_sql:
|
|
224
|
+
def update(self, table_or_sql: str | None = None, dialect: DialectType = None) -> "Update":
|
|
226
225
|
builder_dialect = dialect or self.dialect
|
|
227
226
|
builder = Update(dialect=builder_dialect)
|
|
228
227
|
if table_or_sql:
|
|
@@ -238,7 +237,7 @@ class SQLFactory:
|
|
|
238
237
|
return builder.table(table_or_sql)
|
|
239
238
|
return builder
|
|
240
239
|
|
|
241
|
-
def delete(self, table_or_sql:
|
|
240
|
+
def delete(self, table_or_sql: str | None = None, dialect: DialectType = None) -> "Delete":
|
|
242
241
|
builder_dialect = dialect or self.dialect
|
|
243
242
|
builder = Delete(dialect=builder_dialect)
|
|
244
243
|
if table_or_sql and self._looks_like_sql(table_or_sql):
|
|
@@ -252,7 +251,7 @@ class SQLFactory:
|
|
|
252
251
|
return self._populate_delete_from_sql(builder, table_or_sql)
|
|
253
252
|
return builder
|
|
254
253
|
|
|
255
|
-
def merge(self, table_or_sql:
|
|
254
|
+
def merge(self, table_or_sql: str | None = None, dialect: DialectType = None) -> "Merge":
|
|
256
255
|
builder_dialect = dialect or self.dialect
|
|
257
256
|
builder = Merge(dialect=builder_dialect)
|
|
258
257
|
if table_or_sql:
|
|
@@ -423,7 +422,7 @@ class SQLFactory:
|
|
|
423
422
|
return CommentOn(dialect=dialect or self.dialect)
|
|
424
423
|
|
|
425
424
|
@staticmethod
|
|
426
|
-
def _looks_like_sql(candidate: str, expected_type:
|
|
425
|
+
def _looks_like_sql(candidate: str, expected_type: str | None = None) -> bool:
|
|
427
426
|
"""Determine if a string looks like SQL.
|
|
428
427
|
|
|
429
428
|
Args:
|
|
@@ -525,7 +524,7 @@ class SQLFactory:
|
|
|
525
524
|
logger.warning("Failed to parse MERGE SQL, falling back to traditional mode: %s", e)
|
|
526
525
|
return builder
|
|
527
526
|
|
|
528
|
-
def column(self, name: str, table:
|
|
527
|
+
def column(self, name: str, table: str | None = None) -> Column:
|
|
529
528
|
"""Create a column reference.
|
|
530
529
|
|
|
531
530
|
Args:
|
|
@@ -683,7 +682,7 @@ class SQLFactory:
|
|
|
683
682
|
return Column(name)
|
|
684
683
|
|
|
685
684
|
@staticmethod
|
|
686
|
-
def raw(sql_fragment: str, **parameters: Any) -> "
|
|
685
|
+
def raw(sql_fragment: str, **parameters: Any) -> "exp.Expression | SQL":
|
|
687
686
|
"""Create a raw SQL expression from a string fragment with optional parameters.
|
|
688
687
|
|
|
689
688
|
Args:
|
|
@@ -818,7 +817,7 @@ class SQLFactory:
|
|
|
818
817
|
return AggregateExpression(exp.Min(this=col_expr))
|
|
819
818
|
|
|
820
819
|
@staticmethod
|
|
821
|
-
def rollup(*columns:
|
|
820
|
+
def rollup(*columns: str | exp.Expression) -> FunctionExpression:
|
|
822
821
|
"""Create a ROLLUP expression for GROUP BY clauses.
|
|
823
822
|
|
|
824
823
|
Args:
|
|
@@ -840,7 +839,7 @@ class SQLFactory:
|
|
|
840
839
|
return FunctionExpression(exp.Rollup(expressions=column_exprs))
|
|
841
840
|
|
|
842
841
|
@staticmethod
|
|
843
|
-
def cube(*columns:
|
|
842
|
+
def cube(*columns: str | exp.Expression) -> FunctionExpression:
|
|
844
843
|
"""Create a CUBE expression for GROUP BY clauses.
|
|
845
844
|
|
|
846
845
|
Args:
|
|
@@ -862,7 +861,7 @@ class SQLFactory:
|
|
|
862
861
|
return FunctionExpression(exp.Cube(expressions=column_exprs))
|
|
863
862
|
|
|
864
863
|
@staticmethod
|
|
865
|
-
def grouping_sets(*column_sets:
|
|
864
|
+
def grouping_sets(*column_sets: tuple[str, ...] | list[str]) -> FunctionExpression:
|
|
866
865
|
"""Create a GROUPING SETS expression for GROUP BY clauses.
|
|
867
866
|
|
|
868
867
|
Args:
|
|
@@ -896,7 +895,7 @@ class SQLFactory:
|
|
|
896
895
|
return FunctionExpression(exp.GroupingSets(expressions=set_expressions))
|
|
897
896
|
|
|
898
897
|
@staticmethod
|
|
899
|
-
def any(values:
|
|
898
|
+
def any(values: list[Any] | exp.Expression | str) -> FunctionExpression:
|
|
900
899
|
"""Create an ANY expression for use with comparison operators.
|
|
901
900
|
|
|
902
901
|
Args:
|
|
@@ -924,7 +923,7 @@ class SQLFactory:
|
|
|
924
923
|
return FunctionExpression(exp.Any(this=values))
|
|
925
924
|
|
|
926
925
|
@staticmethod
|
|
927
|
-
def not_any_(values:
|
|
926
|
+
def not_any_(values: list[Any] | exp.Expression | str) -> FunctionExpression:
|
|
928
927
|
"""Create a NOT ANY expression for use with comparison operators.
|
|
929
928
|
|
|
930
929
|
Args:
|
|
@@ -946,7 +945,7 @@ class SQLFactory:
|
|
|
946
945
|
return SQLFactory.any(values)
|
|
947
946
|
|
|
948
947
|
@staticmethod
|
|
949
|
-
def concat(*expressions:
|
|
948
|
+
def concat(*expressions: str | exp.Expression) -> StringExpression:
|
|
950
949
|
"""Create a CONCAT expression.
|
|
951
950
|
|
|
952
951
|
Args:
|
|
@@ -959,7 +958,7 @@ class SQLFactory:
|
|
|
959
958
|
return StringExpression(exp.Concat(expressions=exprs))
|
|
960
959
|
|
|
961
960
|
@staticmethod
|
|
962
|
-
def upper(column:
|
|
961
|
+
def upper(column: str | exp.Expression) -> StringExpression:
|
|
963
962
|
"""Create an UPPER expression.
|
|
964
963
|
|
|
965
964
|
Args:
|
|
@@ -972,7 +971,7 @@ class SQLFactory:
|
|
|
972
971
|
return StringExpression(exp.Upper(this=col_expr))
|
|
973
972
|
|
|
974
973
|
@staticmethod
|
|
975
|
-
def lower(column:
|
|
974
|
+
def lower(column: str | exp.Expression) -> StringExpression:
|
|
976
975
|
"""Create a LOWER expression.
|
|
977
976
|
|
|
978
977
|
Args:
|
|
@@ -985,7 +984,7 @@ class SQLFactory:
|
|
|
985
984
|
return StringExpression(exp.Lower(this=col_expr))
|
|
986
985
|
|
|
987
986
|
@staticmethod
|
|
988
|
-
def length(column:
|
|
987
|
+
def length(column: str | exp.Expression) -> StringExpression:
|
|
989
988
|
"""Create a LENGTH expression.
|
|
990
989
|
|
|
991
990
|
Args:
|
|
@@ -998,7 +997,7 @@ class SQLFactory:
|
|
|
998
997
|
return StringExpression(exp.Length(this=col_expr))
|
|
999
998
|
|
|
1000
999
|
@staticmethod
|
|
1001
|
-
def round(column:
|
|
1000
|
+
def round(column: str | exp.Expression, decimals: int = 0) -> MathExpression:
|
|
1002
1001
|
"""Create a ROUND expression.
|
|
1003
1002
|
|
|
1004
1003
|
Args:
|
|
@@ -1036,7 +1035,7 @@ class SQLFactory:
|
|
|
1036
1035
|
return FunctionExpression(exp.convert(value))
|
|
1037
1036
|
|
|
1038
1037
|
@staticmethod
|
|
1039
|
-
def decode(column:
|
|
1038
|
+
def decode(column: str | exp.Expression, *args: str | exp.Expression | Any) -> FunctionExpression:
|
|
1040
1039
|
"""Create a DECODE expression (Oracle-style conditional logic).
|
|
1041
1040
|
|
|
1042
1041
|
DECODE compares column to each search value and returns the corresponding result.
|
|
@@ -1086,7 +1085,7 @@ class SQLFactory:
|
|
|
1086
1085
|
return FunctionExpression(exp.Case(ifs=conditions, default=default))
|
|
1087
1086
|
|
|
1088
1087
|
@staticmethod
|
|
1089
|
-
def cast(column:
|
|
1088
|
+
def cast(column: str | exp.Expression, data_type: str) -> ConversionExpression:
|
|
1090
1089
|
"""Create a CAST expression for type conversion.
|
|
1091
1090
|
|
|
1092
1091
|
Args:
|
|
@@ -1100,7 +1099,7 @@ class SQLFactory:
|
|
|
1100
1099
|
return ConversionExpression(exp.Cast(this=col_expr, to=exp.DataType.build(data_type)))
|
|
1101
1100
|
|
|
1102
1101
|
@staticmethod
|
|
1103
|
-
def coalesce(*expressions:
|
|
1102
|
+
def coalesce(*expressions: str | exp.Expression) -> ConversionExpression:
|
|
1104
1103
|
"""Create a COALESCE expression.
|
|
1105
1104
|
|
|
1106
1105
|
Args:
|
|
@@ -1113,9 +1112,7 @@ class SQLFactory:
|
|
|
1113
1112
|
return ConversionExpression(exp.Coalesce(expressions=exprs))
|
|
1114
1113
|
|
|
1115
1114
|
@staticmethod
|
|
1116
|
-
def nvl(
|
|
1117
|
-
column: Union[str, exp.Expression], substitute_value: Union[str, exp.Expression, Any]
|
|
1118
|
-
) -> ConversionExpression:
|
|
1115
|
+
def nvl(column: str | exp.Expression, substitute_value: str | exp.Expression | Any) -> ConversionExpression:
|
|
1119
1116
|
"""Create an NVL (Oracle-style) expression using COALESCE.
|
|
1120
1117
|
|
|
1121
1118
|
Args:
|
|
@@ -1131,9 +1128,9 @@ class SQLFactory:
|
|
|
1131
1128
|
|
|
1132
1129
|
@staticmethod
|
|
1133
1130
|
def nvl2(
|
|
1134
|
-
column:
|
|
1135
|
-
value_if_not_null:
|
|
1136
|
-
value_if_null:
|
|
1131
|
+
column: str | exp.Expression,
|
|
1132
|
+
value_if_not_null: str | exp.Expression | Any,
|
|
1133
|
+
value_if_null: str | exp.Expression | Any,
|
|
1137
1134
|
) -> ConversionExpression:
|
|
1138
1135
|
"""Create an NVL2 (Oracle-style) expression using CASE.
|
|
1139
1136
|
|
|
@@ -1246,8 +1243,8 @@ class SQLFactory:
|
|
|
1246
1243
|
|
|
1247
1244
|
def row_number(
|
|
1248
1245
|
self,
|
|
1249
|
-
partition_by:
|
|
1250
|
-
order_by:
|
|
1246
|
+
partition_by: str | list[str] | exp.Expression | None = None,
|
|
1247
|
+
order_by: str | list[str] | exp.Expression | None = None,
|
|
1251
1248
|
) -> FunctionExpression:
|
|
1252
1249
|
"""Create a ROW_NUMBER() window function.
|
|
1253
1250
|
|
|
@@ -1262,8 +1259,8 @@ class SQLFactory:
|
|
|
1262
1259
|
|
|
1263
1260
|
def rank(
|
|
1264
1261
|
self,
|
|
1265
|
-
partition_by:
|
|
1266
|
-
order_by:
|
|
1262
|
+
partition_by: str | list[str] | exp.Expression | None = None,
|
|
1263
|
+
order_by: str | list[str] | exp.Expression | None = None,
|
|
1267
1264
|
) -> FunctionExpression:
|
|
1268
1265
|
"""Create a RANK() window function.
|
|
1269
1266
|
|
|
@@ -1278,8 +1275,8 @@ class SQLFactory:
|
|
|
1278
1275
|
|
|
1279
1276
|
def dense_rank(
|
|
1280
1277
|
self,
|
|
1281
|
-
partition_by:
|
|
1282
|
-
order_by:
|
|
1278
|
+
partition_by: str | list[str] | exp.Expression | None = None,
|
|
1279
|
+
order_by: str | list[str] | exp.Expression | None = None,
|
|
1283
1280
|
) -> FunctionExpression:
|
|
1284
1281
|
"""Create a DENSE_RANK() window function.
|
|
1285
1282
|
|
|
@@ -1296,8 +1293,8 @@ class SQLFactory:
|
|
|
1296
1293
|
def _create_window_function(
|
|
1297
1294
|
func_name: str,
|
|
1298
1295
|
func_args: list[exp.Expression],
|
|
1299
|
-
partition_by:
|
|
1300
|
-
order_by:
|
|
1296
|
+
partition_by: str | list[str] | exp.Expression | None = None,
|
|
1297
|
+
order_by: str | list[str] | exp.Expression | None = None,
|
|
1301
1298
|
) -> FunctionExpression:
|
|
1302
1299
|
"""Helper to create window function expressions.
|
|
1303
1300
|
|