sqlspec 0.10.1__py3-none-any.whl → 0.11.1__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.

@@ -1,23 +1,31 @@
1
+ import logging
1
2
  from contextlib import asynccontextmanager
2
- from typing import TYPE_CHECKING, Any, Optional, Union, cast, overload
3
+ from typing import TYPE_CHECKING, Any, Optional, Union, overload
3
4
 
4
5
  import aiosqlite
6
+ from sqlglot import exp
5
7
 
6
8
  from sqlspec.base import AsyncDriverAdapterProtocol
7
- from sqlspec.mixins import SQLTranslatorMixin
9
+ from sqlspec.filters import StatementFilter
10
+ from sqlspec.mixins import ResultConverter, SQLTranslatorMixin
11
+ from sqlspec.statement import SQLStatement
12
+ from sqlspec.typing import is_dict
8
13
 
9
14
  if TYPE_CHECKING:
10
- from collections.abc import AsyncGenerator, Sequence
15
+ from collections.abc import AsyncGenerator, Mapping, Sequence # Added Mapping, Sequence
11
16
 
12
17
  from sqlspec.typing import ModelDTOT, StatementParameterType, T
13
18
 
14
19
  __all__ = ("AiosqliteConnection", "AiosqliteDriver")
15
20
  AiosqliteConnection = aiosqlite.Connection
16
21
 
22
+ logger = logging.getLogger("sqlspec")
23
+
17
24
 
18
25
  class AiosqliteDriver(
19
26
  SQLTranslatorMixin["AiosqliteConnection"],
20
27
  AsyncDriverAdapterProtocol["AiosqliteConnection"],
28
+ ResultConverter,
21
29
  ):
22
30
  """SQLite Async Driver Adapter."""
23
31
 
@@ -39,14 +47,90 @@ class AiosqliteDriver(
39
47
  finally:
40
48
  await cursor.close()
41
49
 
50
+ def _process_sql_params(
51
+ self,
52
+ sql: str,
53
+ parameters: "Optional[StatementParameterType]" = None,
54
+ *filters: "StatementFilter",
55
+ **kwargs: Any,
56
+ ) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]]]":
57
+ """Process SQL and parameters for aiosqlite using SQLStatement.
58
+
59
+ aiosqlite supports both named (:name) and positional (?) parameters.
60
+ This method processes the SQL with dialect-aware parsing and handles
61
+ parameters appropriately for aiosqlite.
62
+
63
+ Args:
64
+ sql: SQL statement.
65
+ parameters: Query parameters. Can be data or a StatementFilter.
66
+ *filters: Statement filters to apply.
67
+ **kwargs: Additional keyword arguments.
68
+
69
+ Returns:
70
+ Tuple of processed SQL and parameters.
71
+ """
72
+ passed_parameters: Optional[Union[Mapping[str, Any], Sequence[Any]]] = None
73
+ combined_filters_list: list[StatementFilter] = list(filters)
74
+
75
+ if parameters is not None:
76
+ if isinstance(parameters, StatementFilter):
77
+ combined_filters_list.insert(0, parameters)
78
+ # _actual_data_params remains None
79
+ else:
80
+ # If parameters is not a StatementFilter, it's actual data parameters.
81
+ passed_parameters = parameters
82
+
83
+ statement = SQLStatement(sql, passed_parameters, kwargs=kwargs, dialect=self.dialect)
84
+
85
+ for filter_obj in combined_filters_list:
86
+ statement = statement.apply_filter(filter_obj)
87
+
88
+ processed_sql, processed_params, parsed_expr = statement.process()
89
+ if processed_params is None:
90
+ return processed_sql, None
91
+
92
+ if is_dict(processed_params):
93
+ # For dict parameters, we need to use ordered ? placeholders
94
+ # but only if we have a parsed expression to work with
95
+ if parsed_expr:
96
+ # Collect named parameters in the order they appear in the SQL
97
+ named_params = []
98
+ for node in parsed_expr.find_all(exp.Parameter, exp.Placeholder):
99
+ if isinstance(node, exp.Parameter) and node.name and node.name in processed_params:
100
+ named_params.append(node.name)
101
+ elif (
102
+ isinstance(node, exp.Placeholder)
103
+ and isinstance(node.this, str)
104
+ and node.this in processed_params
105
+ ):
106
+ named_params.append(node.this)
107
+
108
+ if named_params:
109
+ # Transform SQL to use ? placeholders
110
+ def _convert_to_qmark(node: exp.Expression) -> exp.Expression:
111
+ if (isinstance(node, exp.Parameter) and node.name and node.name in processed_params) or (
112
+ isinstance(node, exp.Placeholder)
113
+ and isinstance(node.this, str)
114
+ and node.this in processed_params
115
+ ):
116
+ return exp.Placeholder() # ? placeholder
117
+ return node
118
+
119
+ return parsed_expr.transform(_convert_to_qmark, copy=True).sql(dialect=self.dialect), tuple(
120
+ processed_params[name] for name in named_params
121
+ )
122
+ return processed_sql, processed_params
123
+ if isinstance(processed_params, (list, tuple)):
124
+ return processed_sql, tuple(processed_params)
125
+ return processed_sql, (processed_params,)
126
+
42
127
  # --- Public API Methods --- #
43
128
  @overload
44
129
  async def select(
45
130
  self,
46
131
  sql: str,
47
132
  parameters: "Optional[StatementParameterType]" = None,
48
- /,
49
- *,
133
+ *filters: "StatementFilter",
50
134
  connection: "Optional[AiosqliteConnection]" = None,
51
135
  schema_type: None = None,
52
136
  **kwargs: Any,
@@ -56,8 +140,7 @@ class AiosqliteDriver(
56
140
  self,
57
141
  sql: str,
58
142
  parameters: "Optional[StatementParameterType]" = None,
59
- /,
60
- *,
143
+ *filters: "StatementFilter",
61
144
  connection: "Optional[AiosqliteConnection]" = None,
62
145
  schema_type: "type[ModelDTOT]",
63
146
  **kwargs: Any,
@@ -65,37 +148,34 @@ class AiosqliteDriver(
65
148
  async def select(
66
149
  self,
67
150
  sql: str,
68
- parameters: Optional["StatementParameterType"] = None,
69
- /,
70
- *,
71
- connection: Optional["AiosqliteConnection"] = None,
151
+ parameters: "Optional[StatementParameterType]" = None,
152
+ *filters: "StatementFilter",
153
+ connection: "Optional[AiosqliteConnection]" = None,
72
154
  schema_type: "Optional[type[ModelDTOT]]" = None,
73
155
  **kwargs: Any,
74
- ) -> "Sequence[Union[ModelDTOT, dict[str, Any]]]":
156
+ ) -> "Sequence[Union[dict[str, Any], ModelDTOT]]":
75
157
  """Fetch data from the database.
76
158
 
77
159
  Returns:
78
160
  List of row data as either model instances or dictionaries.
79
161
  """
80
162
  connection = self._connection(connection)
81
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
163
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
164
+
82
165
  async with self._with_cursor(connection) as cursor:
83
- await cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
84
- results = await cursor.fetchall() # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
166
+ await cursor.execute(sql, parameters or ())
167
+ results = await cursor.fetchall()
85
168
  if not results:
86
169
  return []
87
- column_names = [c[0] for c in cursor.description or []] # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
88
- if schema_type is None:
89
- return [dict(zip(column_names, row)) for row in results] # pyright: ignore[reportUnknownArgumentType]
90
- return [cast("ModelDTOT", schema_type(**dict(zip(column_names, row)))) for row in results] # pyright: ignore[reportUnknownArgumentType]
170
+ column_names = [column[0] for column in cursor.description]
171
+ return self.to_schema([dict(zip(column_names, row)) for row in results], schema_type=schema_type)
91
172
 
92
173
  @overload
93
174
  async def select_one(
94
175
  self,
95
176
  sql: str,
96
177
  parameters: "Optional[StatementParameterType]" = None,
97
- /,
98
- *,
178
+ *filters: "StatementFilter",
99
179
  connection: "Optional[AiosqliteConnection]" = None,
100
180
  schema_type: None = None,
101
181
  **kwargs: Any,
@@ -105,8 +185,7 @@ class AiosqliteDriver(
105
185
  self,
106
186
  sql: str,
107
187
  parameters: "Optional[StatementParameterType]" = None,
108
- /,
109
- *,
188
+ *filters: "StatementFilter",
110
189
  connection: "Optional[AiosqliteConnection]" = None,
111
190
  schema_type: "type[ModelDTOT]",
112
191
  **kwargs: Any,
@@ -114,36 +193,35 @@ class AiosqliteDriver(
114
193
  async def select_one(
115
194
  self,
116
195
  sql: str,
117
- parameters: Optional["StatementParameterType"] = None,
118
- /,
119
- *,
120
- connection: Optional["AiosqliteConnection"] = None,
196
+ parameters: "Optional[StatementParameterType]" = None,
197
+ *filters: "StatementFilter",
198
+ connection: "Optional[AiosqliteConnection]" = None,
121
199
  schema_type: "Optional[type[ModelDTOT]]" = None,
122
200
  **kwargs: Any,
123
- ) -> "Union[ModelDTOT, dict[str, Any]]":
201
+ ) -> "Union[dict[str, Any], ModelDTOT]":
124
202
  """Fetch one row from the database.
125
203
 
126
204
  Returns:
127
205
  The first row of the query results.
128
206
  """
129
207
  connection = self._connection(connection)
130
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
208
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
209
+
131
210
  async with self._with_cursor(connection) as cursor:
132
- await cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
133
- result = await cursor.fetchone() # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
211
+ await cursor.execute(sql, parameters or ())
212
+ result = await cursor.fetchone()
134
213
  result = self.check_not_found(result)
135
- column_names = [c[0] for c in cursor.description or []] # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
136
- if schema_type is None:
137
- return dict(zip(column_names, result)) # pyright: ignore[reportUnknownArgumentType, reportUnknownVariableType]
138
- return cast("ModelDTOT", schema_type(**dict(zip(column_names, result)))) # pyright: ignore[reportUnknownArgumentType]
214
+
215
+ # Get column names
216
+ column_names = [column[0] for column in cursor.description]
217
+ return self.to_schema(dict(zip(column_names, result)), schema_type=schema_type)
139
218
 
140
219
  @overload
141
220
  async def select_one_or_none(
142
221
  self,
143
222
  sql: str,
144
223
  parameters: "Optional[StatementParameterType]" = None,
145
- /,
146
- *,
224
+ *filters: "StatementFilter",
147
225
  connection: "Optional[AiosqliteConnection]" = None,
148
226
  schema_type: None = None,
149
227
  **kwargs: Any,
@@ -153,8 +231,7 @@ class AiosqliteDriver(
153
231
  self,
154
232
  sql: str,
155
233
  parameters: "Optional[StatementParameterType]" = None,
156
- /,
157
- *,
234
+ *filters: "StatementFilter",
158
235
  connection: "Optional[AiosqliteConnection]" = None,
159
236
  schema_type: "type[ModelDTOT]",
160
237
  **kwargs: Any,
@@ -162,37 +239,34 @@ class AiosqliteDriver(
162
239
  async def select_one_or_none(
163
240
  self,
164
241
  sql: str,
165
- parameters: Optional["StatementParameterType"] = None,
166
- /,
167
- *,
168
- connection: Optional["AiosqliteConnection"] = None,
242
+ parameters: "Optional[StatementParameterType]" = None,
243
+ *filters: "StatementFilter",
244
+ connection: "Optional[AiosqliteConnection]" = None,
169
245
  schema_type: "Optional[type[ModelDTOT]]" = None,
170
246
  **kwargs: Any,
171
- ) -> "Optional[Union[ModelDTOT, dict[str, Any]]]":
247
+ ) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
172
248
  """Fetch one row from the database.
173
249
 
174
250
  Returns:
175
251
  The first row of the query results.
176
252
  """
177
253
  connection = self._connection(connection)
178
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
254
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
255
+
179
256
  async with self._with_cursor(connection) as cursor:
180
- await cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
181
- result = await cursor.fetchone() # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
257
+ await cursor.execute(sql, parameters or ())
258
+ result = await cursor.fetchone()
182
259
  if result is None:
183
260
  return None
184
- column_names = [c[0] for c in cursor.description or []] # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
185
- if schema_type is None:
186
- return dict(zip(column_names, result)) # pyright: ignore[reportUnknownArgumentType, reportUnknownVariableType]
187
- return cast("ModelDTOT", schema_type(**dict(zip(column_names, result)))) # pyright: ignore[reportUnknownArgumentType]
261
+ column_names = [column[0] for column in cursor.description]
262
+ return self.to_schema(dict(zip(column_names, result)), schema_type=schema_type)
188
263
 
189
264
  @overload
190
265
  async def select_value(
191
266
  self,
192
267
  sql: str,
193
268
  parameters: "Optional[StatementParameterType]" = None,
194
- /,
195
- *,
269
+ *filters: "StatementFilter",
196
270
  connection: "Optional[AiosqliteConnection]" = None,
197
271
  schema_type: None = None,
198
272
  **kwargs: Any,
@@ -202,8 +276,7 @@ class AiosqliteDriver(
202
276
  self,
203
277
  sql: str,
204
278
  parameters: "Optional[StatementParameterType]" = None,
205
- /,
206
- *,
279
+ *filters: "StatementFilter",
207
280
  connection: "Optional[AiosqliteConnection]" = None,
208
281
  schema_type: "type[T]",
209
282
  **kwargs: Any,
@@ -212,8 +285,7 @@ class AiosqliteDriver(
212
285
  self,
213
286
  sql: str,
214
287
  parameters: "Optional[StatementParameterType]" = None,
215
- /,
216
- *,
288
+ *filters: "StatementFilter",
217
289
  connection: "Optional[AiosqliteConnection]" = None,
218
290
  schema_type: "Optional[type[T]]" = None,
219
291
  **kwargs: Any,
@@ -221,25 +293,28 @@ class AiosqliteDriver(
221
293
  """Fetch a single value from the database.
222
294
 
223
295
  Returns:
224
- The first value from the first row of results, or None if no results.
296
+ The first value from the first row of results.
225
297
  """
226
298
  connection = self._connection(connection)
227
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
299
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
300
+
228
301
  async with self._with_cursor(connection) as cursor:
229
- await cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
230
- result = await cursor.fetchone() # pyright: ignore[reportUnknownMemberType]
302
+ await cursor.execute(sql, parameters or ())
303
+ result = await cursor.fetchone()
231
304
  result = self.check_not_found(result)
305
+
306
+ # Return first value from the row
307
+ result_value = result[0]
232
308
  if schema_type is None:
233
- return result[0]
234
- return schema_type(result[0]) # type: ignore[call-arg]
309
+ return result_value
310
+ return schema_type(result_value) # type: ignore[call-arg]
235
311
 
236
312
  @overload
237
313
  async def select_value_or_none(
238
314
  self,
239
315
  sql: str,
240
316
  parameters: "Optional[StatementParameterType]" = None,
241
- /,
242
- *,
317
+ *filters: "StatementFilter",
243
318
  connection: "Optional[AiosqliteConnection]" = None,
244
319
  schema_type: None = None,
245
320
  **kwargs: Any,
@@ -249,8 +324,7 @@ class AiosqliteDriver(
249
324
  self,
250
325
  sql: str,
251
326
  parameters: "Optional[StatementParameterType]" = None,
252
- /,
253
- *,
327
+ *filters: "StatementFilter",
254
328
  connection: "Optional[AiosqliteConnection]" = None,
255
329
  schema_type: "type[T]",
256
330
  **kwargs: Any,
@@ -259,8 +333,7 @@ class AiosqliteDriver(
259
333
  self,
260
334
  sql: str,
261
335
  parameters: "Optional[StatementParameterType]" = None,
262
- /,
263
- *,
336
+ *filters: "StatementFilter",
264
337
  connection: "Optional[AiosqliteConnection]" = None,
265
338
  schema_type: "Optional[type[T]]" = None,
266
339
  **kwargs: Any,
@@ -271,23 +344,25 @@ class AiosqliteDriver(
271
344
  The first value from the first row of results, or None if no results.
272
345
  """
273
346
  connection = self._connection(connection)
274
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
347
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
348
+
275
349
  async with self._with_cursor(connection) as cursor:
276
- await cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
277
- result = await cursor.fetchone() # pyright: ignore[reportUnknownMemberType]
350
+ # Execute the query
351
+ await cursor.execute(sql, parameters or ())
352
+ result = await cursor.fetchone()
278
353
  if result is None:
279
354
  return None
355
+ result_value = result[0]
280
356
  if schema_type is None:
281
- return result[0]
282
- return schema_type(result[0]) # type: ignore[call-arg]
357
+ return result_value
358
+ return schema_type(result_value) # type: ignore[call-arg]
283
359
 
284
360
  async def insert_update_delete(
285
361
  self,
286
362
  sql: str,
287
- parameters: Optional["StatementParameterType"] = None,
288
- /,
289
- *,
290
- connection: Optional["AiosqliteConnection"] = None,
363
+ parameters: "Optional[StatementParameterType]" = None,
364
+ *filters: "StatementFilter",
365
+ connection: "Optional[AiosqliteConnection]" = None,
291
366
  **kwargs: Any,
292
367
  ) -> int:
293
368
  """Insert, update, or delete data from the database.
@@ -296,19 +371,18 @@ class AiosqliteDriver(
296
371
  Row count affected by the operation.
297
372
  """
298
373
  connection = self._connection(connection)
299
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
300
-
374
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
301
375
  async with self._with_cursor(connection) as cursor:
302
- await cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
303
- return cursor.rowcount if hasattr(cursor, "rowcount") else -1 # pyright: ignore[reportUnknownVariableType, reportGeneralTypeIssues]
376
+ # Execute the query
377
+ await cursor.execute(sql, parameters or ())
378
+ return cursor.rowcount
304
379
 
305
380
  @overload
306
381
  async def insert_update_delete_returning(
307
382
  self,
308
383
  sql: str,
309
384
  parameters: "Optional[StatementParameterType]" = None,
310
- /,
311
- *,
385
+ *filters: "StatementFilter",
312
386
  connection: "Optional[AiosqliteConnection]" = None,
313
387
  schema_type: None = None,
314
388
  **kwargs: Any,
@@ -318,8 +392,7 @@ class AiosqliteDriver(
318
392
  self,
319
393
  sql: str,
320
394
  parameters: "Optional[StatementParameterType]" = None,
321
- /,
322
- *,
395
+ *filters: "StatementFilter",
323
396
  connection: "Optional[AiosqliteConnection]" = None,
324
397
  schema_type: "type[ModelDTOT]",
325
398
  **kwargs: Any,
@@ -327,38 +400,33 @@ class AiosqliteDriver(
327
400
  async def insert_update_delete_returning(
328
401
  self,
329
402
  sql: str,
330
- parameters: Optional["StatementParameterType"] = None,
331
- /,
332
- *,
333
- connection: Optional["AiosqliteConnection"] = None,
403
+ parameters: "Optional[StatementParameterType]" = None,
404
+ *filters: "StatementFilter",
405
+ connection: "Optional[AiosqliteConnection]" = None,
334
406
  schema_type: "Optional[type[ModelDTOT]]" = None,
335
407
  **kwargs: Any,
336
- ) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
408
+ ) -> "Union[dict[str, Any], ModelDTOT]":
337
409
  """Insert, update, or delete data from the database and return result.
338
410
 
339
411
  Returns:
340
412
  The first row of results.
341
413
  """
342
414
  connection = self._connection(connection)
343
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
415
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
344
416
 
345
417
  async with self._with_cursor(connection) as cursor:
346
- await cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
347
- results = list(await cursor.fetchall()) # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
348
- if not results: # Check if empty
349
- return None
350
- column_names = [c[0] for c in cursor.description or []] # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
351
- if schema_type is not None:
352
- return cast("ModelDTOT", schema_type(**dict(zip(column_names, results[0])))) # pyright: ignore[reportUnknownArgumentType, reportUnknownVariableType]
353
- return dict(zip(column_names, results[0])) # pyright: ignore[reportUnknownArgumentType, reportUnknownVariableType]
418
+ # Execute the query
419
+ await cursor.execute(sql, parameters or ())
420
+ result = await cursor.fetchone()
421
+ result = self.check_not_found(result)
422
+ column_names = [column[0] for column in cursor.description]
423
+ return self.to_schema(dict(zip(column_names, result)), schema_type=schema_type)
354
424
 
355
425
  async def execute_script(
356
426
  self,
357
427
  sql: str,
358
- parameters: Optional["StatementParameterType"] = None,
359
- /,
360
- *,
361
- connection: Optional["AiosqliteConnection"] = None,
428
+ parameters: "Optional[StatementParameterType]" = None,
429
+ connection: "Optional[AiosqliteConnection]" = None,
362
430
  **kwargs: Any,
363
431
  ) -> str:
364
432
  """Execute a script.
@@ -370,33 +438,19 @@ class AiosqliteDriver(
370
438
  sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
371
439
 
372
440
  async with self._with_cursor(connection) as cursor:
373
- await cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
441
+ if parameters:
442
+ await cursor.execute(sql, parameters)
443
+ else:
444
+ await cursor.executescript(sql)
374
445
  return "DONE"
375
446
 
376
- async def execute_script_returning(
377
- self,
378
- sql: str,
379
- parameters: Optional["StatementParameterType"] = None,
380
- /,
381
- *,
382
- connection: Optional["AiosqliteConnection"] = None,
383
- schema_type: "Optional[type[ModelDTOT]]" = None,
384
- **kwargs: Any,
385
- ) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
386
- """Execute a script and return result.
447
+ def _connection(self, connection: "Optional[AiosqliteConnection]" = None) -> "AiosqliteConnection":
448
+ """Get the connection to use for the operation.
449
+
450
+ Args:
451
+ connection: Optional connection to use.
387
452
 
388
453
  Returns:
389
- The first row of results.
454
+ The connection to use.
390
455
  """
391
- connection = self._connection(connection)
392
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
393
-
394
- async with self._with_cursor(connection) as cursor:
395
- await cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
396
- results = list(await cursor.fetchall()) # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
397
- if not results: # Check if empty
398
- return None
399
- column_names = [c[0] for c in cursor.description or []] # pyright: ignore[reportUnknownMemberType, reportUnknownVariableType]
400
- if schema_type is not None:
401
- return cast("ModelDTOT", schema_type(**dict(zip(column_names, results[0])))) # pyright: ignore[reportUnknownArgumentType, reportUnknownVariableType]
402
- return dict(zip(column_names, results[0])) # pyright: ignore[reportUnknownArgumentType, reportUnknownVariableType]
456
+ return connection or self.connection