sqlspec 0.26.0__py3-none-any.whl → 0.28.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 +155 -52
- sqlspec/adapters/adbc/_types.py +1 -1
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +880 -0
- sqlspec/adapters/adbc/config.py +62 -12
- sqlspec/adapters/adbc/data_dictionary.py +74 -2
- sqlspec/adapters/adbc/driver.py +226 -58
- 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 +536 -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 +503 -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 +460 -0
- sqlspec/adapters/asyncpg/config.py +57 -36
- sqlspec/adapters/asyncpg/data_dictionary.py +48 -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 +585 -0
- sqlspec/adapters/bigquery/config.py +36 -11
- sqlspec/adapters/bigquery/data_dictionary.py +42 -2
- sqlspec/adapters/bigquery/driver.py +489 -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 +563 -0
- sqlspec/adapters/duckdb/config.py +79 -21
- sqlspec/adapters/duckdb/data_dictionary.py +41 -2
- sqlspec/adapters/duckdb/driver.py +225 -44
- 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 +1628 -0
- sqlspec/adapters/oracledb/config.py +120 -36
- sqlspec/adapters/oracledb/data_dictionary.py +87 -20
- sqlspec/adapters/oracledb/driver.py +475 -86
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +765 -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 +483 -0
- sqlspec/adapters/psqlpy/config.py +45 -19
- sqlspec/adapters/psqlpy/data_dictionary.py +48 -2
- sqlspec/adapters/psqlpy/driver.py +108 -41
- 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 +962 -0
- sqlspec/adapters/psycopg/config.py +65 -37
- sqlspec/adapters/psycopg/data_dictionary.py +91 -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 +582 -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 +331 -62
- sqlspec/core/__init__.py +5 -4
- sqlspec/core/cache.py +18 -18
- sqlspec/core/compiler.py +6 -8
- sqlspec/core/filters.py +55 -47
- sqlspec/core/hashing.py +9 -9
- sqlspec/core/parameters.py +76 -45
- sqlspec/core/result.py +234 -47
- 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 +183 -160
- sqlspec/driver/_common.py +197 -109
- sqlspec/driver/_sync.py +189 -161
- 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 +69 -61
- sqlspec/extensions/fastapi/__init__.py +21 -0
- sqlspec/extensions/fastapi/extension.py +331 -0
- sqlspec/extensions/fastapi/providers.py +543 -0
- sqlspec/extensions/flask/__init__.py +36 -0
- sqlspec/extensions/flask/_state.py +71 -0
- sqlspec/extensions/flask/_utils.py +40 -0
- sqlspec/extensions/flask/extension.py +389 -0
- sqlspec/extensions/litestar/__init__.py +21 -4
- sqlspec/extensions/litestar/cli.py +54 -10
- sqlspec/extensions/litestar/config.py +56 -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 +349 -224
- sqlspec/extensions/litestar/providers.py +25 -25
- sqlspec/extensions/litestar/store.py +265 -0
- sqlspec/extensions/starlette/__init__.py +10 -0
- sqlspec/extensions/starlette/_state.py +25 -0
- sqlspec/extensions/starlette/_utils.py +52 -0
- sqlspec/extensions/starlette/extension.py +254 -0
- sqlspec/extensions/starlette/middleware.py +154 -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 +106 -36
- sqlspec/storage/_utils.py +85 -0
- sqlspec/storage/backends/fsspec.py +133 -107
- sqlspec/storage/backends/local.py +78 -51
- sqlspec/storage/backends/obstore.py +276 -168
- sqlspec/storage/registry.py +75 -39
- sqlspec/typing.py +30 -84
- sqlspec/utils/__init__.py +25 -4
- sqlspec/utils/arrow_helpers.py +81 -0
- 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 +205 -5
- sqlspec/utils/portal.py +311 -0
- sqlspec/utils/schema.py +288 -0
- sqlspec/utils/serializers.py +113 -4
- sqlspec/utils/sync_tools.py +36 -22
- sqlspec/utils/text.py +1 -2
- sqlspec/utils/type_guards.py +136 -20
- sqlspec/utils/version.py +433 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/METADATA +41 -22
- sqlspec-0.28.0.dist-info/RECORD +221 -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.28.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/licenses/LICENSE +0 -0
sqlspec/driver/_async.py
CHANGED
|
@@ -1,14 +1,22 @@
|
|
|
1
1
|
"""Asynchronous driver protocol implementation."""
|
|
2
2
|
|
|
3
3
|
from abc import abstractmethod
|
|
4
|
-
from typing import TYPE_CHECKING, Any, Final,
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Final, TypeVar, overload
|
|
5
5
|
|
|
6
6
|
from sqlspec.core import SQL, Statement
|
|
7
|
-
from sqlspec.
|
|
8
|
-
from sqlspec.driver.
|
|
9
|
-
|
|
7
|
+
from sqlspec.core.result import create_arrow_result
|
|
8
|
+
from sqlspec.driver._common import (
|
|
9
|
+
CommonDriverAttributesMixin,
|
|
10
|
+
DataDictionaryMixin,
|
|
11
|
+
ExecutionResult,
|
|
12
|
+
VersionInfo,
|
|
13
|
+
handle_single_row_error,
|
|
14
|
+
)
|
|
15
|
+
from sqlspec.driver.mixins import SQLTranslatorMixin
|
|
16
|
+
from sqlspec.exceptions import ImproperConfigurationError
|
|
17
|
+
from sqlspec.utils.arrow_helpers import convert_dict_to_arrow
|
|
10
18
|
from sqlspec.utils.logging import get_logger
|
|
11
|
-
from sqlspec.utils.
|
|
19
|
+
from sqlspec.utils.module_loader import ensure_pyarrow
|
|
12
20
|
|
|
13
21
|
if TYPE_CHECKING:
|
|
14
22
|
from collections.abc import Sequence
|
|
@@ -16,7 +24,7 @@ if TYPE_CHECKING:
|
|
|
16
24
|
|
|
17
25
|
from sqlspec.builder import QueryBuilder
|
|
18
26
|
from sqlspec.core import SQLResult, StatementConfig, StatementFilter
|
|
19
|
-
from sqlspec.typing import
|
|
27
|
+
from sqlspec.typing import SchemaT, StatementParameters
|
|
20
28
|
|
|
21
29
|
_LOGGER_NAME: Final[str] = "sqlspec"
|
|
22
30
|
logger = get_logger(_LOGGER_NAME)
|
|
@@ -29,7 +37,7 @@ EMPTY_FILTERS: Final["list[StatementFilter]"] = []
|
|
|
29
37
|
AsyncDriverT = TypeVar("AsyncDriverT", bound="AsyncDriverAdapterBase")
|
|
30
38
|
|
|
31
39
|
|
|
32
|
-
class AsyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin
|
|
40
|
+
class AsyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin):
|
|
33
41
|
"""Base class for asynchronous database drivers."""
|
|
34
42
|
|
|
35
43
|
__slots__ = ()
|
|
@@ -96,7 +104,7 @@ class AsyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin, To
|
|
|
96
104
|
"""Commit the current transaction on the current connection."""
|
|
97
105
|
|
|
98
106
|
@abstractmethod
|
|
99
|
-
async def _try_special_handling(self, cursor: Any, statement: "SQL") -> "
|
|
107
|
+
async def _try_special_handling(self, cursor: Any, statement: "SQL") -> "SQLResult | None":
|
|
100
108
|
"""Hook for database-specific special operations (e.g., PostgreSQL COPY, bulk operations).
|
|
101
109
|
|
|
102
110
|
This method is called first in dispatch_statement_execution() to allow drivers to handle
|
|
@@ -169,10 +177,10 @@ class AsyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin, To
|
|
|
169
177
|
|
|
170
178
|
async def execute(
|
|
171
179
|
self,
|
|
172
|
-
statement: "
|
|
180
|
+
statement: "SQL | Statement | QueryBuilder",
|
|
173
181
|
/,
|
|
174
|
-
*parameters: "
|
|
175
|
-
statement_config: "
|
|
182
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
183
|
+
statement_config: "StatementConfig | None" = None,
|
|
176
184
|
**kwargs: Any,
|
|
177
185
|
) -> "SQLResult":
|
|
178
186
|
"""Execute a statement with parameter handling."""
|
|
@@ -183,11 +191,11 @@ class AsyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin, To
|
|
|
183
191
|
|
|
184
192
|
async def execute_many(
|
|
185
193
|
self,
|
|
186
|
-
statement: "
|
|
194
|
+
statement: "SQL | Statement | QueryBuilder",
|
|
187
195
|
/,
|
|
188
196
|
parameters: "Sequence[StatementParameters]",
|
|
189
|
-
*filters: "
|
|
190
|
-
statement_config: "
|
|
197
|
+
*filters: "StatementParameters | StatementFilter",
|
|
198
|
+
statement_config: "StatementConfig | None" = None,
|
|
191
199
|
**kwargs: Any,
|
|
192
200
|
) -> "SQLResult":
|
|
193
201
|
"""Execute statement multiple times with different parameters.
|
|
@@ -206,10 +214,10 @@ class AsyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin, To
|
|
|
206
214
|
|
|
207
215
|
async def execute_script(
|
|
208
216
|
self,
|
|
209
|
-
statement: "
|
|
217
|
+
statement: "str | SQL",
|
|
210
218
|
/,
|
|
211
|
-
*parameters: "
|
|
212
|
-
statement_config: "
|
|
219
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
220
|
+
statement_config: "StatementConfig | None" = None,
|
|
213
221
|
**kwargs: Any,
|
|
214
222
|
) -> "SQLResult":
|
|
215
223
|
"""Execute a multi-statement script.
|
|
@@ -225,140 +233,209 @@ class AsyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin, To
|
|
|
225
233
|
@overload
|
|
226
234
|
async def select_one(
|
|
227
235
|
self,
|
|
228
|
-
statement: "
|
|
236
|
+
statement: "Statement | QueryBuilder",
|
|
229
237
|
/,
|
|
230
|
-
*parameters: "
|
|
231
|
-
schema_type: "type[
|
|
232
|
-
statement_config: "
|
|
238
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
239
|
+
schema_type: "type[SchemaT]",
|
|
240
|
+
statement_config: "StatementConfig | None" = None,
|
|
233
241
|
**kwargs: Any,
|
|
234
|
-
) -> "
|
|
242
|
+
) -> "SchemaT": ...
|
|
235
243
|
|
|
236
244
|
@overload
|
|
237
245
|
async def select_one(
|
|
238
246
|
self,
|
|
239
|
-
statement: "
|
|
247
|
+
statement: "Statement | QueryBuilder",
|
|
240
248
|
/,
|
|
241
|
-
*parameters: "
|
|
249
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
242
250
|
schema_type: None = None,
|
|
243
|
-
statement_config: "
|
|
251
|
+
statement_config: "StatementConfig | None" = None,
|
|
244
252
|
**kwargs: Any,
|
|
245
253
|
) -> "dict[str, Any]": ...
|
|
246
254
|
|
|
247
255
|
async def select_one(
|
|
248
256
|
self,
|
|
249
|
-
statement: "
|
|
257
|
+
statement: "Statement | QueryBuilder",
|
|
250
258
|
/,
|
|
251
|
-
*parameters: "
|
|
252
|
-
schema_type: "
|
|
253
|
-
statement_config: "
|
|
259
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
260
|
+
schema_type: "type[SchemaT] | None" = None,
|
|
261
|
+
statement_config: "StatementConfig | None" = None,
|
|
254
262
|
**kwargs: Any,
|
|
255
|
-
) -> "
|
|
263
|
+
) -> "SchemaT | dict[str, Any]":
|
|
256
264
|
"""Execute a select statement and return exactly one row.
|
|
257
265
|
|
|
258
266
|
Raises an exception if no rows or more than one row is returned.
|
|
259
267
|
"""
|
|
260
268
|
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
if data_len > 1:
|
|
266
|
-
self._raise_expected_one_row(data_len)
|
|
267
|
-
first_row = data[0]
|
|
268
|
-
return self.to_schema(first_row, schema_type=schema_type) if schema_type else first_row
|
|
269
|
+
try:
|
|
270
|
+
return result.one(schema_type=schema_type)
|
|
271
|
+
except ValueError as error:
|
|
272
|
+
handle_single_row_error(error)
|
|
269
273
|
|
|
270
274
|
@overload
|
|
271
275
|
async def select_one_or_none(
|
|
272
276
|
self,
|
|
273
|
-
statement: "
|
|
277
|
+
statement: "Statement | QueryBuilder",
|
|
274
278
|
/,
|
|
275
|
-
*parameters: "
|
|
276
|
-
schema_type: "type[
|
|
277
|
-
statement_config: "
|
|
279
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
280
|
+
schema_type: "type[SchemaT]",
|
|
281
|
+
statement_config: "StatementConfig | None" = None,
|
|
278
282
|
**kwargs: Any,
|
|
279
|
-
) -> "
|
|
283
|
+
) -> "SchemaT | None": ...
|
|
280
284
|
|
|
281
285
|
@overload
|
|
282
286
|
async def select_one_or_none(
|
|
283
287
|
self,
|
|
284
|
-
statement: "
|
|
288
|
+
statement: "Statement | QueryBuilder",
|
|
285
289
|
/,
|
|
286
|
-
*parameters: "
|
|
290
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
287
291
|
schema_type: None = None,
|
|
288
|
-
statement_config: "
|
|
292
|
+
statement_config: "StatementConfig | None" = None,
|
|
289
293
|
**kwargs: Any,
|
|
290
|
-
) -> "
|
|
294
|
+
) -> "dict[str, Any] | None": ...
|
|
291
295
|
|
|
292
296
|
async def select_one_or_none(
|
|
293
297
|
self,
|
|
294
|
-
statement: "
|
|
298
|
+
statement: "Statement | QueryBuilder",
|
|
295
299
|
/,
|
|
296
|
-
*parameters: "
|
|
297
|
-
schema_type: "
|
|
298
|
-
statement_config: "
|
|
300
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
301
|
+
schema_type: "type[SchemaT] | None" = None,
|
|
302
|
+
statement_config: "StatementConfig | None" = None,
|
|
299
303
|
**kwargs: Any,
|
|
300
|
-
) -> "
|
|
304
|
+
) -> "SchemaT | dict[str, Any] | None":
|
|
301
305
|
"""Execute a select statement and return at most one row.
|
|
302
306
|
|
|
303
307
|
Returns None if no rows are found.
|
|
304
308
|
Raises an exception if more than one row is returned.
|
|
305
309
|
"""
|
|
306
310
|
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
|
|
307
|
-
|
|
308
|
-
data_len: int = len(data)
|
|
309
|
-
if data_len == 0:
|
|
310
|
-
return None
|
|
311
|
-
if data_len > 1:
|
|
312
|
-
self._raise_expected_at_most_one_row(data_len)
|
|
313
|
-
first_row = data[0]
|
|
314
|
-
return cast(
|
|
315
|
-
"Optional[Union[dict[str, Any], ModelDTOT]]",
|
|
316
|
-
self.to_schema(first_row, schema_type=schema_type) if schema_type else first_row,
|
|
317
|
-
)
|
|
311
|
+
return result.one_or_none(schema_type=schema_type)
|
|
318
312
|
|
|
319
313
|
@overload
|
|
320
314
|
async def select(
|
|
321
315
|
self,
|
|
322
|
-
statement: "
|
|
316
|
+
statement: "Statement | QueryBuilder",
|
|
323
317
|
/,
|
|
324
|
-
*parameters: "
|
|
325
|
-
schema_type: "type[
|
|
326
|
-
statement_config: "
|
|
318
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
319
|
+
schema_type: "type[SchemaT]",
|
|
320
|
+
statement_config: "StatementConfig | None" = None,
|
|
327
321
|
**kwargs: Any,
|
|
328
|
-
) -> "list[
|
|
322
|
+
) -> "list[SchemaT]": ...
|
|
329
323
|
|
|
330
324
|
@overload
|
|
331
325
|
async def select(
|
|
332
326
|
self,
|
|
333
|
-
statement: "
|
|
327
|
+
statement: "Statement | QueryBuilder",
|
|
334
328
|
/,
|
|
335
|
-
*parameters: "
|
|
329
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
336
330
|
schema_type: None = None,
|
|
337
|
-
statement_config: "
|
|
331
|
+
statement_config: "StatementConfig | None" = None,
|
|
338
332
|
**kwargs: Any,
|
|
339
333
|
) -> "list[dict[str, Any]]": ...
|
|
340
334
|
|
|
341
335
|
async def select(
|
|
342
336
|
self,
|
|
343
|
-
statement: "
|
|
337
|
+
statement: "Statement | QueryBuilder",
|
|
344
338
|
/,
|
|
345
|
-
*parameters: "
|
|
346
|
-
schema_type: "
|
|
347
|
-
statement_config: "
|
|
339
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
340
|
+
schema_type: "type[SchemaT] | None" = None,
|
|
341
|
+
statement_config: "StatementConfig | None" = None,
|
|
348
342
|
**kwargs: Any,
|
|
349
|
-
) -> "
|
|
343
|
+
) -> "list[SchemaT] | list[dict[str, Any]]":
|
|
350
344
|
"""Execute a select statement and return all rows."""
|
|
351
345
|
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
|
|
352
|
-
return
|
|
353
|
-
|
|
346
|
+
return result.get_data(schema_type=schema_type)
|
|
347
|
+
|
|
348
|
+
async def select_to_arrow(
|
|
349
|
+
self,
|
|
350
|
+
statement: "Statement | QueryBuilder",
|
|
351
|
+
/,
|
|
352
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
353
|
+
statement_config: "StatementConfig | None" = None,
|
|
354
|
+
return_format: str = "table",
|
|
355
|
+
native_only: bool = False,
|
|
356
|
+
batch_size: int | None = None,
|
|
357
|
+
arrow_schema: Any = None,
|
|
358
|
+
**kwargs: Any,
|
|
359
|
+
) -> "Any":
|
|
360
|
+
"""Execute query and return results as Apache Arrow format (async).
|
|
361
|
+
|
|
362
|
+
This base implementation uses the conversion path: execute() → dict → Arrow.
|
|
363
|
+
Adapters with native Arrow support (ADBC, DuckDB, BigQuery) override this
|
|
364
|
+
method to use zero-copy native paths for 5-10x performance improvement.
|
|
365
|
+
|
|
366
|
+
Args:
|
|
367
|
+
statement: SQL query string, Statement, or QueryBuilder
|
|
368
|
+
*parameters: Query parameters (same format as execute()/select())
|
|
369
|
+
statement_config: Optional statement configuration override
|
|
370
|
+
return_format: "table" for pyarrow.Table (default), "reader" for RecordBatchReader,
|
|
371
|
+
"batches" for iterator of RecordBatches
|
|
372
|
+
native_only: If True, raise error if native Arrow unavailable (default: False)
|
|
373
|
+
batch_size: Rows per batch for "batches" format (default: None = all rows)
|
|
374
|
+
arrow_schema: Optional pyarrow.Schema for type casting
|
|
375
|
+
**kwargs: Additional keyword arguments
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
ArrowResult containing pyarrow.Table, RecordBatchReader, or RecordBatches
|
|
379
|
+
|
|
380
|
+
Raises:
|
|
381
|
+
ImproperConfigurationError: If native_only=True and adapter doesn't support native Arrow
|
|
382
|
+
|
|
383
|
+
Examples:
|
|
384
|
+
>>> result = await driver.select_to_arrow(
|
|
385
|
+
... "SELECT * FROM users WHERE age > ?", 18
|
|
386
|
+
... )
|
|
387
|
+
>>> df = result.to_pandas()
|
|
388
|
+
>>> print(df.head())
|
|
389
|
+
|
|
390
|
+
>>> # Force native Arrow path (raises error if unavailable)
|
|
391
|
+
>>> result = await driver.select_to_arrow(
|
|
392
|
+
... "SELECT * FROM users", native_only=True
|
|
393
|
+
... )
|
|
394
|
+
"""
|
|
395
|
+
# Check pyarrow is available
|
|
396
|
+
ensure_pyarrow()
|
|
397
|
+
|
|
398
|
+
# Check if native_only requested but not supported
|
|
399
|
+
if native_only:
|
|
400
|
+
msg = (
|
|
401
|
+
f"Adapter '{self.__class__.__name__}' does not support native Arrow results. "
|
|
402
|
+
f"Use native_only=False to allow conversion path, or switch to an adapter "
|
|
403
|
+
f"with native Arrow support (ADBC, DuckDB, BigQuery)."
|
|
404
|
+
)
|
|
405
|
+
raise ImproperConfigurationError(msg)
|
|
406
|
+
|
|
407
|
+
# Execute query using standard path
|
|
408
|
+
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
|
|
409
|
+
|
|
410
|
+
# Convert dict results to Arrow
|
|
411
|
+
arrow_data = convert_dict_to_arrow(
|
|
412
|
+
result.data,
|
|
413
|
+
return_format=return_format, # type: ignore[arg-type]
|
|
414
|
+
batch_size=batch_size,
|
|
415
|
+
)
|
|
416
|
+
if arrow_schema is not None:
|
|
417
|
+
import pyarrow as pa
|
|
418
|
+
|
|
419
|
+
if not isinstance(arrow_schema, pa.Schema):
|
|
420
|
+
msg = f"arrow_schema must be a pyarrow.Schema, got {type(arrow_schema).__name__}"
|
|
421
|
+
raise TypeError(msg)
|
|
422
|
+
|
|
423
|
+
arrow_data = arrow_data.cast(arrow_schema)
|
|
424
|
+
return create_arrow_result(
|
|
425
|
+
statement=result.statement,
|
|
426
|
+
data=arrow_data,
|
|
427
|
+
rows_affected=result.rows_affected,
|
|
428
|
+
last_inserted_id=result.last_inserted_id,
|
|
429
|
+
execution_time=result.execution_time,
|
|
430
|
+
metadata=result.metadata,
|
|
354
431
|
)
|
|
355
432
|
|
|
356
433
|
async def select_value(
|
|
357
434
|
self,
|
|
358
|
-
statement: "
|
|
435
|
+
statement: "Statement | QueryBuilder",
|
|
359
436
|
/,
|
|
360
|
-
*parameters: "
|
|
361
|
-
statement_config: "
|
|
437
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
438
|
+
statement_config: "StatementConfig | None" = None,
|
|
362
439
|
**kwargs: Any,
|
|
363
440
|
) -> Any:
|
|
364
441
|
"""Execute a select statement and return a single scalar value.
|
|
@@ -368,28 +445,16 @@ class AsyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin, To
|
|
|
368
445
|
"""
|
|
369
446
|
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
|
|
370
447
|
try:
|
|
371
|
-
|
|
372
|
-
except ValueError as
|
|
373
|
-
|
|
374
|
-
if not row:
|
|
375
|
-
self._raise_no_rows_found()
|
|
376
|
-
if is_dict_row(row):
|
|
377
|
-
if not row:
|
|
378
|
-
self._raise_row_no_columns()
|
|
379
|
-
return next(iter(row.values()))
|
|
380
|
-
if is_indexable_row(row):
|
|
381
|
-
if not row:
|
|
382
|
-
self._raise_row_no_columns()
|
|
383
|
-
return row[0]
|
|
384
|
-
self._raise_unexpected_row_type(type(row))
|
|
385
|
-
return None
|
|
448
|
+
return result.scalar()
|
|
449
|
+
except ValueError as error:
|
|
450
|
+
handle_single_row_error(error)
|
|
386
451
|
|
|
387
452
|
async def select_value_or_none(
|
|
388
453
|
self,
|
|
389
|
-
statement: "
|
|
454
|
+
statement: "Statement | QueryBuilder",
|
|
390
455
|
/,
|
|
391
|
-
*parameters: "
|
|
392
|
-
statement_config: "
|
|
456
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
457
|
+
statement_config: "StatementConfig | None" = None,
|
|
393
458
|
**kwargs: Any,
|
|
394
459
|
) -> Any:
|
|
395
460
|
"""Execute a select statement and return a single scalar value or None.
|
|
@@ -399,53 +464,39 @@ class AsyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin, To
|
|
|
399
464
|
Raises an exception if more than one row is returned.
|
|
400
465
|
"""
|
|
401
466
|
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
|
|
402
|
-
|
|
403
|
-
data_len: int = len(data)
|
|
404
|
-
if data_len == 0:
|
|
405
|
-
return None
|
|
406
|
-
if data_len > 1:
|
|
407
|
-
self._raise_expected_at_most_one_row(data_len)
|
|
408
|
-
row = data[0]
|
|
409
|
-
if is_dict_row(row):
|
|
410
|
-
if not row:
|
|
411
|
-
return None
|
|
412
|
-
return next(iter(row.values()))
|
|
413
|
-
if is_indexable_row(row):
|
|
414
|
-
return row[0]
|
|
415
|
-
self._raise_cannot_extract_value_from_row_type(type(row).__name__)
|
|
416
|
-
return None
|
|
467
|
+
return result.scalar_or_none()
|
|
417
468
|
|
|
418
469
|
@overload
|
|
419
470
|
async def select_with_total(
|
|
420
471
|
self,
|
|
421
|
-
statement: "
|
|
472
|
+
statement: "Statement | QueryBuilder",
|
|
422
473
|
/,
|
|
423
|
-
*parameters: "
|
|
424
|
-
schema_type: "type[
|
|
425
|
-
statement_config: "
|
|
474
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
475
|
+
schema_type: "type[SchemaT]",
|
|
476
|
+
statement_config: "StatementConfig | None" = None,
|
|
426
477
|
**kwargs: Any,
|
|
427
|
-
) -> "tuple[list[
|
|
478
|
+
) -> "tuple[list[SchemaT], int]": ...
|
|
428
479
|
|
|
429
480
|
@overload
|
|
430
481
|
async def select_with_total(
|
|
431
482
|
self,
|
|
432
|
-
statement: "
|
|
483
|
+
statement: "Statement | QueryBuilder",
|
|
433
484
|
/,
|
|
434
|
-
*parameters: "
|
|
485
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
435
486
|
schema_type: None = None,
|
|
436
|
-
statement_config: "
|
|
487
|
+
statement_config: "StatementConfig | None" = None,
|
|
437
488
|
**kwargs: Any,
|
|
438
489
|
) -> "tuple[list[dict[str, Any]], int]": ...
|
|
439
490
|
|
|
440
491
|
async def select_with_total(
|
|
441
492
|
self,
|
|
442
|
-
statement: "
|
|
493
|
+
statement: "Statement | QueryBuilder",
|
|
443
494
|
/,
|
|
444
|
-
*parameters: "
|
|
445
|
-
schema_type: "
|
|
446
|
-
statement_config: "
|
|
495
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
496
|
+
schema_type: "type[SchemaT] | None" = None,
|
|
497
|
+
statement_config: "StatementConfig | None" = None,
|
|
447
498
|
**kwargs: Any,
|
|
448
|
-
) -> "tuple[
|
|
499
|
+
) -> "tuple[list[SchemaT] | list[dict[str, Any]], int]":
|
|
449
500
|
"""Execute a select statement and return both the data and total count.
|
|
450
501
|
|
|
451
502
|
This method is designed for pagination scenarios where you need both
|
|
@@ -469,42 +520,14 @@ class AsyncDriverAdapterBase(CommonDriverAttributesMixin, SQLTranslatorMixin, To
|
|
|
469
520
|
count_result = await self.dispatch_statement_execution(self._create_count_query(sql_statement), self.connection)
|
|
470
521
|
select_result = await self.execute(sql_statement)
|
|
471
522
|
|
|
472
|
-
return (
|
|
473
|
-
|
|
474
|
-
def _raise_no_rows_found(self) -> NoReturn:
|
|
475
|
-
msg = "No rows found"
|
|
476
|
-
raise NotFoundError(msg)
|
|
477
|
-
|
|
478
|
-
def _raise_no_rows_found_from_exception(self, e: ValueError) -> NoReturn:
|
|
479
|
-
msg = "No rows found"
|
|
480
|
-
raise NotFoundError(msg) from e
|
|
481
|
-
|
|
482
|
-
def _raise_expected_one_row(self, data_len: int) -> NoReturn:
|
|
483
|
-
msg = f"Expected exactly one row, found {data_len}"
|
|
484
|
-
raise ValueError(msg)
|
|
485
|
-
|
|
486
|
-
def _raise_expected_at_most_one_row(self, data_len: int) -> NoReturn:
|
|
487
|
-
msg = f"Expected at most one row, found {data_len}"
|
|
488
|
-
raise ValueError(msg)
|
|
489
|
-
|
|
490
|
-
def _raise_row_no_columns(self) -> NoReturn:
|
|
491
|
-
msg = "Row has no columns"
|
|
492
|
-
raise ValueError(msg)
|
|
493
|
-
|
|
494
|
-
def _raise_unexpected_row_type(self, row_type: type) -> NoReturn:
|
|
495
|
-
msg = f"Unexpected row type: {row_type}"
|
|
496
|
-
raise ValueError(msg)
|
|
497
|
-
|
|
498
|
-
def _raise_cannot_extract_value_from_row_type(self, type_name: str) -> NoReturn:
|
|
499
|
-
msg = f"Cannot extract value from row type {type_name}"
|
|
500
|
-
raise TypeError(msg)
|
|
523
|
+
return (select_result.get_data(schema_type=schema_type), count_result.scalar())
|
|
501
524
|
|
|
502
525
|
|
|
503
526
|
class AsyncDataDictionaryBase(DataDictionaryMixin):
|
|
504
527
|
"""Base class for asynchronous data dictionary implementations."""
|
|
505
528
|
|
|
506
529
|
@abstractmethod
|
|
507
|
-
async def get_version(self, driver: "AsyncDriverAdapterBase") -> "
|
|
530
|
+
async def get_version(self, driver: "AsyncDriverAdapterBase") -> "VersionInfo | None":
|
|
508
531
|
"""Get database version information.
|
|
509
532
|
|
|
510
533
|
Args:
|
|
@@ -538,7 +561,7 @@ class AsyncDataDictionaryBase(DataDictionaryMixin):
|
|
|
538
561
|
Database-specific type name
|
|
539
562
|
"""
|
|
540
563
|
|
|
541
|
-
async def get_tables(self, driver: "AsyncDriverAdapterBase", schema: "
|
|
564
|
+
async def get_tables(self, driver: "AsyncDriverAdapterBase", schema: "str | None" = None) -> "list[str]":
|
|
542
565
|
"""Get list of tables in schema.
|
|
543
566
|
|
|
544
567
|
Args:
|
|
@@ -552,7 +575,7 @@ class AsyncDataDictionaryBase(DataDictionaryMixin):
|
|
|
552
575
|
return []
|
|
553
576
|
|
|
554
577
|
async def get_columns(
|
|
555
|
-
self, driver: "AsyncDriverAdapterBase", table: str, schema: "
|
|
578
|
+
self, driver: "AsyncDriverAdapterBase", table: str, schema: "str | None" = None
|
|
556
579
|
) -> "list[dict[str, Any]]":
|
|
557
580
|
"""Get column information for a table.
|
|
558
581
|
|
|
@@ -568,7 +591,7 @@ class AsyncDataDictionaryBase(DataDictionaryMixin):
|
|
|
568
591
|
return []
|
|
569
592
|
|
|
570
593
|
async def get_indexes(
|
|
571
|
-
self, driver: "AsyncDriverAdapterBase", table: str, schema: "
|
|
594
|
+
self, driver: "AsyncDriverAdapterBase", table: str, schema: "str | None" = None
|
|
572
595
|
) -> "list[dict[str, Any]]":
|
|
573
596
|
"""Get index information for a table.
|
|
574
597
|
|