sqlspec 0.13.0__py3-none-any.whl → 0.14.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 +39 -1
- sqlspec/adapters/adbc/config.py +4 -40
- sqlspec/adapters/adbc/driver.py +29 -16
- sqlspec/adapters/aiosqlite/config.py +15 -20
- sqlspec/adapters/aiosqlite/driver.py +36 -18
- sqlspec/adapters/asyncmy/config.py +16 -33
- sqlspec/adapters/asyncmy/driver.py +23 -16
- sqlspec/adapters/asyncpg/config.py +19 -61
- sqlspec/adapters/asyncpg/driver.py +41 -18
- sqlspec/adapters/bigquery/config.py +2 -43
- sqlspec/adapters/bigquery/driver.py +26 -14
- sqlspec/adapters/duckdb/config.py +2 -49
- sqlspec/adapters/duckdb/driver.py +35 -16
- sqlspec/adapters/oracledb/config.py +30 -83
- sqlspec/adapters/oracledb/driver.py +54 -27
- sqlspec/adapters/psqlpy/config.py +17 -57
- sqlspec/adapters/psqlpy/driver.py +28 -8
- sqlspec/adapters/psycopg/config.py +30 -73
- sqlspec/adapters/psycopg/driver.py +69 -24
- sqlspec/adapters/sqlite/config.py +3 -21
- sqlspec/adapters/sqlite/driver.py +50 -26
- sqlspec/cli.py +248 -0
- sqlspec/config.py +18 -20
- sqlspec/driver/_async.py +28 -10
- sqlspec/driver/_common.py +5 -4
- sqlspec/driver/_sync.py +28 -10
- sqlspec/driver/mixins/__init__.py +6 -0
- sqlspec/driver/mixins/_cache.py +114 -0
- sqlspec/driver/mixins/_pipeline.py +0 -4
- sqlspec/{service/base.py → driver/mixins/_query_tools.py} +86 -421
- sqlspec/driver/mixins/_result_utils.py +0 -2
- sqlspec/driver/mixins/_sql_translator.py +0 -2
- sqlspec/driver/mixins/_storage.py +4 -18
- sqlspec/driver/mixins/_type_coercion.py +0 -2
- sqlspec/driver/parameters.py +4 -4
- sqlspec/extensions/aiosql/adapter.py +4 -4
- sqlspec/extensions/litestar/__init__.py +2 -1
- sqlspec/extensions/litestar/cli.py +48 -0
- sqlspec/extensions/litestar/plugin.py +3 -0
- sqlspec/loader.py +1 -1
- sqlspec/migrations/__init__.py +23 -0
- sqlspec/migrations/base.py +390 -0
- sqlspec/migrations/commands.py +525 -0
- sqlspec/migrations/runner.py +215 -0
- sqlspec/migrations/tracker.py +153 -0
- sqlspec/migrations/utils.py +89 -0
- sqlspec/protocols.py +37 -3
- sqlspec/statement/builder/__init__.py +8 -8
- sqlspec/statement/builder/{column.py → _column.py} +82 -52
- sqlspec/statement/builder/{ddl.py → _ddl.py} +5 -5
- sqlspec/statement/builder/_ddl_utils.py +1 -1
- sqlspec/statement/builder/{delete.py → _delete.py} +1 -1
- sqlspec/statement/builder/{insert.py → _insert.py} +1 -1
- sqlspec/statement/builder/{merge.py → _merge.py} +1 -1
- sqlspec/statement/builder/_parsing_utils.py +5 -3
- sqlspec/statement/builder/{select.py → _select.py} +59 -61
- sqlspec/statement/builder/{update.py → _update.py} +2 -2
- sqlspec/statement/builder/mixins/__init__.py +24 -30
- sqlspec/statement/builder/mixins/{_set_ops.py → _cte_and_set_ops.py} +86 -2
- sqlspec/statement/builder/mixins/{_delete_from.py → _delete_operations.py} +2 -0
- sqlspec/statement/builder/mixins/{_insert_values.py → _insert_operations.py} +70 -1
- sqlspec/statement/builder/mixins/{_merge_clauses.py → _merge_operations.py} +2 -0
- sqlspec/statement/builder/mixins/_order_limit_operations.py +123 -0
- sqlspec/statement/builder/mixins/{_pivot.py → _pivot_operations.py} +71 -2
- sqlspec/statement/builder/mixins/_select_operations.py +612 -0
- sqlspec/statement/builder/mixins/{_update_set.py → _update_operations.py} +73 -2
- sqlspec/statement/builder/mixins/_where_clause.py +536 -0
- sqlspec/statement/cache.py +50 -0
- sqlspec/statement/filters.py +37 -8
- sqlspec/statement/parameters.py +154 -25
- sqlspec/statement/pipelines/__init__.py +1 -1
- sqlspec/statement/pipelines/context.py +4 -4
- sqlspec/statement/pipelines/transformers/_expression_simplifier.py +3 -3
- sqlspec/statement/pipelines/validators/_parameter_style.py +22 -22
- sqlspec/statement/pipelines/validators/_performance.py +1 -5
- sqlspec/statement/sql.py +246 -176
- sqlspec/utils/__init__.py +2 -1
- sqlspec/utils/statement_hashing.py +203 -0
- sqlspec/utils/type_guards.py +32 -0
- {sqlspec-0.13.0.dist-info → sqlspec-0.14.0.dist-info}/METADATA +1 -1
- sqlspec-0.14.0.dist-info/RECORD +143 -0
- sqlspec-0.14.0.dist-info/entry_points.txt +2 -0
- sqlspec/service/__init__.py +0 -4
- sqlspec/service/_util.py +0 -147
- sqlspec/service/pagination.py +0 -26
- sqlspec/statement/builder/mixins/_aggregate_functions.py +0 -250
- sqlspec/statement/builder/mixins/_case_builder.py +0 -91
- sqlspec/statement/builder/mixins/_common_table_expr.py +0 -90
- sqlspec/statement/builder/mixins/_from.py +0 -63
- sqlspec/statement/builder/mixins/_group_by.py +0 -118
- sqlspec/statement/builder/mixins/_having.py +0 -35
- sqlspec/statement/builder/mixins/_insert_from_select.py +0 -47
- sqlspec/statement/builder/mixins/_insert_into.py +0 -36
- sqlspec/statement/builder/mixins/_limit_offset.py +0 -53
- sqlspec/statement/builder/mixins/_order_by.py +0 -46
- sqlspec/statement/builder/mixins/_returning.py +0 -37
- sqlspec/statement/builder/mixins/_select_columns.py +0 -61
- sqlspec/statement/builder/mixins/_unpivot.py +0 -77
- sqlspec/statement/builder/mixins/_update_from.py +0 -55
- sqlspec/statement/builder/mixins/_update_table.py +0 -29
- sqlspec/statement/builder/mixins/_where.py +0 -401
- sqlspec/statement/builder/mixins/_window_functions.py +0 -86
- sqlspec/statement/parameter_manager.py +0 -220
- sqlspec/statement/sql_compiler.py +0 -140
- sqlspec-0.13.0.dist-info/RECORD +0 -150
- /sqlspec/statement/builder/{base.py → _base.py} +0 -0
- /sqlspec/statement/builder/mixins/{_join.py → _join_operations.py} +0 -0
- {sqlspec-0.13.0.dist-info → sqlspec-0.14.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.13.0.dist-info → sqlspec-0.14.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.13.0.dist-info → sqlspec-0.14.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,10 +1,15 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
from
|
|
1
|
+
# pyright: reportCallIssue=false, reportAttributeAccessIssue=false, reportArgumentType=false
|
|
2
|
+
import logging
|
|
3
|
+
from abc import ABC
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Generic, Optional, Union, overload
|
|
4
5
|
|
|
5
6
|
from sqlglot import exp, parse_one
|
|
7
|
+
from typing_extensions import Self
|
|
6
8
|
|
|
7
|
-
from sqlspec.
|
|
9
|
+
from sqlspec.exceptions import NotFoundError
|
|
10
|
+
from sqlspec.statement.filters import LimitOffsetFilter, OffsetPagination
|
|
11
|
+
from sqlspec.statement.sql import SQL
|
|
12
|
+
from sqlspec.typing import ConnectionT, ModelDTOT, RowT
|
|
8
13
|
from sqlspec.utils.type_guards import (
|
|
9
14
|
is_dict_row,
|
|
10
15
|
is_indexable_row,
|
|
@@ -14,43 +19,37 @@ from sqlspec.utils.type_guards import (
|
|
|
14
19
|
)
|
|
15
20
|
|
|
16
21
|
if TYPE_CHECKING:
|
|
17
|
-
from
|
|
18
|
-
from sqlspec.service.pagination import OffsetPagination
|
|
19
|
-
from sqlspec.statement import SQLConfig, Statement, StatementFilter
|
|
20
|
-
from sqlspec.statement.builder import Delete, Insert, QueryBuilder, Select, Update
|
|
21
|
-
from sqlspec.statement.sql import SQL
|
|
22
|
-
from sqlspec.typing import ModelDTOT, RowT, StatementParameters
|
|
22
|
+
from sqlglot.dialects.dialect import DialectType
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
from sqlspec.statement import Statement, StatementFilter
|
|
25
|
+
from sqlspec.statement.builder import Select
|
|
26
|
+
from sqlspec.statement.sql import SQLConfig
|
|
27
|
+
from sqlspec.typing import StatementParameters
|
|
25
28
|
|
|
29
|
+
__all__ = ("AsyncQueryMixin", "SyncQueryMixin")
|
|
26
30
|
|
|
27
|
-
|
|
28
|
-
SyncDriverT = TypeVar("SyncDriverT", bound="SyncDriverAdapterProtocol[Any]")
|
|
29
|
-
AsyncDriverT = TypeVar("AsyncDriverT", bound="AsyncDriverAdapterProtocol[Any]")
|
|
31
|
+
logger = logging.getLogger(__name__)
|
|
30
32
|
|
|
33
|
+
WINDOWS_PATH_MIN_LENGTH = 3
|
|
31
34
|
|
|
32
|
-
class SQLSpecSyncService(Generic[SyncDriverT, ConnectionT]):
|
|
33
|
-
"""Sync Service for database operations."""
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
self._connection = connection
|
|
36
|
+
class QueryBase(ABC, Generic[ConnectionT]):
|
|
37
|
+
"""Base class with common query functionality."""
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@property
|
|
44
|
-
def driver(self) -> "SyncDriverT":
|
|
45
|
-
"""Get the driver instance."""
|
|
46
|
-
return self._driver
|
|
39
|
+
config: Any
|
|
40
|
+
_connection: Any
|
|
41
|
+
dialect: "DialectType"
|
|
47
42
|
|
|
48
43
|
@property
|
|
49
44
|
def connection(self) -> "ConnectionT":
|
|
50
45
|
"""Get the connection instance."""
|
|
51
|
-
return self._connection
|
|
46
|
+
return self._connection # type: ignore[no-any-return]
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def new(cls, connection: "ConnectionT") -> Self:
|
|
50
|
+
return cls(connection) # type: ignore[call-arg]
|
|
52
51
|
|
|
53
|
-
def
|
|
52
|
+
def _transform_to_sql(
|
|
54
53
|
self,
|
|
55
54
|
statement: "Union[Statement, Select]",
|
|
56
55
|
params: "Optional[dict[str, Any]]" = None,
|
|
@@ -64,9 +63,8 @@ class SQLSpecSyncService(Generic[SyncDriverT, ConnectionT]):
|
|
|
64
63
|
config: Optional SQL configuration
|
|
65
64
|
|
|
66
65
|
Returns:
|
|
67
|
-
A
|
|
66
|
+
A converted SQL object
|
|
68
67
|
"""
|
|
69
|
-
from sqlspec.statement.sql import SQL
|
|
70
68
|
|
|
71
69
|
if is_select_builder(statement):
|
|
72
70
|
# Select has its own parameters via build(), ignore external params
|
|
@@ -84,106 +82,9 @@ class SQLSpecSyncService(Generic[SyncDriverT, ConnectionT]):
|
|
|
84
82
|
msg = f"Unsupported statement type: {type(statement).__name__}"
|
|
85
83
|
raise TypeError(msg)
|
|
86
84
|
|
|
87
|
-
@overload
|
|
88
|
-
def execute(
|
|
89
|
-
self,
|
|
90
|
-
statement: "Select",
|
|
91
|
-
/,
|
|
92
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
93
|
-
schema_type: "type[ModelDTOT]",
|
|
94
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
95
|
-
_config: "Optional[SQLConfig]" = None,
|
|
96
|
-
**kwargs: Any,
|
|
97
|
-
) -> "list[ModelDTOT]": ...
|
|
98
|
-
|
|
99
|
-
@overload
|
|
100
|
-
def execute(
|
|
101
|
-
self,
|
|
102
|
-
statement: "Select",
|
|
103
|
-
/,
|
|
104
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
105
|
-
schema_type: None = None,
|
|
106
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
107
|
-
_config: "Optional[SQLConfig]" = None,
|
|
108
|
-
**kwargs: Any,
|
|
109
|
-
) -> "list[RowT]": ...
|
|
110
|
-
|
|
111
|
-
@overload
|
|
112
|
-
def execute(
|
|
113
|
-
self,
|
|
114
|
-
statement: "Union[Insert, Update, Delete]",
|
|
115
|
-
/,
|
|
116
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
117
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
118
|
-
_config: "Optional[SQLConfig]" = None,
|
|
119
|
-
**kwargs: Any,
|
|
120
|
-
) -> "list[RowT]": ...
|
|
121
|
-
|
|
122
|
-
@overload
|
|
123
|
-
def execute(
|
|
124
|
-
self,
|
|
125
|
-
statement: "Union[str, SQL]", # exp.Expression
|
|
126
|
-
/,
|
|
127
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
128
|
-
schema_type: "type[ModelDTOT]",
|
|
129
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
130
|
-
_config: "Optional[SQLConfig]" = None,
|
|
131
|
-
**kwargs: Any,
|
|
132
|
-
) -> "list[ModelDTOT]": ...
|
|
133
|
-
|
|
134
|
-
@overload
|
|
135
|
-
def execute(
|
|
136
|
-
self,
|
|
137
|
-
statement: "Union[str, SQL]",
|
|
138
|
-
/,
|
|
139
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
140
|
-
schema_type: None = None,
|
|
141
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
142
|
-
_config: "Optional[SQLConfig]" = None,
|
|
143
|
-
**kwargs: Any,
|
|
144
|
-
) -> "list[RowT]": ...
|
|
145
|
-
|
|
146
|
-
def execute(
|
|
147
|
-
self,
|
|
148
|
-
statement: "Union[Statement, QueryBuilder[Any]]",
|
|
149
|
-
/,
|
|
150
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
151
|
-
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
152
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
153
|
-
_config: "Optional[SQLConfig]" = None,
|
|
154
|
-
**kwargs: Any,
|
|
155
|
-
) -> Any:
|
|
156
|
-
"""Execute a statement and return the result."""
|
|
157
|
-
result = self.driver.execute(
|
|
158
|
-
statement, *parameters, schema_type=schema_type, _connection=_connection, _config=_config, **kwargs
|
|
159
|
-
)
|
|
160
|
-
return result.get_data()
|
|
161
|
-
|
|
162
|
-
def execute_many(
|
|
163
|
-
self,
|
|
164
|
-
statement: "Union[Statement, QueryBuilder[Any]]",
|
|
165
|
-
/,
|
|
166
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
167
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
168
|
-
_config: "Optional[SQLConfig]" = None,
|
|
169
|
-
**kwargs: Any,
|
|
170
|
-
) -> Any:
|
|
171
|
-
"""Execute a statement multiple times and return the result."""
|
|
172
|
-
result = self.driver.execute_many(statement, *parameters, _connection=_connection, _config=_config, **kwargs)
|
|
173
|
-
return result.get_data()
|
|
174
85
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
statement: "Statement",
|
|
178
|
-
/,
|
|
179
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
180
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
181
|
-
_config: "Optional[SQLConfig]" = None,
|
|
182
|
-
**kwargs: Any,
|
|
183
|
-
) -> Any:
|
|
184
|
-
"""Execute a script statement."""
|
|
185
|
-
result = self.driver.execute_script(statement, *parameters, _connection=_connection, _config=_config, **kwargs)
|
|
186
|
-
return result.get_data()
|
|
86
|
+
class SyncQueryMixin(QueryBase[ConnectionT]):
|
|
87
|
+
"""Unified storage operations for synchronous drivers."""
|
|
187
88
|
|
|
188
89
|
@overload
|
|
189
90
|
def select_one(
|
|
@@ -207,7 +108,7 @@ class SQLSpecSyncService(Generic[SyncDriverT, ConnectionT]):
|
|
|
207
108
|
_connection: "Optional[ConnectionT]" = None,
|
|
208
109
|
_config: "Optional[SQLConfig]" = None,
|
|
209
110
|
**kwargs: Any,
|
|
210
|
-
) -> "RowT": ...
|
|
111
|
+
) -> "RowT": ... # type: ignore[type-var]
|
|
211
112
|
|
|
212
113
|
def select_one(
|
|
213
114
|
self,
|
|
@@ -218,26 +119,25 @@ class SQLSpecSyncService(Generic[SyncDriverT, ConnectionT]):
|
|
|
218
119
|
_connection: "Optional[ConnectionT]" = None,
|
|
219
120
|
_config: "Optional[SQLConfig]" = None,
|
|
220
121
|
**kwargs: Any,
|
|
221
|
-
) ->
|
|
122
|
+
) -> "Union[RowT, ModelDTOT]":
|
|
222
123
|
"""Execute a select statement and return exactly one row.
|
|
223
124
|
|
|
224
125
|
Raises an exception if no rows or more than one row is returned.
|
|
225
126
|
"""
|
|
226
|
-
result = self.
|
|
127
|
+
result = self.execute( # type: ignore[attr-defined]
|
|
227
128
|
statement, *parameters, schema_type=schema_type, _connection=_connection, _config=_config, **kwargs
|
|
228
129
|
)
|
|
229
130
|
data = result.get_data()
|
|
230
|
-
# For select operations, data should be a list
|
|
231
131
|
if not isinstance(data, list):
|
|
232
132
|
msg = "Expected list result from select operation"
|
|
233
133
|
raise TypeError(msg)
|
|
234
134
|
if not data:
|
|
235
135
|
msg = "No rows found"
|
|
236
|
-
raise
|
|
136
|
+
raise NotFoundError(msg)
|
|
237
137
|
if len(data) > 1:
|
|
238
138
|
msg = f"Expected exactly one row, found {len(data)}"
|
|
239
139
|
raise ValueError(msg)
|
|
240
|
-
return data[0]
|
|
140
|
+
return data[0] # type: ignore[no-any-return]
|
|
241
141
|
|
|
242
142
|
@overload
|
|
243
143
|
def select_one_or_none(
|
|
@@ -271,14 +171,14 @@ class SQLSpecSyncService(Generic[SyncDriverT, ConnectionT]):
|
|
|
271
171
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
272
172
|
_connection: "Optional[ConnectionT]" = None,
|
|
273
173
|
_config: "Optional[SQLConfig]" = None,
|
|
274
|
-
**kwargs:
|
|
174
|
+
**kwargs: "Optional[Union[RowT, ModelDTOT]]",
|
|
275
175
|
) -> Any:
|
|
276
176
|
"""Execute a select statement and return at most one row.
|
|
277
177
|
|
|
278
178
|
Returns None if no rows are found.
|
|
279
179
|
Raises an exception if more than one row is returned.
|
|
280
180
|
"""
|
|
281
|
-
result = self.
|
|
181
|
+
result = self.execute( # type: ignore[attr-defined]
|
|
282
182
|
statement, *parameters, schema_type=schema_type, _connection=_connection, _config=_config, **kwargs
|
|
283
183
|
)
|
|
284
184
|
data = result.get_data()
|
|
@@ -326,9 +226,9 @@ class SQLSpecSyncService(Generic[SyncDriverT, ConnectionT]):
|
|
|
326
226
|
_connection: "Optional[ConnectionT]" = None,
|
|
327
227
|
_config: "Optional[SQLConfig]" = None,
|
|
328
228
|
**kwargs: Any,
|
|
329
|
-
) ->
|
|
229
|
+
) -> Union[list[RowT], list[ModelDTOT]]:
|
|
330
230
|
"""Execute a select statement and return all rows."""
|
|
331
|
-
result = self.
|
|
231
|
+
result = self.execute( # type: ignore[attr-defined]
|
|
332
232
|
statement, *parameters, schema_type=schema_type, _connection=_connection, _config=_config, **kwargs
|
|
333
233
|
)
|
|
334
234
|
data = result.get_data()
|
|
@@ -352,7 +252,7 @@ class SQLSpecSyncService(Generic[SyncDriverT, ConnectionT]):
|
|
|
352
252
|
Expects exactly one row with one column.
|
|
353
253
|
Raises an exception if no rows or more than one row/column is returned.
|
|
354
254
|
"""
|
|
355
|
-
result = self.
|
|
255
|
+
result = self.execute(statement, *parameters, _connection=_connection, _config=_config, **kwargs) # type: ignore[attr-defined]
|
|
356
256
|
data = result.get_data()
|
|
357
257
|
# For select operations, data should be a list
|
|
358
258
|
if not isinstance(data, list):
|
|
@@ -360,7 +260,7 @@ class SQLSpecSyncService(Generic[SyncDriverT, ConnectionT]):
|
|
|
360
260
|
raise TypeError(msg)
|
|
361
261
|
if not data:
|
|
362
262
|
msg = "No rows found"
|
|
363
|
-
raise
|
|
263
|
+
raise NotFoundError(msg)
|
|
364
264
|
if len(data) > 1:
|
|
365
265
|
msg = f"Expected exactly one row, found {len(data)}"
|
|
366
266
|
raise ValueError(msg)
|
|
@@ -371,7 +271,6 @@ class SQLSpecSyncService(Generic[SyncDriverT, ConnectionT]):
|
|
|
371
271
|
raise ValueError(msg)
|
|
372
272
|
return next(iter(row.values()))
|
|
373
273
|
if is_indexable_row(row):
|
|
374
|
-
# Tuple or list-like row
|
|
375
274
|
if not row:
|
|
376
275
|
msg = "Row has no columns"
|
|
377
276
|
raise ValueError(msg)
|
|
@@ -394,7 +293,7 @@ class SQLSpecSyncService(Generic[SyncDriverT, ConnectionT]):
|
|
|
394
293
|
Expects at most one row with one column.
|
|
395
294
|
Raises an exception if more than one row is returned.
|
|
396
295
|
"""
|
|
397
|
-
result = self.
|
|
296
|
+
result = self.execute(statement, *parameters, _connection=_connection, _config=_config, **kwargs) # type: ignore[attr-defined]
|
|
398
297
|
data = result.get_data()
|
|
399
298
|
# For select operations, data should be a list
|
|
400
299
|
if not isinstance(data, list):
|
|
@@ -452,7 +351,7 @@ class SQLSpecSyncService(Generic[SyncDriverT, ConnectionT]):
|
|
|
452
351
|
_connection: "Optional[ConnectionT]" = None,
|
|
453
352
|
_config: "Optional[SQLConfig]" = None,
|
|
454
353
|
**kwargs: Any,
|
|
455
|
-
) ->
|
|
354
|
+
) -> "Union[OffsetPagination[RowT], OffsetPagination[ModelDTOT]]":
|
|
456
355
|
"""Execute a paginated query with automatic counting.
|
|
457
356
|
|
|
458
357
|
This method performs two queries:
|
|
@@ -513,15 +412,12 @@ class SQLSpecSyncService(Generic[SyncDriverT, ConnectionT]):
|
|
|
513
412
|
... schema_type=User,
|
|
514
413
|
... )
|
|
515
414
|
"""
|
|
516
|
-
from sqlspec.service.pagination import OffsetPagination
|
|
517
|
-
from sqlspec.statement.sql import SQL
|
|
518
415
|
|
|
519
416
|
# Separate filters from parameters
|
|
520
417
|
filters: list[StatementFilter] = []
|
|
521
418
|
params: list[Any] = []
|
|
522
419
|
|
|
523
420
|
for p in parameters:
|
|
524
|
-
# Use type guard to check if it implements the StatementFilter protocol
|
|
525
421
|
if is_statement_filter(p):
|
|
526
422
|
filters.append(p)
|
|
527
423
|
else:
|
|
@@ -546,198 +442,46 @@ class SQLSpecSyncService(Generic[SyncDriverT, ConnectionT]):
|
|
|
546
442
|
msg = "Pagination requires either a LimitOffsetFilter in parameters or 'limit' and 'offset' in kwargs."
|
|
547
443
|
raise ValueError(msg)
|
|
548
444
|
|
|
549
|
-
base_stmt = self.
|
|
445
|
+
base_stmt = self._transform_to_sql(statement, params, _config) # type: ignore[arg-type]
|
|
550
446
|
|
|
551
447
|
filtered_stmt = base_stmt
|
|
552
448
|
for filter_obj in other_filters:
|
|
553
449
|
filtered_stmt = filter_obj.append_to_statement(filtered_stmt)
|
|
554
450
|
|
|
555
451
|
sql_str = filtered_stmt.to_sql()
|
|
556
|
-
|
|
557
|
-
# Parse and transform the AST to create a count query
|
|
558
452
|
parsed = parse_one(sql_str)
|
|
559
453
|
|
|
560
454
|
# Using exp.Subquery to properly wrap the parsed expression
|
|
561
455
|
subquery = exp.Subquery(this=parsed, alias="_count_subquery")
|
|
562
456
|
count_ast = exp.Select().select(exp.func("COUNT", exp.Star()).as_("total")).from_(subquery)
|
|
563
457
|
|
|
564
|
-
|
|
458
|
+
# Preserve parameters from the original statement
|
|
459
|
+
count_stmt = SQL(count_ast, parameters=filtered_stmt.parameters, _config=_config)
|
|
565
460
|
|
|
566
461
|
# Execute count query
|
|
567
462
|
total = self.select_value(count_stmt, _connection=_connection, _config=_config, **kwargs)
|
|
568
463
|
|
|
569
|
-
data_stmt = self.
|
|
464
|
+
data_stmt = self._transform_to_sql(statement, params, _config) # type: ignore[arg-type]
|
|
570
465
|
|
|
571
466
|
for filter_obj in other_filters:
|
|
572
467
|
data_stmt = filter_obj.append_to_statement(data_stmt)
|
|
573
468
|
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
# Execute data query
|
|
577
|
-
items = self.select(data_stmt, schema_type=schema_type, _connection=_connection, _config=_config, **kwargs)
|
|
578
|
-
|
|
579
|
-
return OffsetPagination(items=items, limit=limit, offset=offset, total=total)
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
class SQLSpecAsyncService(Generic[AsyncDriverT, ConnectionT]):
|
|
583
|
-
"""Async Service for database operations."""
|
|
584
|
-
|
|
585
|
-
def __init__(self, driver: "AsyncDriverT", connection: "ConnectionT") -> None:
|
|
586
|
-
self._driver = driver
|
|
587
|
-
self._connection = connection
|
|
588
|
-
|
|
589
|
-
@classmethod
|
|
590
|
-
def new(cls, driver: "AsyncDriverT", connection: "ConnectionT") -> "SQLSpecAsyncService[AsyncDriverT, ConnectionT]":
|
|
591
|
-
return cls(driver=driver, connection=connection)
|
|
592
|
-
|
|
593
|
-
@property
|
|
594
|
-
def driver(self) -> "AsyncDriverT":
|
|
595
|
-
"""Get the driver instance."""
|
|
596
|
-
return self._driver
|
|
597
|
-
|
|
598
|
-
@property
|
|
599
|
-
def connection(self) -> "ConnectionT":
|
|
600
|
-
"""Get the connection instance."""
|
|
601
|
-
return self._connection
|
|
602
|
-
|
|
603
|
-
def _normalize_statement(
|
|
604
|
-
self,
|
|
605
|
-
statement: "Union[Statement, Select]",
|
|
606
|
-
params: "Optional[dict[str, Any]]" = None,
|
|
607
|
-
config: "Optional[SQLConfig]" = None,
|
|
608
|
-
) -> "SQL":
|
|
609
|
-
"""Normalize a statement of any supported type into a SQL object.
|
|
610
|
-
|
|
611
|
-
Args:
|
|
612
|
-
statement: The statement to normalize (str, Expression, SQL, or Select)
|
|
613
|
-
params: Optional parameters (ignored for Select and SQL objects)
|
|
614
|
-
config: Optional SQL configuration
|
|
615
|
-
|
|
616
|
-
Returns:
|
|
617
|
-
A normalized SQL object
|
|
618
|
-
"""
|
|
619
|
-
from sqlspec.statement.sql import SQL
|
|
620
|
-
|
|
621
|
-
if is_select_builder(statement):
|
|
622
|
-
# Select has its own parameters via build(), ignore external params
|
|
623
|
-
safe_query = statement.build()
|
|
624
|
-
return SQL(safe_query.sql, parameters=safe_query.parameters, config=config)
|
|
625
|
-
|
|
626
|
-
if isinstance(statement, SQL):
|
|
627
|
-
# SQL object is already complete, ignore external params
|
|
628
|
-
return statement
|
|
629
|
-
|
|
630
|
-
if isinstance(statement, (str, exp.Expression)):
|
|
631
|
-
return SQL(statement, parameters=params, config=config)
|
|
632
|
-
|
|
633
|
-
# Fallback for type safety
|
|
634
|
-
msg = f"Unsupported statement type: {type(statement).__name__}"
|
|
635
|
-
raise TypeError(msg)
|
|
469
|
+
# Apply limit and offset using LimitOffsetFilter
|
|
470
|
+
from sqlspec.statement.filters import LimitOffsetFilter
|
|
636
471
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
self,
|
|
640
|
-
statement: "Select",
|
|
641
|
-
/,
|
|
642
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
643
|
-
schema_type: "type[ModelDTOT]",
|
|
644
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
645
|
-
_config: "Optional[SQLConfig]" = None,
|
|
646
|
-
**kwargs: Any,
|
|
647
|
-
) -> "list[ModelDTOT]": ...
|
|
472
|
+
limit_offset = LimitOffsetFilter(limit=limit, offset=offset)
|
|
473
|
+
data_stmt = limit_offset.append_to_statement(data_stmt)
|
|
648
474
|
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
statement: "Select",
|
|
653
|
-
/,
|
|
654
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
655
|
-
schema_type: None = None,
|
|
656
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
657
|
-
_config: "Optional[SQLConfig]" = None,
|
|
658
|
-
**kwargs: Any,
|
|
659
|
-
) -> "list[RowT]": ...
|
|
660
|
-
|
|
661
|
-
@overload
|
|
662
|
-
async def execute(
|
|
663
|
-
self,
|
|
664
|
-
statement: "Union[Insert, Update, Delete]",
|
|
665
|
-
/,
|
|
666
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
667
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
668
|
-
_config: "Optional[SQLConfig]" = None,
|
|
669
|
-
**kwargs: Any,
|
|
670
|
-
) -> "list[RowT]": ...
|
|
671
|
-
|
|
672
|
-
@overload
|
|
673
|
-
async def execute(
|
|
674
|
-
self,
|
|
675
|
-
statement: "Union[str, SQL]", # exp.Expression
|
|
676
|
-
/,
|
|
677
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
678
|
-
schema_type: "type[ModelDTOT]",
|
|
679
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
680
|
-
_config: "Optional[SQLConfig]" = None,
|
|
681
|
-
**kwargs: Any,
|
|
682
|
-
) -> "list[ModelDTOT]": ...
|
|
683
|
-
|
|
684
|
-
@overload
|
|
685
|
-
async def execute(
|
|
686
|
-
self,
|
|
687
|
-
statement: "Union[str, SQL]",
|
|
688
|
-
/,
|
|
689
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
690
|
-
schema_type: None = None,
|
|
691
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
692
|
-
_config: "Optional[SQLConfig]" = None,
|
|
693
|
-
**kwargs: Any,
|
|
694
|
-
) -> "list[RowT]": ...
|
|
695
|
-
|
|
696
|
-
async def execute(
|
|
697
|
-
self,
|
|
698
|
-
statement: "Union[Statement, QueryBuilder[Any]]",
|
|
699
|
-
/,
|
|
700
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
701
|
-
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
702
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
703
|
-
_config: "Optional[SQLConfig]" = None,
|
|
704
|
-
**kwargs: Any,
|
|
705
|
-
) -> Any:
|
|
706
|
-
"""Execute a statement and return the result."""
|
|
707
|
-
result = await self.driver.execute(
|
|
708
|
-
statement, *parameters, schema_type=schema_type, _connection=_connection, _config=_config, **kwargs
|
|
475
|
+
# Execute data query
|
|
476
|
+
items = self.select(
|
|
477
|
+
data_stmt, params, schema_type=schema_type, _connection=_connection, _config=_config, **kwargs
|
|
709
478
|
)
|
|
710
|
-
return result.get_data()
|
|
711
479
|
|
|
712
|
-
|
|
713
|
-
self,
|
|
714
|
-
statement: "Union[Statement, QueryBuilder[Any]]",
|
|
715
|
-
/,
|
|
716
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
717
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
718
|
-
_config: "Optional[SQLConfig]" = None,
|
|
719
|
-
**kwargs: Any,
|
|
720
|
-
) -> Any:
|
|
721
|
-
"""Execute a statement multiple times and return the result."""
|
|
722
|
-
result = await self.driver.execute_many(
|
|
723
|
-
statement, *parameters, _connection=_connection, _config=_config, **kwargs
|
|
724
|
-
)
|
|
725
|
-
return result.get_data()
|
|
480
|
+
return OffsetPagination(items=items, limit=limit, offset=offset, total=total) # pyright: ignore
|
|
726
481
|
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
/,
|
|
731
|
-
*parameters: "Union[StatementParameters, StatementFilter]",
|
|
732
|
-
_connection: "Optional[ConnectionT]" = None,
|
|
733
|
-
_config: "Optional[SQLConfig]" = None,
|
|
734
|
-
**kwargs: Any,
|
|
735
|
-
) -> Any:
|
|
736
|
-
"""Execute a script statement."""
|
|
737
|
-
result = await self.driver.execute_script(
|
|
738
|
-
statement, *parameters, _connection=_connection, _config=_config, **kwargs
|
|
739
|
-
)
|
|
740
|
-
return result.get_data()
|
|
482
|
+
|
|
483
|
+
class AsyncQueryMixin(QueryBase[ConnectionT]):
|
|
484
|
+
"""Unified query operations for asynchronous drivers."""
|
|
741
485
|
|
|
742
486
|
@overload
|
|
743
487
|
async def select_one(
|
|
@@ -772,26 +516,22 @@ class SQLSpecAsyncService(Generic[AsyncDriverT, ConnectionT]):
|
|
|
772
516
|
_connection: "Optional[ConnectionT]" = None,
|
|
773
517
|
_config: "Optional[SQLConfig]" = None,
|
|
774
518
|
**kwargs: Any,
|
|
775
|
-
) ->
|
|
519
|
+
) -> "Union[RowT, ModelDTOT]":
|
|
776
520
|
"""Execute a select statement and return exactly one row.
|
|
777
521
|
|
|
778
522
|
Raises an exception if no rows or more than one row is returned.
|
|
779
523
|
"""
|
|
780
|
-
result = await self.
|
|
524
|
+
result = await self.execute( # type: ignore[attr-defined]
|
|
781
525
|
statement, *parameters, schema_type=schema_type, _connection=_connection, _config=_config, **kwargs
|
|
782
526
|
)
|
|
783
527
|
data = result.get_data()
|
|
784
|
-
# For select operations, data should be a list
|
|
785
|
-
if not isinstance(data, list):
|
|
786
|
-
msg = "Expected list result from select operation"
|
|
787
|
-
raise TypeError(msg)
|
|
788
528
|
if not data:
|
|
789
529
|
msg = "No rows found"
|
|
790
|
-
raise
|
|
530
|
+
raise NotFoundError(msg)
|
|
791
531
|
if len(data) > 1:
|
|
792
532
|
msg = f"Expected exactly one row, found {len(data)}"
|
|
793
533
|
raise ValueError(msg)
|
|
794
|
-
return data[0]
|
|
534
|
+
return data[0] # type: ignore[no-any-return]
|
|
795
535
|
|
|
796
536
|
@overload
|
|
797
537
|
async def select_one_or_none(
|
|
@@ -826,26 +566,22 @@ class SQLSpecAsyncService(Generic[AsyncDriverT, ConnectionT]):
|
|
|
826
566
|
_connection: "Optional[ConnectionT]" = None,
|
|
827
567
|
_config: "Optional[SQLConfig]" = None,
|
|
828
568
|
**kwargs: Any,
|
|
829
|
-
) ->
|
|
569
|
+
) -> "Optional[Union[RowT, ModelDTOT]]":
|
|
830
570
|
"""Execute a select statement and return at most one row.
|
|
831
571
|
|
|
832
572
|
Returns None if no rows are found.
|
|
833
573
|
Raises an exception if more than one row is returned.
|
|
834
574
|
"""
|
|
835
|
-
result = await self.
|
|
575
|
+
result = await self.execute( # type: ignore[attr-defined]
|
|
836
576
|
statement, *parameters, schema_type=schema_type, _connection=_connection, _config=_config, **kwargs
|
|
837
577
|
)
|
|
838
578
|
data = result.get_data()
|
|
839
|
-
# For select operations, data should be a list
|
|
840
|
-
if not isinstance(data, list):
|
|
841
|
-
msg = "Expected list result from select operation"
|
|
842
|
-
raise TypeError(msg)
|
|
843
579
|
if not data:
|
|
844
580
|
return None
|
|
845
581
|
if len(data) > 1:
|
|
846
582
|
msg = f"Expected at most one row, found {len(data)}"
|
|
847
583
|
raise ValueError(msg)
|
|
848
|
-
return data[0]
|
|
584
|
+
return data[0] # type: ignore[no-any-return]
|
|
849
585
|
|
|
850
586
|
@overload
|
|
851
587
|
async def select(
|
|
@@ -880,17 +616,12 @@ class SQLSpecAsyncService(Generic[AsyncDriverT, ConnectionT]):
|
|
|
880
616
|
_connection: "Optional[ConnectionT]" = None,
|
|
881
617
|
_config: "Optional[SQLConfig]" = None,
|
|
882
618
|
**kwargs: Any,
|
|
883
|
-
) ->
|
|
619
|
+
) -> "Union[list[RowT], list[ModelDTOT]]":
|
|
884
620
|
"""Execute a select statement and return all rows."""
|
|
885
|
-
result = await self.
|
|
621
|
+
result = await self.execute( # type: ignore[attr-defined]
|
|
886
622
|
statement, *parameters, schema_type=schema_type, _connection=_connection, _config=_config, **kwargs
|
|
887
623
|
)
|
|
888
|
-
|
|
889
|
-
# For select operations, data should be a list
|
|
890
|
-
if not isinstance(data, list):
|
|
891
|
-
msg = "Expected list result from select operation"
|
|
892
|
-
raise TypeError(msg)
|
|
893
|
-
return data
|
|
624
|
+
return result.get_data() # type: ignore[no-any-return]
|
|
894
625
|
|
|
895
626
|
async def select_value(
|
|
896
627
|
self,
|
|
@@ -906,19 +637,11 @@ class SQLSpecAsyncService(Generic[AsyncDriverT, ConnectionT]):
|
|
|
906
637
|
Expects exactly one row with one column.
|
|
907
638
|
Raises an exception if no rows or more than one row/column is returned.
|
|
908
639
|
"""
|
|
909
|
-
result = await self.
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
if not isinstance(data, list):
|
|
913
|
-
msg = "Expected list result from select operation"
|
|
914
|
-
raise TypeError(msg)
|
|
915
|
-
if not data:
|
|
640
|
+
result = await self.execute(statement, *parameters, _connection=_connection, _config=_config, **kwargs) # type: ignore[attr-defined]
|
|
641
|
+
row = result.one()
|
|
642
|
+
if not row:
|
|
916
643
|
msg = "No rows found"
|
|
917
|
-
raise
|
|
918
|
-
if len(data) > 1:
|
|
919
|
-
msg = f"Expected exactly one row, found {len(data)}"
|
|
920
|
-
raise ValueError(msg)
|
|
921
|
-
row = data[0]
|
|
644
|
+
raise NotFoundError(msg)
|
|
922
645
|
if is_dict_row(row):
|
|
923
646
|
if not row:
|
|
924
647
|
msg = "Row has no columns"
|
|
@@ -948,7 +671,9 @@ class SQLSpecAsyncService(Generic[AsyncDriverT, ConnectionT]):
|
|
|
948
671
|
Expects at most one row with one column.
|
|
949
672
|
Raises an exception if more than one row is returned.
|
|
950
673
|
"""
|
|
951
|
-
result = await self.
|
|
674
|
+
result = await self.execute( # type: ignore[attr-defined]
|
|
675
|
+
statement, *parameters, _connection=_connection, _config=_config, **kwargs
|
|
676
|
+
)
|
|
952
677
|
data = result.get_data()
|
|
953
678
|
# For select operations, data should be a list
|
|
954
679
|
if not isinstance(data, list):
|
|
@@ -1007,65 +732,7 @@ class SQLSpecAsyncService(Generic[AsyncDriverT, ConnectionT]):
|
|
|
1007
732
|
_connection: "Optional[ConnectionT]" = None,
|
|
1008
733
|
_config: "Optional[SQLConfig]" = None,
|
|
1009
734
|
**kwargs: Any,
|
|
1010
|
-
) ->
|
|
1011
|
-
"""Execute a paginated query with automatic counting.
|
|
1012
|
-
|
|
1013
|
-
This method performs two queries:
|
|
1014
|
-
1. A count query to get the total number of results
|
|
1015
|
-
2. A data query with limit/offset applied
|
|
1016
|
-
|
|
1017
|
-
Pagination can be specified either via LimitOffsetFilter in parameters
|
|
1018
|
-
or via 'limit' and 'offset' in kwargs.
|
|
1019
|
-
|
|
1020
|
-
Args:
|
|
1021
|
-
statement: The SELECT statement to paginate
|
|
1022
|
-
*parameters: Statement parameters and filters (can include LimitOffsetFilter)
|
|
1023
|
-
schema_type: Optional model type for automatic schema conversion
|
|
1024
|
-
_connection: Optional connection to use
|
|
1025
|
-
_config: Optional SQL configuration
|
|
1026
|
-
**kwargs: Additional driver-specific arguments. Can include 'limit' and 'offset'
|
|
1027
|
-
if LimitOffsetFilter is not provided
|
|
1028
|
-
|
|
1029
|
-
Returns:
|
|
1030
|
-
OffsetPagination object containing items, limit, offset, and total count
|
|
1031
|
-
|
|
1032
|
-
Raises:
|
|
1033
|
-
ValueError: If neither LimitOffsetFilter nor limit/offset kwargs are provided
|
|
1034
|
-
|
|
1035
|
-
Example:
|
|
1036
|
-
>>> # Basic pagination
|
|
1037
|
-
>>> from sqlspec.statement.filters import LimitOffsetFilter
|
|
1038
|
-
>>> result = await service.paginate(
|
|
1039
|
-
... sql.select("*").from_("users"),
|
|
1040
|
-
... LimitOffsetFilter(limit=10, offset=20),
|
|
1041
|
-
... )
|
|
1042
|
-
>>> print(
|
|
1043
|
-
... f"Showing {len(result.items)} of {result.total} users"
|
|
1044
|
-
... )
|
|
1045
|
-
|
|
1046
|
-
>>> # With schema conversion
|
|
1047
|
-
>>> result = await service.paginate(
|
|
1048
|
-
... sql.select("*").from_("users"),
|
|
1049
|
-
... LimitOffsetFilter(limit=10, offset=0),
|
|
1050
|
-
... schema_type=User,
|
|
1051
|
-
... )
|
|
1052
|
-
>>> # result.items is list[User] with proper type inference
|
|
1053
|
-
|
|
1054
|
-
>>> # With multiple filters
|
|
1055
|
-
>>> from sqlspec.statement.filters import (
|
|
1056
|
-
... LimitOffsetFilter,
|
|
1057
|
-
... OrderByFilter,
|
|
1058
|
-
... )
|
|
1059
|
-
>>> result = await service.paginate(
|
|
1060
|
-
... sql.select("*").from_("users"),
|
|
1061
|
-
... OrderByFilter("created_at", "desc"),
|
|
1062
|
-
... LimitOffsetFilter(limit=20, offset=40),
|
|
1063
|
-
... schema_type=User,
|
|
1064
|
-
... )
|
|
1065
|
-
"""
|
|
1066
|
-
from sqlspec.service.pagination import OffsetPagination
|
|
1067
|
-
from sqlspec.statement.sql import SQL
|
|
1068
|
-
|
|
735
|
+
) -> "Union[OffsetPagination[RowT], OffsetPagination[ModelDTOT]]":
|
|
1069
736
|
# Separate filters from parameters
|
|
1070
737
|
filters: list[StatementFilter] = []
|
|
1071
738
|
params: list[Any] = []
|
|
@@ -1096,36 +763,34 @@ class SQLSpecAsyncService(Generic[AsyncDriverT, ConnectionT]):
|
|
|
1096
763
|
msg = "Pagination requires either a LimitOffsetFilter in parameters or 'limit' and 'offset' in kwargs."
|
|
1097
764
|
raise ValueError(msg)
|
|
1098
765
|
|
|
1099
|
-
base_stmt = self.
|
|
766
|
+
base_stmt = self._transform_to_sql(statement, params, _config) # type: ignore[arg-type]
|
|
1100
767
|
|
|
1101
768
|
filtered_stmt = base_stmt
|
|
1102
769
|
for filter_obj in other_filters:
|
|
1103
770
|
filtered_stmt = filter_obj.append_to_statement(filtered_stmt)
|
|
1104
|
-
|
|
1105
|
-
sql_str = filtered_stmt.to_sql()
|
|
1106
|
-
|
|
1107
|
-
# Parse and transform the AST to create a count query
|
|
1108
|
-
parsed = parse_one(sql_str)
|
|
771
|
+
parsed = parse_one(filtered_stmt.to_sql())
|
|
1109
772
|
|
|
1110
773
|
# Using exp.Subquery to properly wrap the parsed expression
|
|
1111
774
|
subquery = exp.Subquery(this=parsed, alias="_count_subquery")
|
|
1112
775
|
count_ast = exp.Select().select(exp.func("COUNT", exp.Star()).as_("total")).from_(subquery)
|
|
1113
776
|
|
|
1114
|
-
|
|
777
|
+
# Preserve parameters from the original statement
|
|
778
|
+
count_stmt = SQL(count_ast, *filtered_stmt.parameters, _config=_config)
|
|
1115
779
|
|
|
1116
780
|
# Execute count query
|
|
1117
781
|
total = await self.select_value(count_stmt, _connection=_connection, _config=_config, **kwargs)
|
|
1118
782
|
|
|
1119
|
-
data_stmt = self.
|
|
783
|
+
data_stmt = self._transform_to_sql(statement, params, _config) # type: ignore[arg-type]
|
|
1120
784
|
|
|
1121
785
|
for filter_obj in other_filters:
|
|
1122
786
|
data_stmt = filter_obj.append_to_statement(data_stmt)
|
|
1123
787
|
|
|
1124
|
-
|
|
788
|
+
limit_offset = LimitOffsetFilter(limit=limit, offset=offset)
|
|
789
|
+
data_stmt = limit_offset.append_to_statement(data_stmt)
|
|
1125
790
|
|
|
1126
791
|
# Execute data query
|
|
1127
792
|
items = await self.select(
|
|
1128
|
-
data_stmt, schema_type=schema_type, _connection=_connection, _config=_config, **kwargs
|
|
793
|
+
data_stmt, *params, schema_type=schema_type, _connection=_connection, _config=_config, **kwargs
|
|
1129
794
|
)
|
|
1130
795
|
|
|
1131
|
-
return OffsetPagination(items=items, limit=limit, offset=offset, total=total)
|
|
796
|
+
return OffsetPagination(items=items, limit=limit, offset=offset, total=total) # pyright: ignore
|