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/_base.py
CHANGED
|
@@ -6,7 +6,7 @@ Provides abstract base classes and core functionality for SQL query builders.
|
|
|
6
6
|
import hashlib
|
|
7
7
|
import uuid
|
|
8
8
|
from abc import ABC, abstractmethod
|
|
9
|
-
from typing import TYPE_CHECKING, Any, NoReturn,
|
|
9
|
+
from typing import TYPE_CHECKING, Any, NoReturn, cast
|
|
10
10
|
|
|
11
11
|
import sqlglot
|
|
12
12
|
from sqlglot import Dialect, exp
|
|
@@ -38,9 +38,7 @@ class SafeQuery:
|
|
|
38
38
|
|
|
39
39
|
__slots__ = ("dialect", "parameters", "sql")
|
|
40
40
|
|
|
41
|
-
def __init__(
|
|
42
|
-
self, sql: str, parameters: Optional[dict[str, Any]] = None, dialect: Optional[DialectType] = None
|
|
43
|
-
) -> None:
|
|
41
|
+
def __init__(self, sql: str, parameters: dict[str, Any] | None = None, dialect: DialectType | None = None) -> None:
|
|
44
42
|
self.sql = sql
|
|
45
43
|
self.parameters = parameters if parameters is not None else {}
|
|
46
44
|
self.dialect = dialect
|
|
@@ -68,8 +66,8 @@ class QueryBuilder(ABC):
|
|
|
68
66
|
|
|
69
67
|
def __init__(
|
|
70
68
|
self,
|
|
71
|
-
dialect:
|
|
72
|
-
schema:
|
|
69
|
+
dialect: DialectType | None = None,
|
|
70
|
+
schema: dict[str, dict[str, str]] | None = None,
|
|
73
71
|
enable_optimization: bool = True,
|
|
74
72
|
optimize_joins: bool = True,
|
|
75
73
|
optimize_predicates: bool = True,
|
|
@@ -82,7 +80,7 @@ class QueryBuilder(ABC):
|
|
|
82
80
|
self.optimize_predicates = optimize_predicates
|
|
83
81
|
self.simplify_expressions = simplify_expressions
|
|
84
82
|
|
|
85
|
-
self._expression:
|
|
83
|
+
self._expression: exp.Expression | None = None
|
|
86
84
|
self._parameters: dict[str, Any] = {}
|
|
87
85
|
self._parameter_counter: int = 0
|
|
88
86
|
self._with_ctes: dict[str, exp.CTE] = {}
|
|
@@ -95,7 +93,7 @@ class QueryBuilder(ABC):
|
|
|
95
93
|
"QueryBuilder._create_base_expression must return a valid sqlglot expression."
|
|
96
94
|
)
|
|
97
95
|
|
|
98
|
-
def get_expression(self) ->
|
|
96
|
+
def get_expression(self) -> exp.Expression | None:
|
|
99
97
|
"""Get expression reference (no copy).
|
|
100
98
|
|
|
101
99
|
Returns:
|
|
@@ -139,7 +137,7 @@ class QueryBuilder(ABC):
|
|
|
139
137
|
"""
|
|
140
138
|
|
|
141
139
|
@staticmethod
|
|
142
|
-
def _raise_sql_builder_error(message: str, cause:
|
|
140
|
+
def _raise_sql_builder_error(message: str, cause: BaseException | None = None) -> NoReturn:
|
|
143
141
|
"""Helper to raise SQLBuilderError, potentially with a cause.
|
|
144
142
|
|
|
145
143
|
Args:
|
|
@@ -191,7 +189,81 @@ class QueryBuilder(ABC):
|
|
|
191
189
|
msg = f"Failed to parse CTE query: {cause!s}"
|
|
192
190
|
raise SQLBuilderError(msg) from cause
|
|
193
191
|
|
|
194
|
-
def
|
|
192
|
+
def _build_final_expression(self, *, copy: bool = False) -> exp.Expression:
|
|
193
|
+
"""Construct the current expression with attached CTEs.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
copy: Whether to copy the underlying expression tree before
|
|
197
|
+
applying transformations.
|
|
198
|
+
|
|
199
|
+
Returns:
|
|
200
|
+
Expression representing the current builder state with CTEs applied.
|
|
201
|
+
"""
|
|
202
|
+
if self._expression is None:
|
|
203
|
+
self._raise_sql_builder_error("QueryBuilder expression not initialized.")
|
|
204
|
+
|
|
205
|
+
base_expression = self._expression.copy() if copy else self._expression
|
|
206
|
+
|
|
207
|
+
if not self._with_ctes:
|
|
208
|
+
return base_expression
|
|
209
|
+
|
|
210
|
+
final_expression: exp.Expression = base_expression
|
|
211
|
+
if has_with_method(final_expression):
|
|
212
|
+
for alias, cte_node in self._with_ctes.items():
|
|
213
|
+
final_expression = cast("Any", final_expression).with_(cte_node.args["this"], as_=alias, copy=False)
|
|
214
|
+
return cast("exp.Expression", final_expression)
|
|
215
|
+
|
|
216
|
+
if isinstance(final_expression, (exp.Select, exp.Insert, exp.Update, exp.Delete, exp.Union)):
|
|
217
|
+
return exp.With(expressions=list(self._with_ctes.values()), this=final_expression)
|
|
218
|
+
|
|
219
|
+
return final_expression
|
|
220
|
+
|
|
221
|
+
def _spawn_like_self(self: Self) -> Self:
|
|
222
|
+
"""Create a new builder instance with matching configuration."""
|
|
223
|
+
return type(self)(
|
|
224
|
+
dialect=self.dialect,
|
|
225
|
+
schema=self.schema,
|
|
226
|
+
enable_optimization=self.enable_optimization,
|
|
227
|
+
optimize_joins=self.optimize_joins,
|
|
228
|
+
optimize_predicates=self.optimize_predicates,
|
|
229
|
+
simplify_expressions=self.simplify_expressions,
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
def _resolve_cte_query(self, alias: str, query: "QueryBuilder | exp.Select | str") -> exp.Select:
|
|
233
|
+
"""Resolve a CTE query into a Select expression with merged parameters."""
|
|
234
|
+
if isinstance(query, QueryBuilder):
|
|
235
|
+
query_expr = query.get_expression()
|
|
236
|
+
if query_expr is None:
|
|
237
|
+
self._raise_cte_query_error(alias, "query builder has no expression")
|
|
238
|
+
if not isinstance(query_expr, exp.Select):
|
|
239
|
+
self._raise_cte_query_error(alias, f"expression must be a Select, got {type(query_expr).__name__}")
|
|
240
|
+
cte_select_expression = query_expr.copy()
|
|
241
|
+
param_mapping = self._merge_cte_parameters(alias, query.parameters)
|
|
242
|
+
updated_expression = self._update_placeholders_in_expression(cte_select_expression, param_mapping)
|
|
243
|
+
if not isinstance(updated_expression, exp.Select): # pragma: no cover - defensive
|
|
244
|
+
msg = "CTE placeholder update produced non-select expression"
|
|
245
|
+
raise SQLBuilderError(msg)
|
|
246
|
+
return updated_expression
|
|
247
|
+
|
|
248
|
+
if isinstance(query, str):
|
|
249
|
+
try:
|
|
250
|
+
parsed_expression = sqlglot.parse_one(query, read=self.dialect_name)
|
|
251
|
+
except SQLGlotParseError as e: # pragma: no cover - defensive
|
|
252
|
+
self._raise_cte_parse_error(e)
|
|
253
|
+
if not isinstance(parsed_expression, exp.Select):
|
|
254
|
+
self._raise_cte_query_error(
|
|
255
|
+
alias, f"query string must parse to SELECT, got {type(parsed_expression).__name__}"
|
|
256
|
+
)
|
|
257
|
+
return parsed_expression
|
|
258
|
+
|
|
259
|
+
if isinstance(query, exp.Select):
|
|
260
|
+
return query
|
|
261
|
+
|
|
262
|
+
self._raise_cte_query_error(alias, f"invalid query type: {type(query).__name__}")
|
|
263
|
+
msg = "Unreachable"
|
|
264
|
+
raise AssertionError(msg)
|
|
265
|
+
|
|
266
|
+
def _add_parameter(self, value: Any, context: str | None = None) -> str:
|
|
195
267
|
"""Adds a parameter to the query and returns its placeholder name.
|
|
196
268
|
|
|
197
269
|
Args:
|
|
@@ -232,7 +304,7 @@ class QueryBuilder(ABC):
|
|
|
232
304
|
|
|
233
305
|
return expression.transform(replacer, copy=False)
|
|
234
306
|
|
|
235
|
-
def add_parameter(self: Self, value: Any, name:
|
|
307
|
+
def add_parameter(self: Self, value: Any, name: str | None = None) -> tuple[Self, str]:
|
|
236
308
|
"""Explicitly adds a parameter to the query.
|
|
237
309
|
|
|
238
310
|
This is useful for parameters that are not directly tied to a
|
|
@@ -313,7 +385,7 @@ class QueryBuilder(ABC):
|
|
|
313
385
|
|
|
314
386
|
return expression.transform(placeholder_replacer, copy=False)
|
|
315
387
|
|
|
316
|
-
def _generate_builder_cache_key(self, config: "
|
|
388
|
+
def _generate_builder_cache_key(self, config: "StatementConfig | None" = None) -> str:
|
|
317
389
|
"""Generate cache key based on builder state and configuration.
|
|
318
390
|
|
|
319
391
|
Args:
|
|
@@ -356,7 +428,7 @@ class QueryBuilder(ABC):
|
|
|
356
428
|
state_string = "|".join(state_parts)
|
|
357
429
|
return f"builder:{hashlib.sha256(state_string.encode()).hexdigest()[:16]}"
|
|
358
430
|
|
|
359
|
-
def with_cte(self: Self, alias: str, query: "
|
|
431
|
+
def with_cte(self: Self, alias: str, query: "QueryBuilder | exp.Select | str") -> Self:
|
|
360
432
|
"""Adds a Common Table Expression (CTE) to the query.
|
|
361
433
|
|
|
362
434
|
Args:
|
|
@@ -370,35 +442,7 @@ class QueryBuilder(ABC):
|
|
|
370
442
|
if alias in self._with_ctes:
|
|
371
443
|
self._raise_sql_builder_error(f"CTE with alias '{alias}' already exists.")
|
|
372
444
|
|
|
373
|
-
cte_select_expression
|
|
374
|
-
|
|
375
|
-
if isinstance(query, QueryBuilder):
|
|
376
|
-
query_expr = query.get_expression()
|
|
377
|
-
if query_expr is None:
|
|
378
|
-
self._raise_cte_query_error(alias, "query builder has no expression")
|
|
379
|
-
if not isinstance(query_expr, exp.Select):
|
|
380
|
-
self._raise_cte_query_error(alias, f"expression must be a Select, got {type(query_expr).__name__}")
|
|
381
|
-
cte_select_expression = query_expr
|
|
382
|
-
param_mapping = self._merge_cte_parameters(alias, query.parameters)
|
|
383
|
-
cte_select_expression = cast(
|
|
384
|
-
"exp.Select", self._update_placeholders_in_expression(cte_select_expression, param_mapping)
|
|
385
|
-
)
|
|
386
|
-
|
|
387
|
-
elif isinstance(query, str):
|
|
388
|
-
try:
|
|
389
|
-
parsed_expression = sqlglot.parse_one(query, read=self.dialect_name)
|
|
390
|
-
if not isinstance(parsed_expression, exp.Select):
|
|
391
|
-
self._raise_cte_query_error(
|
|
392
|
-
alias, f"query string must parse to SELECT, got {type(parsed_expression).__name__}"
|
|
393
|
-
)
|
|
394
|
-
cte_select_expression = parsed_expression
|
|
395
|
-
except SQLGlotParseError as e:
|
|
396
|
-
self._raise_cte_parse_error(e)
|
|
397
|
-
elif isinstance(query, exp.Select):
|
|
398
|
-
cte_select_expression = query
|
|
399
|
-
else:
|
|
400
|
-
self._raise_cte_query_error(alias, f"invalid query type: {type(query).__name__}")
|
|
401
|
-
|
|
445
|
+
cte_select_expression = self._resolve_cte_query(alias, query)
|
|
402
446
|
self._with_ctes[alias] = exp.CTE(this=cte_select_expression, alias=exp.to_table(alias))
|
|
403
447
|
return self
|
|
404
448
|
|
|
@@ -408,18 +452,7 @@ class QueryBuilder(ABC):
|
|
|
408
452
|
Returns:
|
|
409
453
|
SafeQuery: A dataclass containing the SQL string and parameters.
|
|
410
454
|
"""
|
|
411
|
-
|
|
412
|
-
self._raise_sql_builder_error("QueryBuilder expression not initialized.")
|
|
413
|
-
|
|
414
|
-
if self._with_ctes:
|
|
415
|
-
final_expression = self._expression
|
|
416
|
-
if has_with_method(final_expression):
|
|
417
|
-
for alias, cte_node in self._with_ctes.items():
|
|
418
|
-
final_expression = cast("Any", final_expression).with_(cte_node.args["this"], as_=alias, copy=False)
|
|
419
|
-
elif isinstance(final_expression, (exp.Select, exp.Insert, exp.Update, exp.Delete, exp.Union)):
|
|
420
|
-
final_expression = exp.With(expressions=list(self._with_ctes.values()), this=final_expression)
|
|
421
|
-
else:
|
|
422
|
-
final_expression = self._expression
|
|
455
|
+
final_expression = self._build_final_expression()
|
|
423
456
|
|
|
424
457
|
if self.enable_optimization and isinstance(final_expression, exp.Expression):
|
|
425
458
|
final_expression = self._optimize_expression(final_expression)
|
|
@@ -475,7 +508,7 @@ class QueryBuilder(ABC):
|
|
|
475
508
|
else:
|
|
476
509
|
return optimized
|
|
477
510
|
|
|
478
|
-
def to_statement(self, config: "
|
|
511
|
+
def to_statement(self, config: "StatementConfig | None" = None) -> "SQL":
|
|
479
512
|
"""Converts the built query into a SQL statement object.
|
|
480
513
|
|
|
481
514
|
Args:
|
|
@@ -500,7 +533,7 @@ class QueryBuilder(ABC):
|
|
|
500
533
|
|
|
501
534
|
return sql_statement
|
|
502
535
|
|
|
503
|
-
def _to_statement(self, config: "
|
|
536
|
+
def _to_statement(self, config: "StatementConfig | None" = None) -> "SQL":
|
|
504
537
|
"""Internal method to create SQL statement.
|
|
505
538
|
|
|
506
539
|
Args:
|
|
@@ -541,7 +574,7 @@ class QueryBuilder(ABC):
|
|
|
541
574
|
|
|
542
575
|
def _extract_statement_parameters(
|
|
543
576
|
self, raw_parameters: Any
|
|
544
|
-
) -> "tuple[
|
|
577
|
+
) -> "tuple[dict[str, Any] | None, tuple[Any, ...] | None]":
|
|
545
578
|
"""Extract parameters for SQL statement creation.
|
|
546
579
|
|
|
547
580
|
Args:
|
|
@@ -570,7 +603,7 @@ class QueryBuilder(ABC):
|
|
|
570
603
|
return self.build().sql
|
|
571
604
|
|
|
572
605
|
@property
|
|
573
|
-
def dialect_name(self) -> "
|
|
606
|
+
def dialect_name(self) -> "str | None":
|
|
574
607
|
"""Returns the name of the dialect, if set."""
|
|
575
608
|
if isinstance(self.dialect, str):
|
|
576
609
|
return self.dialect
|
sqlspec/builder/_column.py
CHANGED
|
@@ -6,7 +6,7 @@ SQL conditions with parameter binding.
|
|
|
6
6
|
|
|
7
7
|
from collections.abc import Iterable
|
|
8
8
|
from datetime import date, datetime
|
|
9
|
-
from typing import Any,
|
|
9
|
+
from typing import Any, cast
|
|
10
10
|
|
|
11
11
|
from sqlglot import exp
|
|
12
12
|
|
|
@@ -77,7 +77,7 @@ class Column:
|
|
|
77
77
|
|
|
78
78
|
__slots__ = ("_expression", "name", "table")
|
|
79
79
|
|
|
80
|
-
def __init__(self, name: str, table:
|
|
80
|
+
def __init__(self, name: str, table: str | None = None) -> None:
|
|
81
81
|
self.name = name
|
|
82
82
|
self.table = table
|
|
83
83
|
|
|
@@ -122,7 +122,7 @@ class Column:
|
|
|
122
122
|
"""Apply NOT operator (~)."""
|
|
123
123
|
return ColumnExpression(exp.Not(this=self._expression))
|
|
124
124
|
|
|
125
|
-
def like(self, pattern: str, escape:
|
|
125
|
+
def like(self, pattern: str, escape: str | None = None) -> ColumnExpression:
|
|
126
126
|
"""SQL LIKE pattern matching."""
|
|
127
127
|
if escape:
|
|
128
128
|
like_expr = exp.Like(
|
|
@@ -159,7 +159,7 @@ class Column:
|
|
|
159
159
|
"""SQL IS NOT NULL."""
|
|
160
160
|
return ColumnExpression(exp.Not(this=exp.Is(this=self._expression, expression=exp.Null())))
|
|
161
161
|
|
|
162
|
-
def not_like(self, pattern: str, escape:
|
|
162
|
+
def not_like(self, pattern: str, escape: str | None = None) -> ColumnExpression:
|
|
163
163
|
"""SQL NOT LIKE pattern matching."""
|
|
164
164
|
return ~self.like(pattern, escape)
|
|
165
165
|
|
|
@@ -211,7 +211,7 @@ class Column:
|
|
|
211
211
|
"""SQL CEIL() function."""
|
|
212
212
|
return FunctionColumn(exp.Ceil(this=self._expression))
|
|
213
213
|
|
|
214
|
-
def substring(self, start: int, length:
|
|
214
|
+
def substring(self, start: int, length: int | None = None) -> "FunctionColumn":
|
|
215
215
|
"""SQL SUBSTRING() function."""
|
|
216
216
|
args = [self._convert_value(start)]
|
|
217
217
|
if length is not None:
|