sqlspec 0.8.0__py3-none-any.whl → 0.9.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 (45) hide show
  1. sqlspec/_typing.py +39 -6
  2. sqlspec/adapters/adbc/__init__.py +2 -2
  3. sqlspec/adapters/adbc/config.py +34 -11
  4. sqlspec/adapters/adbc/driver.py +167 -108
  5. sqlspec/adapters/aiosqlite/__init__.py +2 -2
  6. sqlspec/adapters/aiosqlite/config.py +2 -2
  7. sqlspec/adapters/aiosqlite/driver.py +28 -39
  8. sqlspec/adapters/asyncmy/__init__.py +3 -3
  9. sqlspec/adapters/asyncmy/config.py +11 -12
  10. sqlspec/adapters/asyncmy/driver.py +25 -34
  11. sqlspec/adapters/asyncpg/__init__.py +5 -5
  12. sqlspec/adapters/asyncpg/config.py +17 -19
  13. sqlspec/adapters/asyncpg/driver.py +249 -93
  14. sqlspec/adapters/duckdb/__init__.py +2 -2
  15. sqlspec/adapters/duckdb/config.py +2 -2
  16. sqlspec/adapters/duckdb/driver.py +49 -49
  17. sqlspec/adapters/oracledb/__init__.py +8 -8
  18. sqlspec/adapters/oracledb/config/__init__.py +6 -6
  19. sqlspec/adapters/oracledb/config/_asyncio.py +9 -10
  20. sqlspec/adapters/oracledb/config/_sync.py +8 -9
  21. sqlspec/adapters/oracledb/driver.py +114 -41
  22. sqlspec/adapters/psqlpy/__init__.py +0 -0
  23. sqlspec/adapters/psqlpy/config.py +258 -0
  24. sqlspec/adapters/psqlpy/driver.py +335 -0
  25. sqlspec/adapters/psycopg/__init__.py +10 -5
  26. sqlspec/adapters/psycopg/config/__init__.py +6 -6
  27. sqlspec/adapters/psycopg/config/_async.py +12 -12
  28. sqlspec/adapters/psycopg/config/_sync.py +13 -13
  29. sqlspec/adapters/psycopg/driver.py +180 -218
  30. sqlspec/adapters/sqlite/__init__.py +2 -2
  31. sqlspec/adapters/sqlite/config.py +2 -2
  32. sqlspec/adapters/sqlite/driver.py +43 -41
  33. sqlspec/base.py +275 -153
  34. sqlspec/exceptions.py +30 -0
  35. sqlspec/extensions/litestar/config.py +6 -0
  36. sqlspec/extensions/litestar/handlers.py +25 -0
  37. sqlspec/extensions/litestar/plugin.py +6 -1
  38. sqlspec/statement.py +373 -0
  39. sqlspec/typing.py +10 -1
  40. {sqlspec-0.8.0.dist-info → sqlspec-0.9.0.dist-info}/METADATA +4 -1
  41. sqlspec-0.9.0.dist-info/RECORD +61 -0
  42. sqlspec-0.8.0.dist-info/RECORD +0 -57
  43. {sqlspec-0.8.0.dist-info → sqlspec-0.9.0.dist-info}/WHEEL +0 -0
  44. {sqlspec-0.8.0.dist-info → sqlspec-0.9.0.dist-info}/licenses/LICENSE +0 -0
  45. {sqlspec-0.8.0.dist-info → sqlspec-0.9.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/_typing.py CHANGED
@@ -1,8 +1,10 @@
1
+ # ruff: noqa: RUF100, PLR0913, A002, DOC201, PLR6301
1
2
  """This is a simple wrapper around a few important classes in each library.
2
3
 
3
4
  This is used to ensure compatibility when one or more of the libraries are installed.
4
5
  """
5
6
 
7
+ from collections.abc import Iterable, Mapping
6
8
  from enum import Enum
7
9
  from typing import (
8
10
  Any,
@@ -96,7 +98,7 @@ except ImportError:
96
98
 
97
99
  def validate_python(
98
100
  self,
99
- object: Any, # noqa: A002
101
+ object: Any,
100
102
  /,
101
103
  *,
102
104
  strict: "Optional[bool]" = None,
@@ -127,10 +129,7 @@ try:
127
129
  except ImportError:
128
130
  import enum
129
131
  from collections.abc import Iterable
130
- from typing import TYPE_CHECKING, Callable, Optional, Union
131
-
132
- if TYPE_CHECKING:
133
- from collections.abc import Iterable
132
+ from typing import Callable, Optional, Union
134
133
 
135
134
  @dataclass_transform()
136
135
  @runtime_checkable
@@ -174,7 +173,6 @@ except ImportError:
174
173
  """Placeholder init"""
175
174
 
176
175
  def create_instance(self, **kwargs: Any) -> "T":
177
- """Placeholder implementation"""
178
176
  return cast("T", kwargs)
179
177
 
180
178
  def update_instance(self, instance: "T", **kwargs: Any) -> "T":
@@ -198,11 +196,46 @@ EmptyType = Union[Literal[EmptyEnum.EMPTY], UnsetType]
198
196
  Empty: Final = EmptyEnum.EMPTY
199
197
 
200
198
 
199
+ try:
200
+ from pyarrow import Table as ArrowTable
201
+
202
+ PYARROW_INSTALLED = True
203
+ except ImportError:
204
+
205
+ @runtime_checkable
206
+ class ArrowTable(Protocol): # type: ignore[no-redef]
207
+ """Placeholder Implementation"""
208
+
209
+ def to_batches(self, batch_size: int) -> Any: ...
210
+ def num_rows(self) -> int: ...
211
+ def num_columns(self) -> int: ...
212
+ def to_pydict(self) -> dict[str, Any]: ...
213
+ def to_string(self) -> str: ...
214
+ def from_arrays(
215
+ self,
216
+ arrays: list[Any],
217
+ names: "Optional[list[str]]" = None,
218
+ schema: "Optional[Any]" = None,
219
+ metadata: "Optional[Mapping[str, Any]]" = None,
220
+ ) -> Any: ...
221
+ def from_pydict(
222
+ self,
223
+ mapping: dict[str, Any],
224
+ schema: "Optional[Any]" = None,
225
+ metadata: "Optional[Mapping[str, Any]]" = None,
226
+ ) -> Any: ...
227
+ def from_batches(self, batches: Iterable[Any], schema: Optional[Any] = None) -> Any: ...
228
+
229
+ PYARROW_INSTALLED = False # pyright: ignore[reportConstantRedefinition]
230
+
231
+
201
232
  __all__ = (
202
233
  "LITESTAR_INSTALLED",
203
234
  "MSGSPEC_INSTALLED",
235
+ "PYARROW_INSTALLED",
204
236
  "PYDANTIC_INSTALLED",
205
237
  "UNSET",
238
+ "ArrowTable",
206
239
  "BaseModel",
207
240
  "DTOData",
208
241
  "DataclassProtocol",
@@ -1,7 +1,7 @@
1
- from sqlspec.adapters.adbc.config import Adbc
1
+ from sqlspec.adapters.adbc.config import AdbcConfig
2
2
  from sqlspec.adapters.adbc.driver import AdbcDriver
3
3
 
4
4
  __all__ = (
5
- "Adbc",
5
+ "AdbcConfig",
6
6
  "AdbcDriver",
7
7
  )
@@ -14,11 +14,11 @@ if TYPE_CHECKING:
14
14
  from collections.abc import Generator
15
15
 
16
16
 
17
- __all__ = ("Adbc",)
17
+ __all__ = ("AdbcConfig",)
18
18
 
19
19
 
20
20
  @dataclass
21
- class Adbc(NoPoolSyncConfig["Connection", "AdbcDriver"]):
21
+ class AdbcConfig(NoPoolSyncConfig["Connection", "AdbcDriver"]):
22
22
  """Configuration for ADBC connections.
23
23
 
24
24
  This class provides configuration options for ADBC database connections using the
@@ -55,17 +55,41 @@ class Adbc(NoPoolSyncConfig["Connection", "AdbcDriver"]):
55
55
  """
56
56
 
57
57
  if isinstance(self.driver_name, str):
58
- if self.driver_name != "adbc_driver_sqlite.dbapi.connect" and "sqlite" in self.driver_name:
58
+ if self.driver_name != "adbc_driver_sqlite.dbapi.connect" and self.driver_name in {
59
+ "sqlite",
60
+ "sqlite3",
61
+ "adbc_driver_sqlite",
62
+ }:
59
63
  self.driver_name = "adbc_driver_sqlite.dbapi.connect"
60
- elif self.driver_name != "adbc_driver_duckdb.dbapi.connect" and "duckdb" in self.driver_name:
64
+ elif self.driver_name != "adbc_driver_duckdb.dbapi.connect" and self.driver_name in {
65
+ "duckdb",
66
+ "adbc_driver_duckdb",
67
+ }:
61
68
  self.driver_name = "adbc_driver_duckdb.dbapi.connect"
62
- elif self.driver_name != "adbc_driver_postgresql.dbapi.connect" and "postgres" in self.driver_name:
69
+ elif self.driver_name != "adbc_driver_postgresql.dbapi.connect" and self.driver_name in {
70
+ "postgres",
71
+ "adbc_driver_postgresql",
72
+ "postgresql",
73
+ "pg",
74
+ }:
63
75
  self.driver_name = "adbc_driver_postgresql.dbapi.connect"
64
- elif self.driver_name != "adbc_driver_snowflake.dbapi.connect" and "snowflake" in self.driver_name:
76
+ elif self.driver_name != "adbc_driver_snowflake.dbapi.connect" and self.driver_name in {
77
+ "snowflake",
78
+ "adbc_driver_snowflake",
79
+ "sf",
80
+ }:
65
81
  self.driver_name = "adbc_driver_snowflake.dbapi.connect"
66
- elif self.driver_name != "adbc_driver_bigquery.dbapi.connect" and "bigquery" in self.driver_name:
82
+ elif self.driver_name != "adbc_driver_bigquery.dbapi.connect" and self.driver_name in {
83
+ "bigquery",
84
+ "adbc_driver_bigquery",
85
+ "bq",
86
+ }:
67
87
  self.driver_name = "adbc_driver_bigquery.dbapi.connect"
68
- elif self.driver_name != "adbc_driver_flightsql.dbapi.connect" and "flightsql" in self.driver_name:
88
+ elif self.driver_name != "adbc_driver_flightsql.dbapi.connect" and self.driver_name in {
89
+ "flightsql",
90
+ "adbc_driver_flightsql",
91
+ "grpc",
92
+ }:
69
93
  self.driver_name = "adbc_driver_flightsql.dbapi.connect"
70
94
  return self.driver_name
71
95
 
@@ -153,11 +177,10 @@ class Adbc(NoPoolSyncConfig["Connection", "AdbcDriver"]):
153
177
  """
154
178
  try:
155
179
  connect_func = self._get_connect_func()
156
- _config = self.connection_config_dict
157
- return connect_func(**_config)
180
+ return connect_func(**self.connection_config_dict)
158
181
  except Exception as e:
159
182
  # Include driver name in error message for better context
160
- driver_name = self.driver_name if isinstance(self.driver_name, str) else "Unknown/Derived"
183
+ driver_name = self.driver_name if isinstance(self.driver_name, str) else "Unknown/Missing"
161
184
  # Use the potentially modified driver_path from _get_connect_func if available,
162
185
  # otherwise fallback to self.driver_name for the error message.
163
186
  # This requires _get_connect_func to potentially return the used path or store it.
@@ -1,41 +1,73 @@
1
1
  import contextlib
2
+ import logging
2
3
  import re
3
4
  from collections.abc import Generator
4
5
  from contextlib import contextmanager
5
- from typing import TYPE_CHECKING, Any, Optional, Union, cast
6
+ from typing import TYPE_CHECKING, Any, ClassVar, Optional, Union, cast
6
7
 
7
8
  from adbc_driver_manager.dbapi import Connection, Cursor
8
9
 
9
- from sqlspec.base import SyncDriverAdapterProtocol, T
10
+ from sqlspec.base import SyncArrowBulkOperationsMixin, SyncDriverAdapterProtocol, T
11
+ from sqlspec.exceptions import ParameterStyleMismatchError, SQLParsingError
12
+ from sqlspec.statement import SQLStatement
13
+ from sqlspec.typing import ArrowTable, StatementParameterType
10
14
 
11
15
  if TYPE_CHECKING:
12
- from sqlspec.typing import ModelDTOT, StatementParameterType
16
+ from sqlspec.typing import ArrowTable, ModelDTOT, StatementParameterType
13
17
 
14
18
  __all__ = ("AdbcDriver",)
15
19
 
20
+ logger = logging.getLogger("sqlspec")
21
+
16
22
 
17
- # Regex to find :param or %(param)s style placeholders, skipping those inside quotes
18
23
  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
+ r"""(?<![:\w\$]) # Avoid matching ::, \:, etc. and other vendor prefixes
25
+ (?:
26
+ (?P<dquote>"(?:[^"]|"")*") | # Double-quoted strings
27
+ (?P<squote>'(?:[^']|'')*') | # Single-quoted strings
28
+ (?P<comment>--.*?\n|\/\*.*?\*\/) | # SQL comments
29
+ (?P<lead>[:\$])(?P<var_name>[a-zA-Z_][a-zA-Z0-9_]*) # :name or $name identifier
30
+ )
24
31
  """,
25
- re.VERBOSE,
32
+ re.VERBOSE | re.DOTALL,
26
33
  )
27
34
 
28
35
 
29
- class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
36
+ class AdbcDriver(SyncArrowBulkOperationsMixin["Connection"], SyncDriverAdapterProtocol["Connection"]):
30
37
  """ADBC Sync Driver Adapter."""
31
38
 
32
39
  connection: Connection
40
+ __supports_arrow__: ClassVar[bool] = True
33
41
 
34
42
  def __init__(self, connection: "Connection") -> None:
35
43
  """Initialize the ADBC driver adapter."""
36
44
  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
45
+ self.dialect = self._get_dialect(connection)
46
+
47
+ @staticmethod
48
+ def _get_dialect(connection: "Connection") -> str: # noqa: PLR0911
49
+ """Get the database dialect based on the driver name.
50
+
51
+ Args:
52
+ connection: The ADBC connection object.
53
+
54
+ Returns:
55
+ The database dialect.
56
+ """
57
+ driver_name = connection.adbc_get_info()["vendor_name"].lower()
58
+ if "postgres" in driver_name:
59
+ return "postgres"
60
+ if "bigquery" in driver_name:
61
+ return "bigquery"
62
+ if "sqlite" in driver_name:
63
+ return "sqlite"
64
+ if "duckdb" in driver_name:
65
+ return "duckdb"
66
+ if "mysql" in driver_name:
67
+ return "mysql"
68
+ if "snowflake" in driver_name:
69
+ return "snowflake"
70
+ return "postgres" # default to postgresql dialect
39
71
 
40
72
  @staticmethod
41
73
  def _cursor(connection: "Connection", *args: Any, **kwargs: Any) -> "Cursor":
@@ -51,79 +83,92 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
51
83
  cursor.close() # type: ignore[no-untyped-call]
52
84
 
53
85
  def _process_sql_params(
54
- self, sql: str, parameters: "Optional[StatementParameterType]" = None
86
+ self,
87
+ sql: str,
88
+ parameters: "Optional[StatementParameterType]" = None,
89
+ /,
90
+ **kwargs: Any,
55
91
  ) -> "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)
92
+ # Determine effective parameter type *before* calling SQLStatement
93
+ merged_params_type = dict if kwargs else type(parameters)
94
+
95
+ # If ADBC + sqlite/duckdb + dictionary params, handle conversion manually
96
+ if self.dialect in {"sqlite", "duckdb"} and merged_params_type is dict:
97
+ logger.debug(
98
+ "ADBC/%s with dict params; bypassing SQLStatement conversion, manually converting to '?' positional.",
99
+ self.dialect,
100
+ )
101
+
102
+ # Combine parameters and kwargs into the actual dictionary to use
103
+ parameter_dict = {} # type: ignore[var-annotated]
104
+ if isinstance(parameters, dict):
105
+ parameter_dict.update(parameters)
106
+ if kwargs:
107
+ parameter_dict.update(kwargs)
108
+
109
+ # Define regex locally to find :name or $name
110
+
111
+ processed_sql_parts: list[str] = []
112
+ ordered_params = []
113
+ last_end = 0
114
+ found_params_regex: list[str] = []
115
+
116
+ for match in PARAM_REGEX.finditer(sql): # Use original sql
117
+ if match.group("dquote") or match.group("squote") or match.group("comment"):
118
+ continue
119
+
120
+ if match.group("var_name"):
121
+ var_name = match.group("var_name")
122
+ leading_char = match.group("lead") # : or $
123
+ found_params_regex.append(var_name)
124
+ # Use match span directly for replacement
125
+ start = match.start()
126
+ end = match.end()
127
+
128
+ if var_name not in parameter_dict:
129
+ msg = f"Named parameter '{leading_char}{var_name}' found in SQL but not provided. SQL: {sql}"
130
+ raise SQLParsingError(msg)
131
+
132
+ processed_sql_parts.extend((sql[last_end:start], "?")) # Force ? style
133
+ ordered_params.append(parameter_dict[var_name])
134
+ last_end = end
135
+
136
+ processed_sql_parts.append(sql[last_end:])
137
+
138
+ if not found_params_regex and parameter_dict:
139
+ msg = f"ADBC/{self.dialect}: Dict params provided, but no :name or $name placeholders found. SQL: {sql}"
140
+ raise ParameterStyleMismatchError(msg)
141
+
142
+ # Key validation
143
+ provided_keys = set(parameter_dict.keys())
144
+ missing_keys = set(found_params_regex) - provided_keys
145
+ if missing_keys:
146
+ msg = (
147
+ f"Named parameters found in SQL ({found_params_regex}) but not provided: {missing_keys}. SQL: {sql}"
148
+ )
149
+ raise SQLParsingError(msg)
150
+ extra_keys = provided_keys - set(found_params_regex)
151
+ if extra_keys:
152
+ logger.debug("Extra parameters provided for ADBC/%s: %s", self.dialect, extra_keys)
153
+ # Allow extra keys
154
+
155
+ final_sql = "".join(processed_sql_parts)
156
+ final_params = tuple(ordered_params)
157
+ return final_sql, final_params
158
+ # For all other cases (other dialects, or non-dict params for sqlite/duckdb),
159
+ # use the standard SQLStatement processing.
160
+ stmt = SQLStatement(sql=sql, parameters=parameters, dialect=self.dialect, kwargs=kwargs or None)
161
+ return stmt.process()
119
162
 
120
163
  def select(
121
164
  self,
122
165
  sql: str,
123
166
  parameters: Optional["StatementParameterType"] = None,
124
167
  /,
168
+ *,
125
169
  connection: Optional["Connection"] = None,
126
170
  schema_type: "Optional[type[ModelDTOT]]" = None,
171
+ **kwargs: Any,
127
172
  ) -> "list[Union[ModelDTOT, dict[str, Any]]]":
128
173
  """Fetch data from the database.
129
174
 
@@ -131,7 +176,7 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
131
176
  List of row data as either model instances or dictionaries.
132
177
  """
133
178
  connection = self._connection(connection)
134
- sql, parameters = self._process_sql_params(sql, parameters)
179
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
135
180
  with self._with_cursor(connection) as cursor:
136
181
  cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
137
182
  results = cursor.fetchall() # pyright: ignore
@@ -149,8 +194,10 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
149
194
  sql: str,
150
195
  parameters: Optional["StatementParameterType"] = None,
151
196
  /,
197
+ *,
152
198
  connection: Optional["Connection"] = None,
153
199
  schema_type: "Optional[type[ModelDTOT]]" = None,
200
+ **kwargs: Any,
154
201
  ) -> "Union[ModelDTOT, dict[str, Any]]":
155
202
  """Fetch one row from the database.
156
203
 
@@ -158,7 +205,7 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
158
205
  The first row of the query results.
159
206
  """
160
207
  connection = self._connection(connection)
161
- sql, parameters = self._process_sql_params(sql, parameters)
208
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
162
209
  with self._with_cursor(connection) as cursor:
163
210
  cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
164
211
  result = cursor.fetchone() # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
@@ -173,8 +220,10 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
173
220
  sql: str,
174
221
  parameters: Optional["StatementParameterType"] = None,
175
222
  /,
223
+ *,
176
224
  connection: Optional["Connection"] = None,
177
225
  schema_type: "Optional[type[ModelDTOT]]" = None,
226
+ **kwargs: Any,
178
227
  ) -> "Optional[Union[ModelDTOT, dict[str, Any]]]":
179
228
  """Fetch one row from the database.
180
229
 
@@ -182,7 +231,7 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
182
231
  The first row of the query results.
183
232
  """
184
233
  connection = self._connection(connection)
185
- sql, parameters = self._process_sql_params(sql, parameters)
234
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
186
235
  with self._with_cursor(connection) as cursor:
187
236
  cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
188
237
  result = cursor.fetchone() # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
@@ -198,8 +247,10 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
198
247
  sql: str,
199
248
  parameters: Optional["StatementParameterType"] = None,
200
249
  /,
250
+ *,
201
251
  connection: Optional["Connection"] = None,
202
252
  schema_type: "Optional[type[T]]" = None,
253
+ **kwargs: Any,
203
254
  ) -> "Union[T, Any]":
204
255
  """Fetch a single value from the database.
205
256
 
@@ -207,7 +258,7 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
207
258
  The first value from the first row of results, or None if no results.
208
259
  """
209
260
  connection = self._connection(connection)
210
- sql, parameters = self._process_sql_params(sql, parameters)
261
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
211
262
  with self._with_cursor(connection) as cursor:
212
263
  cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
213
264
  result = cursor.fetchone() # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
@@ -221,8 +272,10 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
221
272
  sql: str,
222
273
  parameters: Optional["StatementParameterType"] = None,
223
274
  /,
275
+ *,
224
276
  connection: Optional["Connection"] = None,
225
277
  schema_type: "Optional[type[T]]" = None,
278
+ **kwargs: Any,
226
279
  ) -> "Optional[Union[T, Any]]":
227
280
  """Fetch a single value from the database.
228
281
 
@@ -230,7 +283,7 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
230
283
  The first value from the first row of results, or None if no results.
231
284
  """
232
285
  connection = self._connection(connection)
233
- sql, parameters = self._process_sql_params(sql, parameters)
286
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
234
287
  with self._with_cursor(connection) as cursor:
235
288
  cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
236
289
  result = cursor.fetchone() # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
@@ -245,7 +298,9 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
245
298
  sql: str,
246
299
  parameters: Optional["StatementParameterType"] = None,
247
300
  /,
301
+ *,
248
302
  connection: Optional["Connection"] = None,
303
+ **kwargs: Any,
249
304
  ) -> int:
250
305
  """Insert, update, or delete data from the database.
251
306
 
@@ -253,7 +308,7 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
253
308
  Row count affected by the operation.
254
309
  """
255
310
  connection = self._connection(connection)
256
- sql, parameters = self._process_sql_params(sql, parameters)
311
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
257
312
 
258
313
  with self._with_cursor(connection) as cursor:
259
314
  cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
@@ -264,8 +319,10 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
264
319
  sql: str,
265
320
  parameters: Optional["StatementParameterType"] = None,
266
321
  /,
322
+ *,
267
323
  connection: Optional["Connection"] = None,
268
324
  schema_type: "Optional[type[ModelDTOT]]" = None,
325
+ **kwargs: Any,
269
326
  ) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
270
327
  """Insert, update, or delete data from the database and return result.
271
328
 
@@ -273,25 +330,31 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
273
330
  The first row of results.
274
331
  """
275
332
  connection = self._connection(connection)
276
- sql, parameters = self._process_sql_params(sql, parameters)
277
- column_names: list[str] = []
278
-
333
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
279
334
  with self._with_cursor(connection) as cursor:
280
335
  cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
281
336
  result = cursor.fetchall() # pyright: ignore[reportUnknownMemberType,reportUnknownVariableType]
282
- if len(result) == 0: # pyright: ignore[reportUnknownArgumentType]
337
+ if not result:
283
338
  return None
339
+
340
+ first_row = result[0]
341
+
284
342
  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]
343
+
344
+ result_dict = dict(zip(column_names, first_row))
345
+
346
+ if schema_type is None:
347
+ return result_dict
348
+ return cast("ModelDTOT", schema_type(**result_dict))
288
349
 
289
350
  def execute_script(
290
351
  self,
291
352
  sql: str,
292
353
  parameters: Optional["StatementParameterType"] = None,
293
354
  /,
355
+ *,
294
356
  connection: Optional["Connection"] = None,
357
+ **kwargs: Any,
295
358
  ) -> str:
296
359
  """Execute a script.
297
360
 
@@ -305,29 +368,25 @@ class AdbcDriver(SyncDriverAdapterProtocol["Connection"]):
305
368
  cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
306
369
  return cast("str", cursor.statusmessage) if hasattr(cursor, "statusmessage") else "DONE" # pyright: ignore[reportUnknownMemberType,reportAttributeAccessIssue]
307
370
 
308
- def execute_script_returning(
371
+ # --- Arrow Bulk Operations ---
372
+
373
+ def select_arrow( # pyright: ignore[reportUnknownParameterType]
309
374
  self,
310
375
  sql: str,
311
- parameters: Optional["StatementParameterType"] = None,
376
+ parameters: "Optional[StatementParameterType]" = None,
312
377
  /,
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.
378
+ *,
379
+ connection: "Optional[Connection]" = None,
380
+ **kwargs: Any,
381
+ ) -> "ArrowTable":
382
+ """Execute a SQL query and return results as an Apache Arrow Table.
317
383
 
318
384
  Returns:
319
- The first row of results.
385
+ The results of the query as an Apache Arrow Table.
320
386
  """
321
- connection = self._connection(connection)
322
- sql, parameters = self._process_sql_params(sql, parameters)
323
- column_names: list[str] = []
387
+ conn = self._connection(connection)
388
+ sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
324
389
 
325
- with self._with_cursor(connection) as cursor:
390
+ with self._with_cursor(conn) as cursor:
326
391
  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]
392
+ return cast("ArrowTable", cursor.fetch_arrow_table()) # pyright: ignore[reportUnknownMemberType]
@@ -1,7 +1,7 @@
1
- from sqlspec.adapters.aiosqlite.config import Aiosqlite
1
+ from sqlspec.adapters.aiosqlite.config import AiosqliteConfig
2
2
  from sqlspec.adapters.aiosqlite.driver import AiosqliteDriver
3
3
 
4
4
  __all__ = (
5
- "Aiosqlite",
5
+ "AiosqliteConfig",
6
6
  "AiosqliteDriver",
7
7
  )
@@ -15,11 +15,11 @@ if TYPE_CHECKING:
15
15
  from typing import Literal
16
16
 
17
17
 
18
- __all__ = ("Aiosqlite",)
18
+ __all__ = ("AiosqliteConfig",)
19
19
 
20
20
 
21
21
  @dataclass
22
- class Aiosqlite(NoPoolAsyncConfig["Connection", "AiosqliteDriver"]):
22
+ class AiosqliteConfig(NoPoolAsyncConfig["Connection", "AiosqliteDriver"]):
23
23
  """Configuration for Aiosqlite database connections.
24
24
 
25
25
  This class provides configuration options for Aiosqlite database connections, wrapping all parameters