sqlspec 0.25.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 +256 -24
- sqlspec/_typing.py +71 -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 +69 -12
- sqlspec/adapters/adbc/data_dictionary.py +340 -0
- sqlspec/adapters/adbc/driver.py +266 -58
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +504 -0
- sqlspec/adapters/adbc/type_converter.py +153 -0
- 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 +88 -15
- sqlspec/adapters/aiosqlite/data_dictionary.py +149 -0
- sqlspec/adapters/aiosqlite/driver.py +143 -40
- 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 +2 -2
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +493 -0
- sqlspec/adapters/asyncmy/config.py +68 -23
- sqlspec/adapters/asyncmy/data_dictionary.py +161 -0
- sqlspec/adapters/asyncmy/driver.py +313 -58
- 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 +59 -35
- sqlspec/adapters/asyncpg/data_dictionary.py +173 -0
- sqlspec/adapters/asyncpg/driver.py +170 -25
- 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 +27 -10
- sqlspec/adapters/bigquery/data_dictionary.py +149 -0
- sqlspec/adapters/bigquery/driver.py +368 -142
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +327 -0
- sqlspec/adapters/bigquery/type_converter.py +125 -0
- sqlspec/adapters/duckdb/_types.py +1 -1
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +553 -0
- sqlspec/adapters/duckdb/config.py +80 -20
- sqlspec/adapters/duckdb/data_dictionary.py +163 -0
- sqlspec/adapters/duckdb/driver.py +167 -45
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +332 -0
- sqlspec/adapters/duckdb/pool.py +4 -4
- sqlspec/adapters/duckdb/type_converter.py +133 -0
- 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 +122 -32
- sqlspec/adapters/oracledb/data_dictionary.py +509 -0
- sqlspec/adapters/oracledb/driver.py +353 -91
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +767 -0
- sqlspec/adapters/oracledb/migrations.py +348 -73
- sqlspec/adapters/oracledb/type_converter.py +207 -0
- 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 +46 -17
- sqlspec/adapters/psqlpy/data_dictionary.py +172 -0
- sqlspec/adapters/psqlpy/driver.py +123 -209
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +272 -0
- sqlspec/adapters/psqlpy/type_converter.py +102 -0
- 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 +69 -35
- sqlspec/adapters/psycopg/data_dictionary.py +331 -0
- sqlspec/adapters/psycopg/driver.py +238 -81
- 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 +87 -15
- sqlspec/adapters/sqlite/data_dictionary.py +149 -0
- sqlspec/adapters/sqlite/driver.py +137 -54
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +318 -0
- sqlspec/adapters/sqlite/pool.py +18 -9
- sqlspec/base.py +45 -26
- sqlspec/builder/__init__.py +73 -4
- sqlspec/builder/_base.py +162 -89
- sqlspec/builder/_column.py +62 -29
- sqlspec/builder/_ddl.py +180 -121
- sqlspec/builder/_delete.py +5 -4
- sqlspec/builder/_dml.py +388 -0
- sqlspec/{_sql.py → builder/_factory.py} +53 -94
- sqlspec/builder/_insert.py +32 -131
- sqlspec/builder/_join.py +375 -0
- sqlspec/builder/_merge.py +446 -11
- sqlspec/builder/_parsing_utils.py +111 -17
- sqlspec/builder/_select.py +1457 -24
- sqlspec/builder/_update.py +11 -42
- sqlspec/cli.py +307 -194
- sqlspec/config.py +252 -67
- sqlspec/core/__init__.py +5 -4
- sqlspec/core/cache.py +17 -17
- sqlspec/core/compiler.py +62 -9
- sqlspec/core/filters.py +37 -37
- sqlspec/core/hashing.py +9 -9
- sqlspec/core/parameters.py +83 -48
- sqlspec/core/result.py +102 -46
- sqlspec/core/splitter.py +16 -17
- sqlspec/core/statement.py +36 -30
- sqlspec/core/type_conversion.py +235 -0
- sqlspec/driver/__init__.py +7 -6
- sqlspec/driver/_async.py +188 -151
- sqlspec/driver/_common.py +285 -80
- sqlspec/driver/_sync.py +188 -152
- sqlspec/driver/mixins/_result_tools.py +20 -236
- sqlspec/driver/mixins/_sql_translator.py +4 -4
- sqlspec/exceptions.py +75 -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/__init__.py +4 -3
- sqlspec/migrations/base.py +302 -39
- sqlspec/migrations/commands.py +611 -144
- sqlspec/migrations/context.py +142 -0
- sqlspec/migrations/fix.py +199 -0
- sqlspec/migrations/loaders.py +68 -23
- sqlspec/migrations/runner.py +543 -107
- 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 +16 -84
- sqlspec/utils/config_resolver.py +153 -0
- 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 +50 -2
- 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.25.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 -254
- sqlspec/builder/mixins/_delete_operations.py +0 -50
- sqlspec/builder/mixins/_insert_operations.py +0 -282
- sqlspec/builder/mixins/_join_operations.py +0 -389
- sqlspec/builder/mixins/_merge_operations.py +0 -592
- sqlspec/builder/mixins/_order_limit_operations.py +0 -152
- sqlspec/builder/mixins/_pivot_operations.py +0 -157
- sqlspec/builder/mixins/_select_operations.py +0 -936
- sqlspec/builder/mixins/_update_operations.py +0 -218
- sqlspec/builder/mixins/_where_clause.py +0 -1304
- sqlspec-0.25.0.dist-info/RECORD +0 -139
- sqlspec-0.25.0.dist-info/licenses/NOTICE +0 -29
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/licenses/LICENSE +0 -0
sqlspec/adapters/adbc/config.py
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""ADBC database configuration."""
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
+
from collections.abc import Callable
|
|
4
5
|
from contextlib import contextmanager
|
|
5
|
-
from typing import TYPE_CHECKING, Any,
|
|
6
|
+
from typing import TYPE_CHECKING, Any, ClassVar, TypedDict
|
|
6
7
|
|
|
7
8
|
from typing_extensions import NotRequired
|
|
8
9
|
|
|
@@ -22,7 +23,7 @@ if TYPE_CHECKING:
|
|
|
22
23
|
logger = logging.getLogger("sqlspec.adapters.adbc")
|
|
23
24
|
|
|
24
25
|
|
|
25
|
-
class AdbcConnectionParams(TypedDict
|
|
26
|
+
class AdbcConnectionParams(TypedDict):
|
|
26
27
|
"""ADBC connection parameters."""
|
|
27
28
|
|
|
28
29
|
uri: NotRequired[str]
|
|
@@ -54,7 +55,37 @@ class AdbcConnectionParams(TypedDict, total=False):
|
|
|
54
55
|
extra: NotRequired[dict[str, Any]]
|
|
55
56
|
|
|
56
57
|
|
|
57
|
-
|
|
58
|
+
class AdbcDriverFeatures(TypedDict):
|
|
59
|
+
"""ADBC driver feature configuration.
|
|
60
|
+
|
|
61
|
+
Controls optional type handling and serialization behavior for the ADBC adapter.
|
|
62
|
+
These features configure how data is converted between Python and Arrow types.
|
|
63
|
+
|
|
64
|
+
Attributes:
|
|
65
|
+
json_serializer: JSON serialization function to use.
|
|
66
|
+
Callable that takes Any and returns str (JSON string).
|
|
67
|
+
Default: sqlspec.utils.serializers.to_json
|
|
68
|
+
enable_cast_detection: Enable cast-aware parameter processing.
|
|
69
|
+
When True, detects SQL casts (e.g., ::JSONB) and applies appropriate
|
|
70
|
+
serialization. Currently used for PostgreSQL JSONB handling.
|
|
71
|
+
Default: True
|
|
72
|
+
strict_type_coercion: Enforce strict type coercion rules.
|
|
73
|
+
When True, raises errors for unsupported type conversions.
|
|
74
|
+
When False, attempts best-effort conversion.
|
|
75
|
+
Default: False
|
|
76
|
+
arrow_extension_types: Enable PyArrow extension type support.
|
|
77
|
+
When True, preserves Arrow extension type metadata when reading data.
|
|
78
|
+
When False, falls back to storage types.
|
|
79
|
+
Default: True
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
json_serializer: "NotRequired[Callable[[Any], str]]"
|
|
83
|
+
enable_cast_detection: NotRequired[bool]
|
|
84
|
+
strict_type_coercion: NotRequired[bool]
|
|
85
|
+
arrow_extension_types: NotRequired[bool]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
__all__ = ("AdbcConfig", "AdbcConnectionParams", "AdbcDriverFeatures")
|
|
58
89
|
|
|
59
90
|
|
|
60
91
|
class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]):
|
|
@@ -69,14 +100,17 @@ class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]):
|
|
|
69
100
|
|
|
70
101
|
driver_type: ClassVar[type[AdbcDriver]] = AdbcDriver
|
|
71
102
|
connection_type: "ClassVar[type[AdbcConnection]]" = AdbcConnection
|
|
103
|
+
supports_transactional_ddl: ClassVar[bool] = False
|
|
72
104
|
|
|
73
105
|
def __init__(
|
|
74
106
|
self,
|
|
75
107
|
*,
|
|
76
|
-
connection_config:
|
|
77
|
-
migration_config:
|
|
78
|
-
statement_config:
|
|
79
|
-
driver_features:
|
|
108
|
+
connection_config: AdbcConnectionParams | dict[str, Any] | None = None,
|
|
109
|
+
migration_config: dict[str, Any] | None = None,
|
|
110
|
+
statement_config: StatementConfig | None = None,
|
|
111
|
+
driver_features: "AdbcDriverFeatures | dict[str, Any] | None" = None,
|
|
112
|
+
bind_key: str | None = None,
|
|
113
|
+
extension_config: "dict[str, dict[str, Any]] | None" = None,
|
|
80
114
|
) -> None:
|
|
81
115
|
"""Initialize configuration.
|
|
82
116
|
|
|
@@ -84,7 +118,9 @@ class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]):
|
|
|
84
118
|
connection_config: Connection configuration parameters
|
|
85
119
|
migration_config: Migration configuration
|
|
86
120
|
statement_config: Default SQL statement configuration
|
|
87
|
-
driver_features: Driver feature configuration
|
|
121
|
+
driver_features: Driver feature configuration (AdbcDriverFeatures)
|
|
122
|
+
bind_key: Optional unique identifier for this configuration
|
|
123
|
+
extension_config: Extension-specific configuration (e.g., Litestar plugin settings)
|
|
88
124
|
"""
|
|
89
125
|
if connection_config is None:
|
|
90
126
|
connection_config = {}
|
|
@@ -99,11 +135,26 @@ class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]):
|
|
|
99
135
|
detected_dialect = str(self._get_dialect() or "sqlite")
|
|
100
136
|
statement_config = get_adbc_statement_config(detected_dialect)
|
|
101
137
|
|
|
138
|
+
from sqlspec.utils.serializers import to_json
|
|
139
|
+
|
|
140
|
+
if driver_features is None:
|
|
141
|
+
driver_features = {}
|
|
142
|
+
if "json_serializer" not in driver_features:
|
|
143
|
+
driver_features["json_serializer"] = to_json
|
|
144
|
+
if "enable_cast_detection" not in driver_features:
|
|
145
|
+
driver_features["enable_cast_detection"] = True
|
|
146
|
+
if "strict_type_coercion" not in driver_features:
|
|
147
|
+
driver_features["strict_type_coercion"] = False
|
|
148
|
+
if "arrow_extension_types" not in driver_features:
|
|
149
|
+
driver_features["arrow_extension_types"] = True
|
|
150
|
+
|
|
102
151
|
super().__init__(
|
|
103
152
|
connection_config=self.connection_config,
|
|
104
153
|
migration_config=migration_config,
|
|
105
154
|
statement_config=statement_config,
|
|
106
|
-
driver_features=driver_features
|
|
155
|
+
driver_features=dict(driver_features),
|
|
156
|
+
bind_key=bind_key,
|
|
157
|
+
extension_config=extension_config,
|
|
107
158
|
)
|
|
108
159
|
|
|
109
160
|
def _resolve_driver_name(self) -> str:
|
|
@@ -174,7 +225,11 @@ class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]):
|
|
|
174
225
|
try:
|
|
175
226
|
connect_func = import_string(driver_path)
|
|
176
227
|
except ImportError as e:
|
|
177
|
-
|
|
228
|
+
# Only add .dbapi.connect if it's not already there
|
|
229
|
+
if not driver_path.endswith(".dbapi.connect"):
|
|
230
|
+
driver_path_with_suffix = f"{driver_path}.dbapi.connect"
|
|
231
|
+
else:
|
|
232
|
+
driver_path_with_suffix = driver_path
|
|
178
233
|
try:
|
|
179
234
|
connect_func = import_string(driver_path_with_suffix)
|
|
180
235
|
except ImportError as e2:
|
|
@@ -277,7 +332,7 @@ class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]):
|
|
|
277
332
|
connection.close()
|
|
278
333
|
|
|
279
334
|
def provide_session(
|
|
280
|
-
self, *args: Any, statement_config: "
|
|
335
|
+
self, *args: Any, statement_config: "StatementConfig | None" = None, **kwargs: Any
|
|
281
336
|
) -> "AbstractContextManager[AdbcDriver]":
|
|
282
337
|
"""Provide a driver session context manager.
|
|
283
338
|
|
|
@@ -298,7 +353,9 @@ class AdbcConfig(NoPoolSyncConfig[AdbcConnection, AdbcDriver]):
|
|
|
298
353
|
or self.statement_config
|
|
299
354
|
or get_adbc_statement_config(str(self._get_dialect() or "sqlite"))
|
|
300
355
|
)
|
|
301
|
-
yield self.driver_type(
|
|
356
|
+
yield self.driver_type(
|
|
357
|
+
connection=connection, statement_config=final_statement_config, driver_features=self.driver_features
|
|
358
|
+
)
|
|
302
359
|
|
|
303
360
|
return session_manager()
|
|
304
361
|
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
"""ADBC multi-dialect data dictionary for metadata queries."""
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
5
|
+
|
|
6
|
+
from sqlspec.driver import SyncDataDictionaryBase, SyncDriverAdapterBase, VersionInfo
|
|
7
|
+
from sqlspec.utils.logging import get_logger
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from collections.abc import Callable
|
|
11
|
+
|
|
12
|
+
from sqlspec.adapters.adbc.driver import AdbcDriver
|
|
13
|
+
|
|
14
|
+
logger = get_logger("adapters.adbc.data_dictionary")
|
|
15
|
+
|
|
16
|
+
POSTGRES_VERSION_PATTERN = re.compile(r"PostgreSQL (\d+)\.(\d+)(?:\.(\d+))?")
|
|
17
|
+
SQLITE_VERSION_PATTERN = re.compile(r"(\d+)\.(\d+)\.(\d+)")
|
|
18
|
+
DUCKDB_VERSION_PATTERN = re.compile(r"v?(\d+)\.(\d+)\.(\d+)")
|
|
19
|
+
MYSQL_VERSION_PATTERN = re.compile(r"(\d+)\.(\d+)\.(\d+)")
|
|
20
|
+
|
|
21
|
+
__all__ = ("AdbcDataDictionary",)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AdbcDataDictionary(SyncDataDictionaryBase):
|
|
25
|
+
"""ADBC multi-dialect data dictionary.
|
|
26
|
+
|
|
27
|
+
Delegates to appropriate dialect-specific logic based on the driver's dialect.
|
|
28
|
+
"""
|
|
29
|
+
|
|
30
|
+
def _get_dialect(self, driver: SyncDriverAdapterBase) -> str:
|
|
31
|
+
"""Get dialect from ADBC driver.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
driver: ADBC driver instance
|
|
35
|
+
|
|
36
|
+
Returns:
|
|
37
|
+
Dialect name
|
|
38
|
+
"""
|
|
39
|
+
return str(cast("AdbcDriver", driver).dialect)
|
|
40
|
+
|
|
41
|
+
def get_version(self, driver: SyncDriverAdapterBase) -> "VersionInfo | None":
|
|
42
|
+
"""Get database version information based on detected dialect.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
driver: ADBC driver instance
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
Database version information or None if detection fails
|
|
49
|
+
"""
|
|
50
|
+
dialect = self._get_dialect(driver)
|
|
51
|
+
adbc_driver = cast("AdbcDriver", driver)
|
|
52
|
+
|
|
53
|
+
try:
|
|
54
|
+
if dialect == "postgres":
|
|
55
|
+
version_str = adbc_driver.select_value("SELECT version()")
|
|
56
|
+
if version_str:
|
|
57
|
+
match = POSTGRES_VERSION_PATTERN.search(str(version_str))
|
|
58
|
+
if match:
|
|
59
|
+
major = int(match.group(1))
|
|
60
|
+
minor = int(match.group(2))
|
|
61
|
+
patch = int(match.group(3)) if match.group(3) else 0
|
|
62
|
+
return VersionInfo(major, minor, patch)
|
|
63
|
+
|
|
64
|
+
elif dialect == "sqlite":
|
|
65
|
+
version_str = adbc_driver.select_value("SELECT sqlite_version()")
|
|
66
|
+
if version_str:
|
|
67
|
+
match = SQLITE_VERSION_PATTERN.match(str(version_str))
|
|
68
|
+
if match:
|
|
69
|
+
major, minor, patch = map(int, match.groups())
|
|
70
|
+
return VersionInfo(major, minor, patch)
|
|
71
|
+
|
|
72
|
+
elif dialect == "duckdb":
|
|
73
|
+
version_str = adbc_driver.select_value("SELECT version()")
|
|
74
|
+
if version_str:
|
|
75
|
+
match = DUCKDB_VERSION_PATTERN.search(str(version_str))
|
|
76
|
+
if match:
|
|
77
|
+
major, minor, patch = map(int, match.groups())
|
|
78
|
+
return VersionInfo(major, minor, patch)
|
|
79
|
+
|
|
80
|
+
elif dialect == "mysql":
|
|
81
|
+
version_str = adbc_driver.select_value("SELECT VERSION()")
|
|
82
|
+
if version_str:
|
|
83
|
+
match = MYSQL_VERSION_PATTERN.search(str(version_str))
|
|
84
|
+
if match:
|
|
85
|
+
major, minor, patch = map(int, match.groups())
|
|
86
|
+
return VersionInfo(major, minor, patch)
|
|
87
|
+
|
|
88
|
+
elif dialect == "bigquery":
|
|
89
|
+
return VersionInfo(1, 0, 0)
|
|
90
|
+
|
|
91
|
+
except Exception:
|
|
92
|
+
logger.warning("Failed to get %s version", dialect)
|
|
93
|
+
|
|
94
|
+
return None
|
|
95
|
+
|
|
96
|
+
def get_feature_flag(self, driver: SyncDriverAdapterBase, feature: str) -> bool:
|
|
97
|
+
"""Check if database supports a specific feature based on detected dialect.
|
|
98
|
+
|
|
99
|
+
Args:
|
|
100
|
+
driver: ADBC driver instance
|
|
101
|
+
feature: Feature name to check
|
|
102
|
+
|
|
103
|
+
Returns:
|
|
104
|
+
True if feature is supported, False otherwise
|
|
105
|
+
"""
|
|
106
|
+
dialect = self._get_dialect(driver)
|
|
107
|
+
version_info = self.get_version(driver)
|
|
108
|
+
|
|
109
|
+
if dialect == "postgres":
|
|
110
|
+
feature_checks: dict[str, Callable[..., bool]] = {
|
|
111
|
+
"supports_json": lambda v: v and v >= VersionInfo(9, 2, 0),
|
|
112
|
+
"supports_jsonb": lambda v: v and v >= VersionInfo(9, 4, 0),
|
|
113
|
+
"supports_uuid": lambda _: True,
|
|
114
|
+
"supports_arrays": lambda _: True,
|
|
115
|
+
"supports_returning": lambda v: v and v >= VersionInfo(8, 2, 0),
|
|
116
|
+
"supports_upsert": lambda v: v and v >= VersionInfo(9, 5, 0),
|
|
117
|
+
"supports_window_functions": lambda v: v and v >= VersionInfo(8, 4, 0),
|
|
118
|
+
"supports_cte": lambda v: v and v >= VersionInfo(8, 4, 0),
|
|
119
|
+
"supports_transactions": lambda _: True,
|
|
120
|
+
"supports_prepared_statements": lambda _: True,
|
|
121
|
+
"supports_schemas": lambda _: True,
|
|
122
|
+
}
|
|
123
|
+
elif dialect == "sqlite":
|
|
124
|
+
feature_checks = {
|
|
125
|
+
"supports_json": lambda v: v and v >= VersionInfo(3, 38, 0),
|
|
126
|
+
"supports_returning": lambda v: v and v >= VersionInfo(3, 35, 0),
|
|
127
|
+
"supports_upsert": lambda v: v and v >= VersionInfo(3, 24, 0),
|
|
128
|
+
"supports_window_functions": lambda v: v and v >= VersionInfo(3, 25, 0),
|
|
129
|
+
"supports_cte": lambda v: v and v >= VersionInfo(3, 8, 3),
|
|
130
|
+
"supports_transactions": lambda _: True,
|
|
131
|
+
"supports_prepared_statements": lambda _: True,
|
|
132
|
+
"supports_schemas": lambda _: False,
|
|
133
|
+
"supports_arrays": lambda _: False,
|
|
134
|
+
"supports_uuid": lambda _: False,
|
|
135
|
+
}
|
|
136
|
+
elif dialect == "duckdb":
|
|
137
|
+
feature_checks = {
|
|
138
|
+
"supports_json": lambda _: True,
|
|
139
|
+
"supports_arrays": lambda _: True,
|
|
140
|
+
"supports_uuid": lambda _: True,
|
|
141
|
+
"supports_returning": lambda v: v and v >= VersionInfo(0, 8, 0),
|
|
142
|
+
"supports_upsert": lambda v: v and v >= VersionInfo(0, 8, 0),
|
|
143
|
+
"supports_window_functions": lambda _: True,
|
|
144
|
+
"supports_cte": lambda _: True,
|
|
145
|
+
"supports_transactions": lambda _: True,
|
|
146
|
+
"supports_prepared_statements": lambda _: True,
|
|
147
|
+
"supports_schemas": lambda _: True,
|
|
148
|
+
}
|
|
149
|
+
elif dialect == "mysql":
|
|
150
|
+
feature_checks = {
|
|
151
|
+
"supports_json": lambda v: v and v >= VersionInfo(5, 7, 8),
|
|
152
|
+
"supports_cte": lambda v: v and v >= VersionInfo(8, 0, 1),
|
|
153
|
+
"supports_returning": lambda _: False,
|
|
154
|
+
"supports_upsert": lambda _: True,
|
|
155
|
+
"supports_window_functions": lambda v: v and v >= VersionInfo(8, 0, 2),
|
|
156
|
+
"supports_transactions": lambda _: True,
|
|
157
|
+
"supports_prepared_statements": lambda _: True,
|
|
158
|
+
"supports_schemas": lambda _: True,
|
|
159
|
+
"supports_uuid": lambda _: False,
|
|
160
|
+
"supports_arrays": lambda _: False,
|
|
161
|
+
}
|
|
162
|
+
elif dialect == "bigquery":
|
|
163
|
+
feature_checks = {
|
|
164
|
+
"supports_json": lambda _: True,
|
|
165
|
+
"supports_arrays": lambda _: True,
|
|
166
|
+
"supports_structs": lambda _: True,
|
|
167
|
+
"supports_returning": lambda _: False,
|
|
168
|
+
"supports_upsert": lambda _: True,
|
|
169
|
+
"supports_window_functions": lambda _: True,
|
|
170
|
+
"supports_cte": lambda _: True,
|
|
171
|
+
"supports_transactions": lambda _: False,
|
|
172
|
+
"supports_prepared_statements": lambda _: True,
|
|
173
|
+
"supports_schemas": lambda _: True,
|
|
174
|
+
"supports_uuid": lambda _: False,
|
|
175
|
+
}
|
|
176
|
+
else:
|
|
177
|
+
feature_checks = {
|
|
178
|
+
"supports_transactions": lambda _: True,
|
|
179
|
+
"supports_prepared_statements": lambda _: True,
|
|
180
|
+
"supports_window_functions": lambda _: True,
|
|
181
|
+
"supports_cte": lambda _: True,
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if feature in feature_checks:
|
|
185
|
+
return bool(feature_checks[feature](version_info))
|
|
186
|
+
|
|
187
|
+
return False
|
|
188
|
+
|
|
189
|
+
def get_optimal_type(self, driver: SyncDriverAdapterBase, type_category: str) -> str:
|
|
190
|
+
"""Get optimal database type for a category based on detected dialect.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
driver: ADBC driver instance
|
|
194
|
+
type_category: Type category
|
|
195
|
+
|
|
196
|
+
Returns:
|
|
197
|
+
Database-specific type name
|
|
198
|
+
"""
|
|
199
|
+
dialect = self._get_dialect(driver)
|
|
200
|
+
version_info = self.get_version(driver)
|
|
201
|
+
|
|
202
|
+
if dialect == "postgres":
|
|
203
|
+
if type_category == "json":
|
|
204
|
+
if version_info and version_info >= VersionInfo(9, 4, 0):
|
|
205
|
+
return "JSONB"
|
|
206
|
+
if version_info and version_info >= VersionInfo(9, 2, 0):
|
|
207
|
+
return "JSON"
|
|
208
|
+
return "TEXT"
|
|
209
|
+
type_map = {
|
|
210
|
+
"uuid": "UUID",
|
|
211
|
+
"boolean": "BOOLEAN",
|
|
212
|
+
"timestamp": "TIMESTAMP WITH TIME ZONE",
|
|
213
|
+
"text": "TEXT",
|
|
214
|
+
"blob": "BYTEA",
|
|
215
|
+
"array": "ARRAY",
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
elif dialect == "sqlite":
|
|
219
|
+
if type_category == "json":
|
|
220
|
+
if version_info and version_info >= VersionInfo(3, 38, 0):
|
|
221
|
+
return "JSON"
|
|
222
|
+
return "TEXT"
|
|
223
|
+
type_map = {"uuid": "TEXT", "boolean": "INTEGER", "timestamp": "TIMESTAMP", "text": "TEXT", "blob": "BLOB"}
|
|
224
|
+
|
|
225
|
+
elif dialect == "duckdb":
|
|
226
|
+
type_map = {
|
|
227
|
+
"json": "JSON",
|
|
228
|
+
"uuid": "UUID",
|
|
229
|
+
"boolean": "BOOLEAN",
|
|
230
|
+
"timestamp": "TIMESTAMP",
|
|
231
|
+
"text": "TEXT",
|
|
232
|
+
"blob": "BLOB",
|
|
233
|
+
"array": "LIST",
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
elif dialect == "mysql":
|
|
237
|
+
if type_category == "json":
|
|
238
|
+
if version_info and version_info >= VersionInfo(5, 7, 8):
|
|
239
|
+
return "JSON"
|
|
240
|
+
return "TEXT"
|
|
241
|
+
type_map = {
|
|
242
|
+
"uuid": "VARCHAR(36)",
|
|
243
|
+
"boolean": "TINYINT(1)",
|
|
244
|
+
"timestamp": "TIMESTAMP",
|
|
245
|
+
"text": "TEXT",
|
|
246
|
+
"blob": "BLOB",
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
elif dialect == "bigquery":
|
|
250
|
+
type_map = {
|
|
251
|
+
"json": "JSON",
|
|
252
|
+
"uuid": "STRING",
|
|
253
|
+
"boolean": "BOOL",
|
|
254
|
+
"timestamp": "TIMESTAMP",
|
|
255
|
+
"text": "STRING",
|
|
256
|
+
"blob": "BYTES",
|
|
257
|
+
"array": "ARRAY",
|
|
258
|
+
}
|
|
259
|
+
else:
|
|
260
|
+
type_map = {
|
|
261
|
+
"json": "TEXT",
|
|
262
|
+
"uuid": "VARCHAR(36)",
|
|
263
|
+
"boolean": "INTEGER",
|
|
264
|
+
"timestamp": "TIMESTAMP",
|
|
265
|
+
"text": "TEXT",
|
|
266
|
+
"blob": "BLOB",
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return type_map.get(type_category, "TEXT")
|
|
270
|
+
|
|
271
|
+
def get_columns(
|
|
272
|
+
self, driver: SyncDriverAdapterBase, table: str, schema: "str | None" = None
|
|
273
|
+
) -> "list[dict[str, Any]]":
|
|
274
|
+
"""Get column information for a table based on detected dialect.
|
|
275
|
+
|
|
276
|
+
Args:
|
|
277
|
+
driver: ADBC driver instance
|
|
278
|
+
table: Table name to query columns for
|
|
279
|
+
schema: Schema name (None for default)
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
List of column metadata dictionaries with keys:
|
|
283
|
+
- column_name: Name of the column
|
|
284
|
+
- data_type: Database data type
|
|
285
|
+
- is_nullable or nullable: Whether column allows NULL
|
|
286
|
+
- column_default or default_value: Default value if any
|
|
287
|
+
"""
|
|
288
|
+
dialect = self._get_dialect(driver)
|
|
289
|
+
adbc_driver = cast("AdbcDriver", driver)
|
|
290
|
+
|
|
291
|
+
if dialect == "sqlite":
|
|
292
|
+
result = adbc_driver.execute(f"PRAGMA table_info({table})")
|
|
293
|
+
return [
|
|
294
|
+
{
|
|
295
|
+
"column_name": row["name"] if isinstance(row, dict) else row[1],
|
|
296
|
+
"data_type": row["type"] if isinstance(row, dict) else row[2],
|
|
297
|
+
"nullable": not (row["notnull"] if isinstance(row, dict) else row[3]),
|
|
298
|
+
"default_value": row["dflt_value"] if isinstance(row, dict) else row[4],
|
|
299
|
+
}
|
|
300
|
+
for row in result.data or []
|
|
301
|
+
]
|
|
302
|
+
|
|
303
|
+
if schema:
|
|
304
|
+
sql = f"""
|
|
305
|
+
SELECT column_name, data_type, is_nullable, column_default
|
|
306
|
+
FROM information_schema.columns
|
|
307
|
+
WHERE table_name = '{table}' AND table_schema = '{schema}'
|
|
308
|
+
ORDER BY ordinal_position
|
|
309
|
+
"""
|
|
310
|
+
else:
|
|
311
|
+
sql = f"""
|
|
312
|
+
SELECT column_name, data_type, is_nullable, column_default
|
|
313
|
+
FROM information_schema.columns
|
|
314
|
+
WHERE table_name = '{table}'
|
|
315
|
+
ORDER BY ordinal_position
|
|
316
|
+
"""
|
|
317
|
+
|
|
318
|
+
result = adbc_driver.execute(sql)
|
|
319
|
+
return result.data or []
|
|
320
|
+
|
|
321
|
+
def list_available_features(self) -> "list[str]":
|
|
322
|
+
"""List available feature flags across all supported dialects.
|
|
323
|
+
|
|
324
|
+
Returns:
|
|
325
|
+
List of supported feature names
|
|
326
|
+
"""
|
|
327
|
+
return [
|
|
328
|
+
"supports_json",
|
|
329
|
+
"supports_jsonb",
|
|
330
|
+
"supports_uuid",
|
|
331
|
+
"supports_arrays",
|
|
332
|
+
"supports_structs",
|
|
333
|
+
"supports_returning",
|
|
334
|
+
"supports_upsert",
|
|
335
|
+
"supports_window_functions",
|
|
336
|
+
"supports_cte",
|
|
337
|
+
"supports_transactions",
|
|
338
|
+
"supports_prepared_statements",
|
|
339
|
+
"supports_schemas",
|
|
340
|
+
]
|