sqlspec 0.7.0__py3-none-any.whl → 0.8.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.

Files changed (50) hide show
  1. sqlspec/__init__.py +15 -0
  2. sqlspec/_serialization.py +16 -2
  3. sqlspec/_typing.py +1 -1
  4. sqlspec/adapters/adbc/__init__.py +7 -0
  5. sqlspec/adapters/adbc/config.py +160 -17
  6. sqlspec/adapters/adbc/driver.py +333 -0
  7. sqlspec/adapters/aiosqlite/__init__.py +6 -2
  8. sqlspec/adapters/aiosqlite/config.py +25 -7
  9. sqlspec/adapters/aiosqlite/driver.py +275 -0
  10. sqlspec/adapters/asyncmy/__init__.py +7 -2
  11. sqlspec/adapters/asyncmy/config.py +75 -14
  12. sqlspec/adapters/asyncmy/driver.py +255 -0
  13. sqlspec/adapters/asyncpg/__init__.py +9 -0
  14. sqlspec/adapters/asyncpg/config.py +99 -20
  15. sqlspec/adapters/asyncpg/driver.py +288 -0
  16. sqlspec/adapters/duckdb/__init__.py +6 -2
  17. sqlspec/adapters/duckdb/config.py +197 -15
  18. sqlspec/adapters/duckdb/driver.py +225 -0
  19. sqlspec/adapters/oracledb/__init__.py +11 -8
  20. sqlspec/adapters/oracledb/config/__init__.py +6 -6
  21. sqlspec/adapters/oracledb/config/_asyncio.py +98 -13
  22. sqlspec/adapters/oracledb/config/_common.py +1 -1
  23. sqlspec/adapters/oracledb/config/_sync.py +99 -14
  24. sqlspec/adapters/oracledb/driver.py +498 -0
  25. sqlspec/adapters/psycopg/__init__.py +11 -0
  26. sqlspec/adapters/psycopg/config/__init__.py +6 -6
  27. sqlspec/adapters/psycopg/config/_async.py +105 -13
  28. sqlspec/adapters/psycopg/config/_common.py +2 -2
  29. sqlspec/adapters/psycopg/config/_sync.py +105 -13
  30. sqlspec/adapters/psycopg/driver.py +616 -0
  31. sqlspec/adapters/sqlite/__init__.py +7 -0
  32. sqlspec/adapters/sqlite/config.py +25 -7
  33. sqlspec/adapters/sqlite/driver.py +303 -0
  34. sqlspec/base.py +416 -36
  35. sqlspec/extensions/litestar/__init__.py +19 -0
  36. sqlspec/extensions/litestar/_utils.py +56 -0
  37. sqlspec/extensions/litestar/config.py +81 -0
  38. sqlspec/extensions/litestar/handlers.py +188 -0
  39. sqlspec/extensions/litestar/plugin.py +103 -11
  40. sqlspec/typing.py +72 -17
  41. sqlspec/utils/__init__.py +3 -0
  42. sqlspec/utils/deprecation.py +1 -1
  43. sqlspec/utils/fixtures.py +4 -5
  44. sqlspec/utils/sync_tools.py +335 -0
  45. {sqlspec-0.7.0.dist-info → sqlspec-0.8.0.dist-info}/METADATA +1 -1
  46. sqlspec-0.8.0.dist-info/RECORD +57 -0
  47. sqlspec-0.7.0.dist-info/RECORD +0 -46
  48. {sqlspec-0.7.0.dist-info → sqlspec-0.8.0.dist-info}/WHEEL +0 -0
  49. {sqlspec-0.7.0.dist-info → sqlspec-0.8.0.dist-info}/licenses/LICENSE +0 -0
  50. {sqlspec-0.7.0.dist-info → sqlspec-0.8.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/__init__.py CHANGED
@@ -0,0 +1,15 @@
1
+ from sqlspec import adapters, base, exceptions, extensions, filters, typing, utils
2
+ from sqlspec.__metadata__ import __version__
3
+ from sqlspec.base import SQLSpec
4
+
5
+ __all__ = (
6
+ "SQLSpec",
7
+ "__version__",
8
+ "adapters",
9
+ "base",
10
+ "exceptions",
11
+ "extensions",
12
+ "filters",
13
+ "typing",
14
+ "utils",
15
+ )
sqlspec/_serialization.py CHANGED
@@ -60,12 +60,26 @@ __all__ = (
60
60
 
61
61
 
62
62
  def convert_datetime_to_gmt_iso(dt: datetime.datetime) -> str: # pragma: no cover
63
- """Handle datetime serialization for nested timestamps."""
63
+ """Handle datetime serialization for nested timestamps.
64
+
65
+ Args:
66
+ dt: The datetime to convert.
67
+
68
+ Returns:
69
+ The ISO formatted datetime string.
70
+ """
64
71
  if not dt.tzinfo:
65
72
  dt = dt.replace(tzinfo=datetime.timezone.utc)
66
73
  return dt.isoformat().replace("+00:00", "Z")
67
74
 
68
75
 
69
76
  def convert_date_to_iso(dt: datetime.date) -> str: # pragma: no cover
70
- """Handle datetime serialization for nested timestamps."""
77
+ """Handle datetime serialization for nested timestamps.
78
+
79
+ Args:
80
+ dt: The date to convert.
81
+
82
+ Returns:
83
+ The ISO formatted date string.
84
+ """
71
85
  return dt.isoformat()
sqlspec/_typing.py CHANGED
@@ -29,7 +29,7 @@ T_co = TypeVar("T_co", covariant=True)
29
29
 
30
30
  try:
31
31
  from pydantic import (
32
- BaseModel,
32
+ BaseModel, # pyright: ignore[reportAssignmentType]
33
33
  FailFast, # pyright: ignore[reportGeneralTypeIssues,reportAssignmentType]
34
34
  TypeAdapter,
35
35
  )
@@ -0,0 +1,7 @@
1
+ from sqlspec.adapters.adbc.config import Adbc
2
+ from sqlspec.adapters.adbc.driver import AdbcDriver
3
+
4
+ __all__ = (
5
+ "Adbc",
6
+ "AdbcDriver",
7
+ )
@@ -1,21 +1,24 @@
1
1
  from contextlib import contextmanager
2
- from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Optional, Union
2
+ from dataclasses import dataclass, field
3
+ from typing import TYPE_CHECKING, Any, Callable, Optional, Union, cast
4
4
 
5
+ from adbc_driver_manager.dbapi import Connection
6
+
7
+ from sqlspec.adapters.adbc.driver import AdbcDriver
5
8
  from sqlspec.base import NoPoolSyncConfig
9
+ from sqlspec.exceptions import ImproperConfigurationError
6
10
  from sqlspec.typing import Empty, EmptyType
11
+ from sqlspec.utils.module_loader import import_string
7
12
 
8
13
  if TYPE_CHECKING:
9
14
  from collections.abc import Generator
10
- from typing import Any
11
15
 
12
- from adbc_driver_manager.dbapi import Connection
13
16
 
14
- __all__ = ("AdbcDatabaseConfig",)
17
+ __all__ = ("Adbc",)
15
18
 
16
19
 
17
20
  @dataclass
18
- class AdbcDatabaseConfig(NoPoolSyncConfig["Connection"]):
21
+ class Adbc(NoPoolSyncConfig["Connection", "AdbcDriver"]):
19
22
  """Configuration for ADBC connections.
20
23
 
21
24
  This class provides configuration options for ADBC database connections using the
@@ -25,23 +28,163 @@ class AdbcDatabaseConfig(NoPoolSyncConfig["Connection"]):
25
28
  uri: "Union[str, EmptyType]" = Empty
26
29
  """Database URI"""
27
30
  driver_name: "Union[str, EmptyType]" = Empty
28
- """Name of the ADBC driver to use"""
31
+ """Full dotted path to the ADBC driver's connect function (e.g., 'adbc_driver_sqlite.dbapi.connect')"""
29
32
  db_kwargs: "Optional[dict[str, Any]]" = None
30
33
  """Additional database-specific connection parameters"""
34
+ conn_kwargs: "Optional[dict[str, Any]]" = None
35
+ """Additional database-specific connection parameters"""
36
+ connection_type: "type[Connection]" = field(init=False, default_factory=lambda: Connection)
37
+ """Type of the connection object"""
38
+ driver_type: "type[AdbcDriver]" = field(init=False, default_factory=lambda: AdbcDriver) # type: ignore[type-abstract,unused-ignore]
39
+ """Type of the driver object"""
40
+ pool_instance: None = field(init=False, default=None)
41
+ """No connection pool is used for ADBC connections"""
42
+ _is_in_memory: bool = field(init=False, default=False)
43
+ """Flag indicating if the connection is for an in-memory database"""
44
+
45
+ def _set_adbc(self) -> str: # noqa: PLR0912
46
+ """Identify the driver type based on the URI (if provided) or preset driver name.
47
+
48
+ Also sets the `_is_in_memory` flag for specific in-memory URIs.
49
+
50
+ Raises:
51
+ ImproperConfigurationError: If the driver name is not recognized or supported.
52
+
53
+ Returns:
54
+ str: The driver name to be used for the connection.
55
+ """
56
+
57
+ if isinstance(self.driver_name, str):
58
+ if self.driver_name != "adbc_driver_sqlite.dbapi.connect" and "sqlite" in self.driver_name:
59
+ self.driver_name = "adbc_driver_sqlite.dbapi.connect"
60
+ elif self.driver_name != "adbc_driver_duckdb.dbapi.connect" and "duckdb" in self.driver_name:
61
+ self.driver_name = "adbc_driver_duckdb.dbapi.connect"
62
+ elif self.driver_name != "adbc_driver_postgresql.dbapi.connect" and "postgres" in self.driver_name:
63
+ self.driver_name = "adbc_driver_postgresql.dbapi.connect"
64
+ elif self.driver_name != "adbc_driver_snowflake.dbapi.connect" and "snowflake" in self.driver_name:
65
+ self.driver_name = "adbc_driver_snowflake.dbapi.connect"
66
+ elif self.driver_name != "adbc_driver_bigquery.dbapi.connect" and "bigquery" in self.driver_name:
67
+ self.driver_name = "adbc_driver_bigquery.dbapi.connect"
68
+ elif self.driver_name != "adbc_driver_flightsql.dbapi.connect" and "flightsql" in self.driver_name:
69
+ self.driver_name = "adbc_driver_flightsql.dbapi.connect"
70
+ return self.driver_name
71
+
72
+ # If driver_name wasn't explicit, try to determine from URI
73
+ if isinstance(self.uri, str) and self.uri.startswith("postgresql://"):
74
+ self.driver_name = "adbc_driver_postgresql.dbapi.connect"
75
+ elif isinstance(self.uri, str) and self.uri.startswith("sqlite://"):
76
+ self.driver_name = "adbc_driver_sqlite.dbapi.connect"
77
+ elif isinstance(self.uri, str) and self.uri.startswith("grpc://"):
78
+ self.driver_name = "adbc_driver_flightsql.dbapi.connect"
79
+ elif isinstance(self.uri, str) and self.uri.startswith("snowflake://"):
80
+ self.driver_name = "adbc_driver_snowflake.dbapi.connect"
81
+ elif isinstance(self.uri, str) and self.uri.startswith("bigquery://"):
82
+ self.driver_name = "adbc_driver_bigquery.dbapi.connect"
83
+ elif isinstance(self.uri, str) and self.uri.startswith("duckdb://"):
84
+ self.driver_name = "adbc_driver_duckdb.dbapi.connect"
85
+
86
+ # Check if we successfully determined a driver name
87
+ if self.driver_name is Empty or not isinstance(self.driver_name, str):
88
+ msg = (
89
+ "Could not determine ADBC driver connect path. Please specify 'driver_name' "
90
+ "(e.g., 'adbc_driver_sqlite.dbapi.connect') or provide a supported 'uri'. "
91
+ f"URI: {self.uri}, Driver Name: {self.driver_name}"
92
+ )
93
+ raise ImproperConfigurationError(msg)
94
+ return self.driver_name
31
95
 
32
96
  @property
33
- def connection_params(self) -> "dict[str, Any]":
34
- """Return the connection parameters as a dict."""
35
- return {
36
- k: v
37
- for k, v in {"uri": self.uri, "driver": self.driver_name, **(self.db_kwargs or {})}.items()
38
- if v is not Empty
39
- }
97
+ def connection_config_dict(self) -> "dict[str, Any]":
98
+ """Return the connection configuration as a dict.
99
+
100
+ Omits the 'uri' key for known in-memory database types.
101
+
102
+ Returns:
103
+ A string keyed dict of config kwargs for the adbc_driver_manager.dbapi.connect function.
104
+ """
105
+ config = {}
106
+ db_kwargs = self.db_kwargs or {}
107
+ conn_kwargs = self.conn_kwargs or {}
108
+ if isinstance(self.uri, str) and self.uri.startswith("sqlite://"):
109
+ db_kwargs["uri"] = self.uri.replace("sqlite://", "")
110
+ elif isinstance(self.uri, str) and self.uri.startswith("duckdb://"):
111
+ db_kwargs["path"] = self.uri.replace("duckdb://", "")
112
+ elif isinstance(self.uri, str):
113
+ db_kwargs["uri"] = self.uri
114
+ if isinstance(self.driver_name, str) and self.driver_name.startswith("adbc_driver_bigquery"):
115
+ config["db_kwargs"] = db_kwargs
116
+ else:
117
+ config = db_kwargs
118
+ if conn_kwargs:
119
+ config["conn_kwargs"] = conn_kwargs
120
+ return config
121
+
122
+ def _get_connect_func(self) -> "Callable[..., Connection]":
123
+ self._set_adbc()
124
+ driver_path = cast("str", self.driver_name)
125
+ try:
126
+ connect_func = import_string(driver_path)
127
+ except ImportError as e:
128
+ # Check if the error is likely due to missing suffix and try again
129
+ if ".dbapi.connect" not in driver_path:
130
+ try:
131
+ driver_path += ".dbapi.connect"
132
+ connect_func = import_string(driver_path)
133
+ except ImportError as e2:
134
+ msg = f"Failed to import ADBC connect function from '{self.driver_name}' or '{driver_path}'. Is the driver installed and the path correct? Original error: {e} / {e2}"
135
+ raise ImproperConfigurationError(msg) from e2
136
+ else:
137
+ # Original import failed, and suffix was already present or added
138
+ msg = f"Failed to import ADBC connect function from '{driver_path}'. Is the driver installed and the path correct? Original error: {e}"
139
+ raise ImproperConfigurationError(msg) from e
140
+ if not callable(connect_func):
141
+ msg = f"The path '{driver_path}' did not resolve to a callable function."
142
+ raise ImproperConfigurationError(msg)
143
+ return connect_func # type: ignore[no-any-return]
144
+
145
+ def create_connection(self) -> "Connection":
146
+ """Create and return a new database connection using the specific driver.
147
+
148
+ Returns:
149
+ A new ADBC connection instance.
150
+
151
+ Raises:
152
+ ImproperConfigurationError: If the connection could not be established.
153
+ """
154
+ try:
155
+ connect_func = self._get_connect_func()
156
+ _config = self.connection_config_dict
157
+ return connect_func(**_config)
158
+ except Exception as e:
159
+ # Include driver name in error message for better context
160
+ driver_name = self.driver_name if isinstance(self.driver_name, str) else "Unknown/Derived"
161
+ # Use the potentially modified driver_path from _get_connect_func if available,
162
+ # otherwise fallback to self.driver_name for the error message.
163
+ # This requires _get_connect_func to potentially return the used path or store it.
164
+ # For simplicity now, we stick to self.driver_name in the message.
165
+ msg = f"Could not configure the ADBC connection using driver path '{driver_name}'. Error: {e!s}"
166
+ raise ImproperConfigurationError(msg) from e
40
167
 
41
168
  @contextmanager
42
169
  def provide_connection(self, *args: "Any", **kwargs: "Any") -> "Generator[Connection, None, None]":
43
- """Create and provide a database connection."""
44
- from adbc_driver_manager.dbapi import connect
170
+ """Create and provide a database connection using the specific driver.
45
171
 
46
- with connect(**self.connection_params) as connection:
172
+ Yields:
173
+ Connection: A database connection instance.
174
+ """
175
+
176
+ connection = self.create_connection()
177
+ try:
47
178
  yield connection
179
+ finally:
180
+ connection.close()
181
+
182
+ @contextmanager
183
+ def provide_session(self, *args: Any, **kwargs: Any) -> "Generator[AdbcDriver, None, None]":
184
+ """Create and provide a database session.
185
+
186
+ Yields:
187
+ An ADBC driver instance with an active connection.
188
+ """
189
+ with self.provide_connection(*args, **kwargs) as connection:
190
+ yield self.driver_type(connection)
@@ -0,0 +1,333 @@
1
+ import contextlib
2
+ import re
3
+ from collections.abc import Generator
4
+ from contextlib import contextmanager
5
+ from typing import TYPE_CHECKING, Any, Optional, Union, cast
6
+
7
+ from adbc_driver_manager.dbapi import Connection, Cursor
8
+
9
+ from sqlspec.base import SyncDriverAdapterProtocol, T
10
+
11
+ if TYPE_CHECKING:
12
+ from sqlspec.typing import ModelDTOT, StatementParameterType
13
+
14
+ __all__ = ("AdbcDriver",)
15
+
16
+
17
+ # Regex to find :param or %(param)s style placeholders, skipping those inside quotes
18
+ PARAM_REGEX = re.compile(
19
+ r"""
20
+ (?P<dquote>"([^"]|\\")*") | # Double-quoted strings
21
+ (?P<squote>'([^']|\\')*') | # Single-quoted strings
22
+ : (?P<var_name_colon>[a-zA-Z_][a-zA-Z0-9_]*) | # :var_name
23
+ % \( (?P<var_name_perc>[a-zA-Z_][a-zA-Z0-9_]*) \) s # %(var_name)s
24
+ """,
25
+ re.VERBOSE,
26
+ )
27
+
28
+
29
+ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
30
+ """ADBC Sync Driver Adapter."""
31
+
32
+ connection: Connection
33
+
34
+ def __init__(self, connection: "Connection") -> None:
35
+ """Initialize the ADBC driver adapter."""
36
+ self.connection = connection
37
+ # Potentially introspect connection.paramstyle here if needed in the future
38
+ # For now, assume 'qmark' based on typical ADBC DBAPI behavior
39
+
40
+ @staticmethod
41
+ def _cursor(connection: "Connection", *args: Any, **kwargs: Any) -> "Cursor":
42
+ return connection.cursor(*args, **kwargs)
43
+
44
+ @contextmanager
45
+ def _with_cursor(self, connection: "Connection") -> Generator["Cursor", None, None]:
46
+ cursor = self._cursor(connection)
47
+ try:
48
+ yield cursor
49
+ finally:
50
+ with contextlib.suppress(Exception):
51
+ cursor.close() # type: ignore[no-untyped-call]
52
+
53
+ def _process_sql_params(
54
+ self, sql: str, parameters: "Optional[StatementParameterType]" = None
55
+ ) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]]]":
56
+ """Process SQL query and parameters for DB-API execution.
57
+
58
+ Converts named parameters (:name or %(name)s) to positional parameters specified by `self.param_style`
59
+ if the input parameters are a dictionary.
60
+
61
+ Args:
62
+ sql: The SQL query string.
63
+ parameters: The parameters for the query (dict, tuple, list, or None).
64
+
65
+ Returns:
66
+ A tuple containing the processed SQL string and the processed parameters
67
+ (always a tuple or None if the input was a dictionary, otherwise the original type).
68
+
69
+ Raises:
70
+ ValueError: If a named parameter in the SQL is not found in the dictionary
71
+ or if a parameter in the dictionary is not used in the SQL.
72
+ """
73
+ if not isinstance(parameters, dict) or not parameters:
74
+ # If parameters are not a dict, or empty dict, assume positional/no params
75
+ # Let the underlying driver handle tuples/lists directly
76
+ return self._process_sql_statement(sql), parameters
77
+
78
+ processed_sql = ""
79
+ processed_params_list: list[Any] = []
80
+ last_end = 0
81
+ found_params: set[str] = set()
82
+
83
+ for match in PARAM_REGEX.finditer(sql):
84
+ if match.group("dquote") is not None or match.group("squote") is not None:
85
+ # Skip placeholders within quotes
86
+ continue
87
+
88
+ # Get name from whichever group matched
89
+ var_name = match.group("var_name_colon") or match.group("var_name_perc")
90
+
91
+ if var_name is None: # Should not happen with the new regex structure
92
+ continue
93
+
94
+ if var_name not in parameters:
95
+ placeholder = match.group(0) # Get the full matched placeholder
96
+ msg = f"Named parameter '{placeholder}' found in SQL but not provided in parameters dictionary."
97
+ raise ValueError(msg)
98
+
99
+ # Append segment before the placeholder
100
+ processed_sql += sql[last_end : match.start()]
101
+ # Append the driver's positional placeholder
102
+ processed_sql += self.param_style
103
+ processed_params_list.append(parameters[var_name])
104
+ found_params.add(var_name)
105
+ last_end = match.end()
106
+
107
+ # Append the rest of the SQL string
108
+ processed_sql += sql[last_end:]
109
+
110
+ # Check if all provided parameters were used
111
+ unused_params = set(parameters.keys()) - found_params
112
+ if unused_params:
113
+ msg = f"Parameters provided but not found in SQL: {unused_params}"
114
+ # Depending on desired strictness, this could be a warning or an error
115
+ # For now, let's raise an error for clarity
116
+ raise ValueError(msg)
117
+
118
+ return self._process_sql_statement(processed_sql), tuple(processed_params_list)
119
+
120
+ def select(
121
+ self,
122
+ sql: str,
123
+ parameters: Optional["StatementParameterType"] = None,
124
+ /,
125
+ connection: Optional["Connection"] = None,
126
+ schema_type: "Optional[type[ModelDTOT]]" = None,
127
+ ) -> "list[Union[ModelDTOT, dict[str, Any]]]":
128
+ """Fetch data from the database.
129
+
130
+ Returns:
131
+ List of row data as either model instances or dictionaries.
132
+ """
133
+ connection = self._connection(connection)
134
+ sql, parameters = self._process_sql_params(sql, parameters)
135
+ with self._with_cursor(connection) as cursor:
136
+ cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
137
+ results = cursor.fetchall() # pyright: ignore
138
+ if not results:
139
+ return []
140
+
141
+ column_names = [col[0] for col in cursor.description or []] # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
142
+
143
+ if schema_type is not None:
144
+ return [cast("ModelDTOT", schema_type(**dict(zip(column_names, row)))) for row in results] # pyright: ignore[reportUnknownArgumentType,reportUnknownVariableType]
145
+ return [dict(zip(column_names, row)) for row in results] # pyright: ignore[reportUnknownArgumentType,reportUnknownVariableType]
146
+
147
+ def select_one(
148
+ self,
149
+ sql: str,
150
+ parameters: Optional["StatementParameterType"] = None,
151
+ /,
152
+ connection: Optional["Connection"] = None,
153
+ schema_type: "Optional[type[ModelDTOT]]" = None,
154
+ ) -> "Union[ModelDTOT, dict[str, Any]]":
155
+ """Fetch one row from the database.
156
+
157
+ Returns:
158
+ The first row of the query results.
159
+ """
160
+ connection = self._connection(connection)
161
+ sql, parameters = self._process_sql_params(sql, parameters)
162
+ with self._with_cursor(connection) as cursor:
163
+ cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
164
+ result = cursor.fetchone() # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
165
+ result = self.check_not_found(result) # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType,reportUnknownArgumentType]
166
+ column_names = [c[0] for c in cursor.description or []] # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
167
+ if schema_type is None:
168
+ return dict(zip(column_names, result)) # pyright: ignore[reportUnknownArgumentType, reportUnknownVariableType]
169
+ return schema_type(**dict(zip(column_names, result))) # type: ignore[return-value]
170
+
171
+ def select_one_or_none(
172
+ self,
173
+ sql: str,
174
+ parameters: Optional["StatementParameterType"] = None,
175
+ /,
176
+ connection: Optional["Connection"] = None,
177
+ schema_type: "Optional[type[ModelDTOT]]" = None,
178
+ ) -> "Optional[Union[ModelDTOT, dict[str, Any]]]":
179
+ """Fetch one row from the database.
180
+
181
+ Returns:
182
+ The first row of the query results.
183
+ """
184
+ connection = self._connection(connection)
185
+ sql, parameters = self._process_sql_params(sql, parameters)
186
+ with self._with_cursor(connection) as cursor:
187
+ cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
188
+ result = cursor.fetchone() # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
189
+ if result is None:
190
+ return None
191
+ column_names = [c[0] for c in cursor.description or []] # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
192
+ if schema_type is None:
193
+ return dict(zip(column_names, result)) # pyright: ignore[reportUnknownArgumentType, reportUnknownVariableType]
194
+ return schema_type(**dict(zip(column_names, result))) # type: ignore[return-value]
195
+
196
+ def select_value(
197
+ self,
198
+ sql: str,
199
+ parameters: Optional["StatementParameterType"] = None,
200
+ /,
201
+ connection: Optional["Connection"] = None,
202
+ schema_type: "Optional[type[T]]" = None,
203
+ ) -> "Union[T, Any]":
204
+ """Fetch a single value from the database.
205
+
206
+ Returns:
207
+ The first value from the first row of results, or None if no results.
208
+ """
209
+ connection = self._connection(connection)
210
+ sql, parameters = self._process_sql_params(sql, parameters)
211
+ with self._with_cursor(connection) as cursor:
212
+ cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
213
+ result = cursor.fetchone() # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
214
+ result = self.check_not_found(result) # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType,reportUnknownArgumentType]
215
+ if schema_type is None:
216
+ return result[0] # pyright: ignore[reportUnknownVariableType]
217
+ return schema_type(result[0]) # type: ignore[call-arg]
218
+
219
+ def select_value_or_none(
220
+ self,
221
+ sql: str,
222
+ parameters: Optional["StatementParameterType"] = None,
223
+ /,
224
+ connection: Optional["Connection"] = None,
225
+ schema_type: "Optional[type[T]]" = None,
226
+ ) -> "Optional[Union[T, Any]]":
227
+ """Fetch a single value from the database.
228
+
229
+ Returns:
230
+ The first value from the first row of results, or None if no results.
231
+ """
232
+ connection = self._connection(connection)
233
+ sql, parameters = self._process_sql_params(sql, parameters)
234
+ with self._with_cursor(connection) as cursor:
235
+ cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
236
+ result = cursor.fetchone() # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
237
+ if result is None:
238
+ return None
239
+ if schema_type is None:
240
+ return result[0] # pyright: ignore[reportUnknownVariableType]
241
+ return schema_type(result[0]) # type: ignore[call-arg]
242
+
243
+ def insert_update_delete(
244
+ self,
245
+ sql: str,
246
+ parameters: Optional["StatementParameterType"] = None,
247
+ /,
248
+ connection: Optional["Connection"] = None,
249
+ ) -> int:
250
+ """Insert, update, or delete data from the database.
251
+
252
+ Returns:
253
+ Row count affected by the operation.
254
+ """
255
+ connection = self._connection(connection)
256
+ sql, parameters = self._process_sql_params(sql, parameters)
257
+
258
+ with self._with_cursor(connection) as cursor:
259
+ cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
260
+ return cursor.rowcount if hasattr(cursor, "rowcount") else -1
261
+
262
+ def insert_update_delete_returning(
263
+ self,
264
+ sql: str,
265
+ parameters: Optional["StatementParameterType"] = None,
266
+ /,
267
+ connection: Optional["Connection"] = None,
268
+ schema_type: "Optional[type[ModelDTOT]]" = None,
269
+ ) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
270
+ """Insert, update, or delete data from the database and return result.
271
+
272
+ Returns:
273
+ The first row of results.
274
+ """
275
+ connection = self._connection(connection)
276
+ sql, parameters = self._process_sql_params(sql, parameters)
277
+ column_names: list[str] = []
278
+
279
+ with self._with_cursor(connection) as cursor:
280
+ cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
281
+ result = cursor.fetchall() # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
282
+ if len(result) == 0: # pyright: ignore[reportUnknownArgumentType]
283
+ return None
284
+ column_names = [c[0] for c in cursor.description or []] # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
285
+ if schema_type is not None:
286
+ return cast("ModelDTOT", schema_type(**dict(zip(column_names, result[0])))) # pyright: ignore[reportUnknownArgumentType]
287
+ return dict(zip(column_names, result[0])) # pyright: ignore[reportUnknownVariableType,reportUnknownArgumentType]
288
+
289
+ def execute_script(
290
+ self,
291
+ sql: str,
292
+ parameters: Optional["StatementParameterType"] = None,
293
+ /,
294
+ connection: Optional["Connection"] = None,
295
+ ) -> str:
296
+ """Execute a script.
297
+
298
+ Returns:
299
+ Status message for the operation.
300
+ """
301
+ connection = self._connection(connection)
302
+ sql, parameters = self._process_sql_params(sql, parameters)
303
+
304
+ with self._with_cursor(connection) as cursor:
305
+ cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
306
+ return cast("str", cursor.statusmessage) if hasattr(cursor, "statusmessage") else "DONE" # pyright: ignore[reportUnknownMemberType,reportAttributeAccessIssue]
307
+
308
+ def execute_script_returning(
309
+ self,
310
+ sql: str,
311
+ parameters: Optional["StatementParameterType"] = None,
312
+ /,
313
+ connection: Optional["Connection"] = None,
314
+ schema_type: "Optional[type[ModelDTOT]]" = None,
315
+ ) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
316
+ """Execute a script and return result.
317
+
318
+ Returns:
319
+ The first row of results.
320
+ """
321
+ connection = self._connection(connection)
322
+ sql, parameters = self._process_sql_params(sql, parameters)
323
+ column_names: list[str] = []
324
+
325
+ with self._with_cursor(connection) as cursor:
326
+ cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
327
+ result = cursor.fetchall() # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
328
+ if len(result) == 0: # pyright: ignore[reportUnknownArgumentType]
329
+ return None
330
+ column_names = [c[0] for c in cursor.description or []] # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
331
+ if schema_type is not None:
332
+ return cast("ModelDTOT", schema_type(**dict(zip(column_names, result[0])))) # pyright: ignore[reportUnknownArgumentType]
333
+ return dict(zip(column_names, result[0])) # pyright: ignore[reportUnknownArgumentType, reportUnknownVariableType]
@@ -1,3 +1,7 @@
1
- from sqlspec.adapters.aiosqlite.config import AiosqliteConfig
1
+ from sqlspec.adapters.aiosqlite.config import Aiosqlite
2
+ from sqlspec.adapters.aiosqlite.driver import AiosqliteDriver
2
3
 
3
- __all__ = ("AiosqliteConfig",)
4
+ __all__ = (
5
+ "Aiosqlite",
6
+ "AiosqliteDriver",
7
+ )