sqlspec 0.56.1__py3-none-any.whl → 0.56.2__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.
- sqlspec/adapters/adbc/core.py +2 -1
- sqlspec/adapters/adbc/driver.py +11 -3
- sqlspec/adapters/bigquery/driver.py +21 -2
- sqlspec/adapters/duckdb/core.py +2 -2
- sqlspec/adapters/oracledb/_json_handlers.py +10 -7
- sqlspec/adapters/oracledb/_param_types.py +3 -2
- sqlspec/adapters/oracledb/_uuid_handlers.py +2 -1
- sqlspec/adapters/oracledb/adk/store.py +2 -2
- sqlspec/adapters/oracledb/config.py +10 -28
- sqlspec/adapters/oracledb/core.py +390 -57
- sqlspec/adapters/oracledb/data_dictionary.py +21 -0
- sqlspec/adapters/oracledb/driver.py +22 -4
- sqlspec/adapters/psqlpy/core.py +2 -1
- sqlspec/adapters/spanner/type_converter.py +15 -4
- sqlspec/core/type_converter.py +3 -1
- sqlspec/utils/arrow_helpers.py +50 -3
- sqlspec/utils/schema.py +3 -2
- sqlspec/utils/type_converters.py +2 -1
- sqlspec/utils/uuids.py +50 -3
- {sqlspec-0.56.1.dist-info → sqlspec-0.56.2.dist-info}/METADATA +1 -1
- {sqlspec-0.56.1.dist-info → sqlspec-0.56.2.dist-info}/RECORD +24 -24
- {sqlspec-0.56.1.dist-info → sqlspec-0.56.2.dist-info}/WHEEL +0 -0
- {sqlspec-0.56.1.dist-info → sqlspec-0.56.2.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.56.1.dist-info → sqlspec-0.56.2.dist-info}/licenses/LICENSE +0 -0
sqlspec/adapters/adbc/core.py
CHANGED
|
@@ -46,6 +46,7 @@ from sqlspec.utils.module_loader import import_string
|
|
|
46
46
|
from sqlspec.utils.serializers import to_json
|
|
47
47
|
from sqlspec.utils.type_converters import build_uuid_coercions
|
|
48
48
|
from sqlspec.utils.type_guards import has_rowcount, has_sqlstate
|
|
49
|
+
from sqlspec.utils.uuids import uuid_from_string
|
|
49
50
|
|
|
50
51
|
if TYPE_CHECKING:
|
|
51
52
|
from collections.abc import Callable, Mapping
|
|
@@ -1068,7 +1069,7 @@ def _convert_uuid_value(value: Any, ordinal: int, row_number: int, element_index
|
|
|
1068
1069
|
if not isinstance(value, str):
|
|
1069
1070
|
raise SQLSpecError(_uuid_binding_error(ordinal, value, row_number, element_index))
|
|
1070
1071
|
try:
|
|
1071
|
-
return str(
|
|
1072
|
+
return str(uuid_from_string(value))
|
|
1072
1073
|
except (TypeError, ValueError) as exc:
|
|
1073
1074
|
raise SQLSpecError(_uuid_binding_error(ordinal, value, row_number, element_index)) from exc
|
|
1074
1075
|
|
sqlspec/adapters/adbc/driver.py
CHANGED
|
@@ -40,7 +40,7 @@ from sqlspec.core import (
|
|
|
40
40
|
)
|
|
41
41
|
from sqlspec.driver import BaseSyncExceptionHandler, SyncDriverAdapterBase, SyncRowStream
|
|
42
42
|
from sqlspec.exceptions import DatabaseConnectionError, SQLSpecError
|
|
43
|
-
from sqlspec.utils.arrow_helpers import arrow_reader_with_deferred_close
|
|
43
|
+
from sqlspec.utils.arrow_helpers import arrow_reader_with_deferred_close, arrow_table_to_pylist
|
|
44
44
|
from sqlspec.utils.logging import get_logger
|
|
45
45
|
from sqlspec.utils.module_loader import ensure_pyarrow
|
|
46
46
|
from sqlspec.utils.serializers import to_json
|
|
@@ -129,7 +129,12 @@ class AdbcSelectStreamSource:
|
|
|
129
129
|
batch = next(reader)
|
|
130
130
|
except StopIteration:
|
|
131
131
|
return []
|
|
132
|
-
rows =
|
|
132
|
+
rows = arrow_table_to_pylist(
|
|
133
|
+
batch,
|
|
134
|
+
decode_arrow_extension_types=bool(
|
|
135
|
+
self._driver.driver_features.get("enable_arrow_extension_types", True)
|
|
136
|
+
),
|
|
137
|
+
)
|
|
133
138
|
if rows:
|
|
134
139
|
return rows
|
|
135
140
|
|
|
@@ -224,7 +229,10 @@ class AdbcDriver(SyncDriverAdapterBase):
|
|
|
224
229
|
|
|
225
230
|
if is_select_like:
|
|
226
231
|
arrow_table = cursor.fetch_arrow_table()
|
|
227
|
-
data =
|
|
232
|
+
data = arrow_table_to_pylist(
|
|
233
|
+
arrow_table,
|
|
234
|
+
decode_arrow_extension_types=bool(self.driver_features.get("enable_arrow_extension_types", True)),
|
|
235
|
+
)
|
|
228
236
|
column_names = list(arrow_table.column_names)
|
|
229
237
|
return self.create_execution_result(
|
|
230
238
|
cursor,
|
|
@@ -772,14 +772,33 @@ def _bigquery_arrow_reader_from_iterable(batches: "Iterable[ArrowRecordBatch]")
|
|
|
772
772
|
def _records_to_json_rows(
|
|
773
773
|
records: "Sequence[Mapping[str, Any]] | Sequence[Sequence[Any]]", columns: "list[str] | None"
|
|
774
774
|
) -> "list[dict[str, Any]]":
|
|
775
|
-
materialized = list(records)
|
|
775
|
+
materialized = records if isinstance(records, list) else list(records)
|
|
776
776
|
if not materialized:
|
|
777
777
|
msg = "load_from_records requires at least one record."
|
|
778
778
|
raise ImproperConfigurationError(msg)
|
|
779
779
|
|
|
780
780
|
first = materialized[0]
|
|
781
781
|
if isinstance(first, Mapping):
|
|
782
|
-
|
|
782
|
+
if columns is None:
|
|
783
|
+
resolved = list(first.keys())
|
|
784
|
+
expected = set(resolved)
|
|
785
|
+
can_reuse = True
|
|
786
|
+
for record in materialized:
|
|
787
|
+
if not isinstance(record, Mapping):
|
|
788
|
+
msg = "load_from_records mapping records must all be mappings."
|
|
789
|
+
raise ImproperConfigurationError(msg)
|
|
790
|
+
record_keys = list(record.keys())
|
|
791
|
+
if set(record_keys) != expected:
|
|
792
|
+
msg = "load_from_records mapping records must all share the same keys."
|
|
793
|
+
raise ImproperConfigurationError(msg)
|
|
794
|
+
if type(record) is not dict or record_keys != resolved:
|
|
795
|
+
can_reuse = False
|
|
796
|
+
validated_records = cast("list[Mapping[str, Any]]", materialized)
|
|
797
|
+
if can_reuse:
|
|
798
|
+
return cast("list[dict[str, Any]]", validated_records)
|
|
799
|
+
return [{column: record[column] for column in resolved} for record in validated_records]
|
|
800
|
+
|
|
801
|
+
resolved = columns
|
|
783
802
|
expected = set(resolved)
|
|
784
803
|
rows: list[dict[str, Any]] = []
|
|
785
804
|
for record in materialized:
|
sqlspec/adapters/duckdb/core.py
CHANGED
|
@@ -4,7 +4,6 @@ import contextlib
|
|
|
4
4
|
from datetime import date, datetime
|
|
5
5
|
from decimal import Decimal
|
|
6
6
|
from typing import TYPE_CHECKING, Any, Final, cast
|
|
7
|
-
from uuid import UUID
|
|
8
7
|
|
|
9
8
|
from sqlspec.core import DriverParameterProfile, ParameterStyle, StatementConfig, build_statement_config_from_profile
|
|
10
9
|
from sqlspec.exceptions import (
|
|
@@ -24,6 +23,7 @@ from sqlspec.exceptions import (
|
|
|
24
23
|
from sqlspec.utils.serializers import to_json
|
|
25
24
|
from sqlspec.utils.type_converters import build_decimal_converter, build_uuid_coercions, time_iso_convert
|
|
26
25
|
from sqlspec.utils.type_guards import has_rowcount
|
|
26
|
+
from sqlspec.utils.uuids import uuid_from_string
|
|
27
27
|
|
|
28
28
|
if TYPE_CHECKING:
|
|
29
29
|
from collections.abc import Callable, Mapping
|
|
@@ -395,7 +395,7 @@ def _restore_uuid_columns(rows: "list[dict[str, Any]]", description: "list[Any]
|
|
|
395
395
|
for column in uuid_columns:
|
|
396
396
|
value = row.get(column)
|
|
397
397
|
if isinstance(value, str):
|
|
398
|
-
row[column] =
|
|
398
|
+
row[column] = uuid_from_string(value)
|
|
399
399
|
|
|
400
400
|
|
|
401
401
|
driver_profile = build_profile()
|
|
@@ -6,8 +6,9 @@ and Oracle's JSON storage types via connection type handlers.
|
|
|
6
6
|
Routing matrix (input):
|
|
7
7
|
|
|
8
8
|
* Oracle 21c+ native ``JSON``: bind via ``DB_TYPE_JSON`` (binary OSON).
|
|
9
|
-
* Oracle 12c-20c with ``BLOB CHECK (... IS JSON)``:
|
|
10
|
-
|
|
9
|
+
* Oracle 12c-20c with ``BLOB CHECK (... IS JSON)``: the driver pre-serializes
|
|
10
|
+
JSON-shaped values and creates a ``DB_TYPE_BLOB`` locator containing UTF-8
|
|
11
|
+
JSON before input-handler dispatch.
|
|
11
12
|
* Oracle 11g and earlier: bind via ``DB_TYPE_CLOB`` with serialized JSON string.
|
|
12
13
|
* The rung is chosen by ``resolve_oracle_json_storage``, the single JSON-threshold
|
|
13
14
|
source shared with ``OracleVersionInfo`` and the extension stores.
|
|
@@ -33,11 +34,12 @@ handler returns ``None`` for values it does not own.
|
|
|
33
34
|
"""
|
|
34
35
|
|
|
35
36
|
from functools import partial
|
|
36
|
-
from typing import TYPE_CHECKING, Any
|
|
37
|
+
from typing import TYPE_CHECKING, Any
|
|
37
38
|
|
|
38
39
|
from oracledb import DB_TYPE_CHAR, DB_TYPE_NCHAR, DB_TYPE_NVARCHAR, DB_TYPE_VARCHAR
|
|
39
40
|
|
|
40
41
|
from sqlspec.adapters.oracledb._typing import DB_TYPE_BLOB, DB_TYPE_CLOB, DB_TYPE_JSON, DB_TYPE_LONG, DB_TYPE_LONG_RAW
|
|
42
|
+
from sqlspec.adapters.oracledb.data_dictionary import resolve_oracle_connection_major
|
|
41
43
|
from sqlspec.data_dictionary.dialects.oracle import (
|
|
42
44
|
ORACLE_JSON_STORAGE_BLOB_JSON,
|
|
43
45
|
ORACLE_JSON_STORAGE_NATIVE,
|
|
@@ -51,6 +53,7 @@ if TYPE_CHECKING:
|
|
|
51
53
|
__all__ = (
|
|
52
54
|
"chain_input_handler",
|
|
53
55
|
"chain_output_handler",
|
|
56
|
+
"is_json_payload",
|
|
54
57
|
"json_converter_in_blob",
|
|
55
58
|
"json_converter_in_clob",
|
|
56
59
|
"json_converter_out_blob",
|
|
@@ -131,7 +134,7 @@ def chain_input_handler(inner: Any, fallback: "Any | None") -> Any:
|
|
|
131
134
|
instance: python-oracledb selects its calling convention via
|
|
132
135
|
``inspect.signature(handler)``, which succeeds for a partial of a (compiled)
|
|
133
136
|
function but raises ``ValueError`` for a compiled ``__call__`` object -- the
|
|
134
|
-
latter forces the
|
|
137
|
+
latter forces the older 6-argument call and breaks fetches.
|
|
135
138
|
"""
|
|
136
139
|
return partial(_chained_input_handler, inner, fallback)
|
|
137
140
|
|
|
@@ -144,7 +147,7 @@ def chain_output_handler(inner: Any, fallback: "Any | None") -> Any:
|
|
|
144
147
|
return partial(_chained_output_handler, inner, fallback)
|
|
145
148
|
|
|
146
149
|
|
|
147
|
-
def
|
|
150
|
+
def is_json_payload(value: Any) -> bool:
|
|
148
151
|
"""Return True if the value should be claimed by the JSON input handler.
|
|
149
152
|
|
|
150
153
|
``dict`` and ``tuple``/``list`` of dicts are claimed. Sequences whose first
|
|
@@ -167,10 +170,10 @@ def _is_json_payload(value: Any) -> bool:
|
|
|
167
170
|
|
|
168
171
|
def _input_type_handler(cursor: "Cursor | AsyncCursor", value: Any, arraysize: int) -> Any:
|
|
169
172
|
"""Oracle input type handler for JSON-shaped Python values."""
|
|
170
|
-
if not
|
|
173
|
+
if not is_json_payload(value):
|
|
171
174
|
return None
|
|
172
175
|
|
|
173
|
-
server_major =
|
|
176
|
+
server_major = resolve_oracle_connection_major(cursor.connection)
|
|
174
177
|
|
|
175
178
|
if server_major is None:
|
|
176
179
|
return cursor.var(DB_TYPE_JSON, arraysize=arraysize)
|
|
@@ -8,7 +8,8 @@ Wrap a parameter value to express explicit intent:
|
|
|
8
8
|
|
|
9
9
|
* :class:`OracleClob` — bind as ``DB_TYPE_CLOB`` regardless of length.
|
|
10
10
|
* :class:`OracleBlob` — bind as ``DB_TYPE_BLOB`` regardless of length.
|
|
11
|
-
* :class:`OracleJson` —
|
|
11
|
+
* :class:`OracleJson` — express JSON intent; the input handler selects the
|
|
12
|
+
storage type supported by the connected Oracle version.
|
|
12
13
|
|
|
13
14
|
The wrappers themselves perform no validation — type discipline lives at the
|
|
14
15
|
routing site so error messages can include database-context detail.
|
|
@@ -38,7 +39,7 @@ class OracleBlob:
|
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
class OracleJson:
|
|
41
|
-
"""Mark a value
|
|
42
|
+
"""Mark a value for version-aware Oracle JSON binding."""
|
|
42
43
|
|
|
43
44
|
__slots__ = ("value",)
|
|
44
45
|
|
|
@@ -10,6 +10,7 @@ from typing import TYPE_CHECKING, Any
|
|
|
10
10
|
from sqlspec.adapters.oracledb._json_handlers import chain_input_handler, chain_output_handler
|
|
11
11
|
from sqlspec.adapters.oracledb._typing import DB_TYPE_RAW
|
|
12
12
|
from sqlspec.utils.logging import get_logger
|
|
13
|
+
from sqlspec.utils.uuids import uuid_from_bytes
|
|
13
14
|
|
|
14
15
|
if TYPE_CHECKING:
|
|
15
16
|
from oracledb import AsyncConnection, AsyncCursor, Connection, Cursor
|
|
@@ -59,7 +60,7 @@ def uuid_converter_out(value: bytes | None) -> "uuid.UUID | bytes | None":
|
|
|
59
60
|
return value
|
|
60
61
|
|
|
61
62
|
try:
|
|
62
|
-
return
|
|
63
|
+
return uuid_from_bytes(value)
|
|
63
64
|
except (ValueError, TypeError):
|
|
64
65
|
logger.debug("RAW(16) value is not valid UUID format, returning as bytes", extra={"value_length": len(value)})
|
|
65
66
|
return value
|
|
@@ -296,7 +296,7 @@ class OracleAsyncADKStore(BaseAsyncADKStore["OracleAsyncConfig"]):
|
|
|
296
296
|
config: OracleAsyncConfig with extension_config["adk"] settings.
|
|
297
297
|
|
|
298
298
|
Notes:
|
|
299
|
-
- JSON storage type detected
|
|
299
|
+
- JSON storage type detected by capability (native JSON, BLOB IS JSON, or plain LOB)
|
|
300
300
|
- event_data stored as JSON (21c+) or BLOB (older versions)
|
|
301
301
|
- TIMESTAMP WITH TIME ZONE for timezone-aware timestamps
|
|
302
302
|
- Named parameters using :param_name
|
|
@@ -1265,7 +1265,7 @@ class OracleSyncADKStore(BaseSyncADKStore["OracleSyncConfig"]):
|
|
|
1265
1265
|
config: OracleSyncConfig with extension_config["adk"] settings.
|
|
1266
1266
|
|
|
1267
1267
|
Notes:
|
|
1268
|
-
- JSON storage type detected
|
|
1268
|
+
- JSON storage type detected by capability (native JSON, BLOB IS JSON, or plain LOB)
|
|
1269
1269
|
- event_data stored as JSON (21c+) or BLOB (older versions)
|
|
1270
1270
|
- TIMESTAMP WITH TIME ZONE for timezone-aware timestamps
|
|
1271
1271
|
- Named parameters using :param_name
|
|
@@ -23,7 +23,7 @@ from sqlspec.adapters.oracledb._typing import (
|
|
|
23
23
|
from sqlspec.adapters.oracledb._uuid_handlers import register_uuid_handlers
|
|
24
24
|
from sqlspec.adapters.oracledb._vector_handlers import register_numpy_handlers # pyright: ignore[reportPrivateUsage]
|
|
25
25
|
from sqlspec.adapters.oracledb.core import apply_driver_features, default_statement_config
|
|
26
|
-
from sqlspec.adapters.oracledb.data_dictionary import OracleVersionCache
|
|
26
|
+
from sqlspec.adapters.oracledb.data_dictionary import OracleVersionCache, resolve_oracle_connection_major
|
|
27
27
|
from sqlspec.adapters.oracledb.driver import (
|
|
28
28
|
OracleAsyncDriver,
|
|
29
29
|
OracleAsyncExceptionHandler,
|
|
@@ -32,7 +32,6 @@ from sqlspec.adapters.oracledb.driver import (
|
|
|
32
32
|
)
|
|
33
33
|
from sqlspec.adapters.oracledb.migrations import OracleAsyncMigrationTracker, OracleSyncMigrationTracker
|
|
34
34
|
from sqlspec.config import AsyncDatabaseConfig, ExtensionConfigs, SyncDatabaseConfig
|
|
35
|
-
from sqlspec.data_dictionary.dialects.oracle import parse_oracle_version_components
|
|
36
35
|
from sqlspec.driver._async import AsyncPoolConnectionContext, AsyncPoolSessionFactory
|
|
37
36
|
from sqlspec.driver._sync import SyncPoolConnectionContext, SyncPoolSessionFactory
|
|
38
37
|
from sqlspec.extensions.events import EventRuntimeHints
|
|
@@ -413,9 +412,9 @@ class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConne
|
|
|
413
412
|
vector case) internally. UUID registration remains gated for
|
|
414
413
|
backwards compatibility with existing user configurations.
|
|
415
414
|
|
|
416
|
-
Caches ``connection._sqlspec_oracle_major`` so
|
|
417
|
-
can pick the right binding path (``DB_TYPE_JSON`` on 21c+,
|
|
418
|
-
``
|
|
415
|
+
Caches ``connection._sqlspec_oracle_major`` so JSON parameter coercion
|
|
416
|
+
can pick the right binding path (``DB_TYPE_JSON`` on 21c+, a textual
|
|
417
|
+
``BLOB IS JSON`` locator on 12c-20c, and ``DB_TYPE_CLOB`` before 12c)
|
|
419
418
|
without re-querying server metadata on every bind. Caches
|
|
420
419
|
``connection._sqlspec_vector_return_format`` so the vector output
|
|
421
420
|
handler can dispatch to ``numpy`` / ``list`` / ``array`` without
|
|
@@ -434,7 +433,9 @@ class OracleSyncConfig(SyncDatabaseConfig[OracleSyncConnection, "OracleSyncConne
|
|
|
434
433
|
|
|
435
434
|
# Stash detected major version on the connection so the JSON input handler
|
|
436
435
|
# can pick the right binding path without per-bind metadata queries.
|
|
437
|
-
setattr(
|
|
436
|
+
setattr(
|
|
437
|
+
connection, "_sqlspec_oracle_major", resolve_oracle_connection_major(connection, self._oracle_version_cache)
|
|
438
|
+
)
|
|
438
439
|
# Stash the vector-read format so the VECTOR output handler can
|
|
439
440
|
# dispatch without re-reading driver-feature defaults on every fetch.
|
|
440
441
|
setattr(connection, "_sqlspec_vector_return_format", self.driver_features.get("vector_return_format"))
|
|
@@ -628,7 +629,9 @@ class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncC
|
|
|
628
629
|
|
|
629
630
|
# Stash detected major version on the connection so the JSON input handler
|
|
630
631
|
# can pick the right binding path without per-bind metadata queries.
|
|
631
|
-
setattr(
|
|
632
|
+
setattr(
|
|
633
|
+
connection, "_sqlspec_oracle_major", resolve_oracle_connection_major(connection, self._oracle_version_cache)
|
|
634
|
+
)
|
|
632
635
|
# Stash the vector-read format so the VECTOR output handler can
|
|
633
636
|
# dispatch without re-reading driver-feature defaults on every fetch.
|
|
634
637
|
setattr(connection, "_sqlspec_vector_return_format", self.driver_features.get("vector_return_format"))
|
|
@@ -655,24 +658,3 @@ class OracleAsyncConfig(AsyncDatabaseConfig[OracleAsyncConnection, "OracleAsyncC
|
|
|
655
658
|
await self.connection_instance.close()
|
|
656
659
|
self.connection_instance = None
|
|
657
660
|
self._oracle_version_cache.reset()
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
def _resolve_connection_major(cache: "OracleVersionCache", connection: Any) -> "int | None":
|
|
661
|
-
"""Resolve the Oracle server major for connection-setup type handlers.
|
|
662
|
-
|
|
663
|
-
Prefers the pool-scoped version cache once a driver has resolved it through
|
|
664
|
-
the data-dictionary path. Before that, the major is parsed from
|
|
665
|
-
``connection.version`` — a connection attribute populated at connect time, so
|
|
666
|
-
no query is issued — using the same version parser the data dictionary uses.
|
|
667
|
-
Returns ``None`` when unavailable; callers treat ``None`` as "assume 21c+".
|
|
668
|
-
"""
|
|
669
|
-
if cache.resolved and cache.version is not None:
|
|
670
|
-
return cache.version.major
|
|
671
|
-
try:
|
|
672
|
-
version_str = connection.version
|
|
673
|
-
except AttributeError:
|
|
674
|
-
return None
|
|
675
|
-
if not version_str:
|
|
676
|
-
return None
|
|
677
|
-
components = parse_oracle_version_components(str(version_str))
|
|
678
|
-
return components[0] if components is not None else None
|