sqlspec 0.10.0__py3-none-any.whl → 0.11.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.

@@ -1,4 +1,5 @@
1
1
  import logging
2
+ import re
2
3
  from contextlib import asynccontextmanager, contextmanager
3
4
  from typing import TYPE_CHECKING, Any, Optional, Union, cast, overload
4
5
 
@@ -6,143 +7,102 @@ from psycopg import AsyncConnection, Connection
6
7
  from psycopg.rows import dict_row
7
8
 
8
9
  from sqlspec.base import AsyncDriverAdapterProtocol, SyncDriverAdapterProtocol
9
- from sqlspec.exceptions import SQLParsingError
10
- from sqlspec.mixins import SQLTranslatorMixin
11
- from sqlspec.statement import PARAM_REGEX, SQLStatement
10
+ from sqlspec.exceptions import ParameterStyleMismatchError
11
+ from sqlspec.mixins import ResultConverter, SQLTranslatorMixin
12
+ from sqlspec.statement import SQLStatement
13
+ from sqlspec.typing import is_dict
12
14
 
13
15
  if TYPE_CHECKING:
14
16
  from collections.abc import AsyncGenerator, Generator, Sequence
15
17
 
18
+ from sqlspec.filters import StatementFilter
16
19
  from sqlspec.typing import ModelDTOT, StatementParameterType, T
17
20
 
18
21
  logger = logging.getLogger("sqlspec")
19
22
 
20
23
  __all__ = ("PsycopgAsyncConnection", "PsycopgAsyncDriver", "PsycopgSyncConnection", "PsycopgSyncDriver")
21
24
 
25
+
26
+ NAMED_PARAMS_PATTERN = re.compile(r"(?<!:):([a-zA-Z0-9_]+)")
27
+ # Pattern matches %(name)s format while trying to avoid matches in string literals and comments
28
+ PSYCOPG_PARAMS_PATTERN = re.compile(r"(?<!'|\"|\w)%\(([a-zA-Z0-9_]+)\)s(?!'|\")")
29
+
22
30
  PsycopgSyncConnection = Connection
23
31
  PsycopgAsyncConnection = AsyncConnection
24
32
 
25
33
 
26
34
  class PsycopgDriverBase:
27
- dialect: str
35
+ dialect: str = "postgres"
28
36
 
29
37
  def _process_sql_params(
30
38
  self,
31
39
  sql: str,
32
40
  parameters: "Optional[StatementParameterType]" = None,
33
41
  /,
42
+ *filters: "StatementFilter",
34
43
  **kwargs: Any,
35
44
  ) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]]]":
36
- """Process SQL and parameters, converting :name -> %(name)s if needed."""
37
- stmt = SQLStatement(sql=sql, parameters=parameters, dialect=self.dialect, kwargs=kwargs or None)
38
- processed_sql, processed_params = stmt.process()
45
+ """Process SQL and parameters using SQLStatement with dialect support.
46
+
47
+ Args:
48
+ sql: The SQL statement to process.
49
+ parameters: The parameters to bind to the statement.
50
+ *filters: Statement filters to apply.
51
+ **kwargs: Additional keyword arguments.
52
+
53
+ Raises:
54
+ ParameterStyleMismatchError: If the parameter style is mismatched.
39
55
 
40
- if isinstance(processed_params, dict):
41
- parameter_dict = processed_params
42
- processed_sql_parts: list[str] = []
43
- last_end = 0
44
- found_params_regex: list[str] = []
56
+ Returns:
57
+ A tuple of (sql, parameters) ready for execution.
58
+ """
59
+ statement = SQLStatement(sql, parameters, kwargs=kwargs, dialect=self.dialect)
60
+
61
+ # Apply all statement filters
62
+ for filter_obj in filters:
63
+ statement = statement.apply_filter(filter_obj)
45
64
 
46
- for match in PARAM_REGEX.finditer(processed_sql):
47
- if match.group("dquote") or match.group("squote") or match.group("comment"):
48
- continue
65
+ processed_sql, processed_params, _ = statement.process()
49
66
 
50
- if match.group("var_name"):
51
- var_name = match.group("var_name")
52
- found_params_regex.append(var_name)
53
- start = match.start("var_name") - 1
54
- end = match.end("var_name")
67
+ if is_dict(processed_params):
68
+ named_params = NAMED_PARAMS_PATTERN.findall(processed_sql)
55
69
 
56
- if var_name not in parameter_dict:
57
- msg = (
58
- f"Named parameter ':{var_name}' found in SQL but missing from processed parameters. "
59
- f"Processed SQL: {processed_sql}"
60
- )
61
- raise SQLParsingError(msg)
70
+ if not named_params:
71
+ if PSYCOPG_PARAMS_PATTERN.search(processed_sql):
72
+ return processed_sql, processed_params
62
73
 
63
- processed_sql_parts.extend((processed_sql[last_end:start], f"%({var_name})s"))
64
- last_end = end
74
+ if processed_params:
75
+ msg = "psycopg: Dictionary parameters provided, but no named placeholders found in SQL."
76
+ raise ParameterStyleMismatchError(msg)
77
+ return processed_sql, None
65
78
 
66
- processed_sql_parts.append(processed_sql[last_end:])
67
- final_sql = "".join(processed_sql_parts)
79
+ # Convert named parameters to psycopg's preferred format
80
+ return NAMED_PARAMS_PATTERN.sub("%s", processed_sql), tuple(processed_params[name] for name in named_params)
68
81
 
69
- if not found_params_regex and parameter_dict:
70
- logger.warning(
71
- "Dict params provided (%s), but no :name placeholders found. SQL: %s",
72
- list(parameter_dict.keys()),
73
- processed_sql,
74
- )
75
- return processed_sql, parameter_dict
82
+ # For sequence parameters, ensure they're a tuple
83
+ if isinstance(processed_params, (list, tuple)):
84
+ return processed_sql, tuple(processed_params)
76
85
 
77
- return final_sql, parameter_dict
86
+ # For scalar parameter or None
87
+ if processed_params is not None:
88
+ return processed_sql, (processed_params,)
78
89
 
79
- return processed_sql, processed_params
90
+ return processed_sql, None
80
91
 
81
92
 
82
93
  class PsycopgSyncDriver(
83
94
  PsycopgDriverBase,
84
95
  SQLTranslatorMixin["PsycopgSyncConnection"],
85
96
  SyncDriverAdapterProtocol["PsycopgSyncConnection"],
97
+ ResultConverter,
86
98
  ):
87
99
  """Psycopg Sync Driver Adapter."""
88
100
 
89
101
  connection: "PsycopgSyncConnection"
90
- dialect: str = "postgres"
91
102
 
92
103
  def __init__(self, connection: "PsycopgSyncConnection") -> None:
93
104
  self.connection = connection
94
105
 
95
- def _process_sql_params(
96
- self,
97
- sql: str,
98
- parameters: "Optional[StatementParameterType]" = None,
99
- /,
100
- **kwargs: Any,
101
- ) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]]]":
102
- stmt = SQLStatement(sql=sql, parameters=parameters, dialect=self.dialect, kwargs=kwargs or None)
103
- processed_sql, processed_params = stmt.process()
104
-
105
- if isinstance(processed_params, dict):
106
- parameter_dict = processed_params
107
- processed_sql_parts: list[str] = []
108
- last_end = 0
109
- found_params_regex: list[str] = []
110
-
111
- for match in PARAM_REGEX.finditer(processed_sql):
112
- if match.group("dquote") or match.group("squote") or match.group("comment"):
113
- continue
114
-
115
- if match.group("var_name"):
116
- var_name = match.group("var_name")
117
- found_params_regex.append(var_name)
118
- start = match.start("var_name") - 1
119
- end = match.end("var_name")
120
-
121
- if var_name not in parameter_dict:
122
- msg = (
123
- f"Named parameter ':{var_name}' found in SQL but missing from processed parameters. "
124
- f"Processed SQL: {processed_sql}"
125
- )
126
- raise SQLParsingError(msg)
127
-
128
- processed_sql_parts.extend((processed_sql[last_end:start], f"%({var_name})s"))
129
- last_end = end
130
-
131
- processed_sql_parts.append(processed_sql[last_end:])
132
- final_sql = "".join(processed_sql_parts)
133
-
134
- if not found_params_regex and parameter_dict:
135
- logger.warning(
136
- "Dict params provided (%s), but no :name placeholders found. SQL: %s",
137
- list(parameter_dict.keys()),
138
- processed_sql,
139
- )
140
- return processed_sql, parameter_dict
141
-
142
- return final_sql, parameter_dict
143
-
144
- return processed_sql, processed_params
145
-
146
106
  @staticmethod
147
107
  @contextmanager
148
108
  def _with_cursor(connection: "PsycopgSyncConnection") -> "Generator[Any, None, None]":
@@ -159,7 +119,7 @@ class PsycopgSyncDriver(
159
119
  sql: str,
160
120
  parameters: "Optional[StatementParameterType]" = None,
161
121
  /,
162
- *,
122
+ *filters: "StatementFilter",
163
123
  connection: "Optional[PsycopgSyncConnection]" = None,
164
124
  schema_type: None = None,
165
125
  **kwargs: Any,
@@ -170,7 +130,7 @@ class PsycopgSyncDriver(
170
130
  sql: str,
171
131
  parameters: "Optional[StatementParameterType]" = None,
172
132
  /,
173
- *,
133
+ *filters: "StatementFilter",
174
134
  connection: "Optional[PsycopgSyncConnection]" = None,
175
135
  schema_type: "type[ModelDTOT]",
176
136
  **kwargs: Any,
@@ -180,7 +140,7 @@ class PsycopgSyncDriver(
180
140
  sql: str,
181
141
  parameters: "Optional[StatementParameterType]" = None,
182
142
  /,
183
- *,
143
+ *filters: "StatementFilter",
184
144
  schema_type: "Optional[type[ModelDTOT]]" = None,
185
145
  connection: "Optional[PsycopgSyncConnection]" = None,
186
146
  **kwargs: Any,
@@ -191,16 +151,14 @@ class PsycopgSyncDriver(
191
151
  List of row data as either model instances or dictionaries.
192
152
  """
193
153
  connection = self._connection(connection)
194
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
154
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
195
155
  with self._with_cursor(connection) as cursor:
196
156
  cursor.execute(sql, parameters)
197
157
  results = cursor.fetchall()
198
158
  if not results:
199
159
  return []
200
160
 
201
- if schema_type is not None:
202
- return [cast("ModelDTOT", schema_type(**row)) for row in results] # pyright: ignore[reportUnknownArgumentType]
203
- return [cast("dict[str,Any]", row) for row in results] # pyright: ignore[reportUnknownArgumentType]
161
+ return self.to_schema(cast("Sequence[dict[str, Any]]", results), schema_type=schema_type)
204
162
 
205
163
  @overload
206
164
  def select_one(
@@ -208,7 +166,7 @@ class PsycopgSyncDriver(
208
166
  sql: str,
209
167
  parameters: "Optional[StatementParameterType]" = None,
210
168
  /,
211
- *,
169
+ *filters: "StatementFilter",
212
170
  connection: "Optional[PsycopgSyncConnection]" = None,
213
171
  schema_type: None = None,
214
172
  **kwargs: Any,
@@ -219,7 +177,7 @@ class PsycopgSyncDriver(
219
177
  sql: str,
220
178
  parameters: "Optional[StatementParameterType]" = None,
221
179
  /,
222
- *,
180
+ *filters: "StatementFilter",
223
181
  connection: "Optional[PsycopgSyncConnection]" = None,
224
182
  schema_type: "type[ModelDTOT]",
225
183
  **kwargs: Any,
@@ -229,7 +187,7 @@ class PsycopgSyncDriver(
229
187
  sql: str,
230
188
  parameters: "Optional[StatementParameterType]" = None,
231
189
  /,
232
- *,
190
+ *filters: "StatementFilter",
233
191
  connection: "Optional[PsycopgSyncConnection]" = None,
234
192
  schema_type: "Optional[type[ModelDTOT]]" = None,
235
193
  **kwargs: Any,
@@ -240,14 +198,13 @@ class PsycopgSyncDriver(
240
198
  The first row of the query results.
241
199
  """
242
200
  connection = self._connection(connection)
243
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
201
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
244
202
  with self._with_cursor(connection) as cursor:
245
203
  cursor.execute(sql, parameters)
246
204
  row = cursor.fetchone()
247
205
  row = self.check_not_found(row)
248
- if schema_type is not None:
249
- return cast("ModelDTOT", schema_type(**cast("dict[str,Any]", row)))
250
- return cast("dict[str,Any]", row)
206
+
207
+ return self.to_schema(cast("dict[str, Any]", row), schema_type=schema_type)
251
208
 
252
209
  @overload
253
210
  def select_one_or_none(
@@ -255,7 +212,7 @@ class PsycopgSyncDriver(
255
212
  sql: str,
256
213
  parameters: "Optional[StatementParameterType]" = None,
257
214
  /,
258
- *,
215
+ *filters: "StatementFilter",
259
216
  connection: "Optional[PsycopgSyncConnection]" = None,
260
217
  schema_type: None = None,
261
218
  **kwargs: Any,
@@ -266,7 +223,7 @@ class PsycopgSyncDriver(
266
223
  sql: str,
267
224
  parameters: "Optional[StatementParameterType]" = None,
268
225
  /,
269
- *,
226
+ *filters: "StatementFilter",
270
227
  connection: "Optional[PsycopgSyncConnection]" = None,
271
228
  schema_type: "type[ModelDTOT]",
272
229
  **kwargs: Any,
@@ -276,7 +233,7 @@ class PsycopgSyncDriver(
276
233
  sql: str,
277
234
  parameters: "Optional[StatementParameterType]" = None,
278
235
  /,
279
- *,
236
+ *filters: "StatementFilter",
280
237
  connection: "Optional[PsycopgSyncConnection]" = None,
281
238
  schema_type: "Optional[type[ModelDTOT]]" = None,
282
239
  **kwargs: Any,
@@ -287,15 +244,13 @@ class PsycopgSyncDriver(
287
244
  The first row of the query results.
288
245
  """
289
246
  connection = self._connection(connection)
290
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
247
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
291
248
  with self._with_cursor(connection) as cursor:
292
249
  cursor.execute(sql, parameters)
293
250
  row = cursor.fetchone()
294
251
  if row is None:
295
252
  return None
296
- if schema_type is not None:
297
- return cast("ModelDTOT", schema_type(**cast("dict[str,Any]", row)))
298
- return cast("dict[str,Any]", row)
253
+ return self.to_schema(cast("dict[str, Any]", row), schema_type=schema_type)
299
254
 
300
255
  @overload
301
256
  def select_value(
@@ -303,7 +258,7 @@ class PsycopgSyncDriver(
303
258
  sql: str,
304
259
  parameters: "Optional[StatementParameterType]" = None,
305
260
  /,
306
- *,
261
+ *filters: "StatementFilter",
307
262
  connection: "Optional[PsycopgSyncConnection]" = None,
308
263
  schema_type: None = None,
309
264
  **kwargs: Any,
@@ -314,7 +269,7 @@ class PsycopgSyncDriver(
314
269
  sql: str,
315
270
  parameters: "Optional[StatementParameterType]" = None,
316
271
  /,
317
- *,
272
+ *filters: "StatementFilter",
318
273
  connection: "Optional[PsycopgSyncConnection]" = None,
319
274
  schema_type: "type[T]",
320
275
  **kwargs: Any,
@@ -324,7 +279,7 @@ class PsycopgSyncDriver(
324
279
  sql: str,
325
280
  parameters: "Optional[StatementParameterType]" = None,
326
281
  /,
327
- *,
282
+ *filters: "StatementFilter",
328
283
  connection: "Optional[PsycopgSyncConnection]" = None,
329
284
  schema_type: "Optional[type[T]]" = None,
330
285
  **kwargs: Any,
@@ -335,7 +290,7 @@ class PsycopgSyncDriver(
335
290
  The first value from the first row of results, or None if no results.
336
291
  """
337
292
  connection = self._connection(connection)
338
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
293
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
339
294
  with self._with_cursor(connection) as cursor:
340
295
  cursor.execute(sql, parameters)
341
296
  row = cursor.fetchone()
@@ -352,7 +307,7 @@ class PsycopgSyncDriver(
352
307
  sql: str,
353
308
  parameters: "Optional[StatementParameterType]" = None,
354
309
  /,
355
- *,
310
+ *filters: "StatementFilter",
356
311
  connection: "Optional[PsycopgSyncConnection]" = None,
357
312
  schema_type: None = None,
358
313
  **kwargs: Any,
@@ -363,7 +318,7 @@ class PsycopgSyncDriver(
363
318
  sql: str,
364
319
  parameters: "Optional[StatementParameterType]" = None,
365
320
  /,
366
- *,
321
+ *filters: "StatementFilter",
367
322
  connection: "Optional[PsycopgSyncConnection]" = None,
368
323
  schema_type: "type[T]",
369
324
  **kwargs: Any,
@@ -373,7 +328,7 @@ class PsycopgSyncDriver(
373
328
  sql: str,
374
329
  parameters: "Optional[StatementParameterType]" = None,
375
330
  /,
376
- *,
331
+ *filters: "StatementFilter",
377
332
  connection: "Optional[PsycopgSyncConnection]" = None,
378
333
  schema_type: "Optional[type[T]]" = None,
379
334
  **kwargs: Any,
@@ -384,7 +339,7 @@ class PsycopgSyncDriver(
384
339
  The first value from the first row of results, or None if no results.
385
340
  """
386
341
  connection = self._connection(connection)
387
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
342
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
388
343
  with self._with_cursor(connection) as cursor:
389
344
  cursor.execute(sql, parameters)
390
345
  row = cursor.fetchone()
@@ -402,7 +357,7 @@ class PsycopgSyncDriver(
402
357
  sql: str,
403
358
  parameters: "Optional[StatementParameterType]" = None,
404
359
  /,
405
- *,
360
+ *filters: "StatementFilter",
406
361
  connection: "Optional[PsycopgSyncConnection]" = None,
407
362
  **kwargs: Any,
408
363
  ) -> int:
@@ -412,7 +367,7 @@ class PsycopgSyncDriver(
412
367
  The number of rows affected by the operation.
413
368
  """
414
369
  connection = self._connection(connection)
415
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
370
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
416
371
  with self._with_cursor(connection) as cursor:
417
372
  cursor.execute(sql, parameters)
418
373
  return getattr(cursor, "rowcount", -1) # pyright: ignore[reportUnknownMemberType]
@@ -423,7 +378,7 @@ class PsycopgSyncDriver(
423
378
  sql: str,
424
379
  parameters: "Optional[StatementParameterType]" = None,
425
380
  /,
426
- *,
381
+ *filters: "StatementFilter",
427
382
  connection: "Optional[PsycopgSyncConnection]" = None,
428
383
  schema_type: None = None,
429
384
  **kwargs: Any,
@@ -434,7 +389,7 @@ class PsycopgSyncDriver(
434
389
  sql: str,
435
390
  parameters: "Optional[StatementParameterType]" = None,
436
391
  /,
437
- *,
392
+ *filters: "StatementFilter",
438
393
  connection: "Optional[PsycopgSyncConnection]" = None,
439
394
  schema_type: "type[ModelDTOT]",
440
395
  **kwargs: Any,
@@ -444,7 +399,7 @@ class PsycopgSyncDriver(
444
399
  sql: str,
445
400
  parameters: "Optional[StatementParameterType]" = None,
446
401
  /,
447
- *,
402
+ *filters: "StatementFilter",
448
403
  connection: "Optional[PsycopgSyncConnection]" = None,
449
404
  schema_type: "Optional[type[ModelDTOT]]" = None,
450
405
  **kwargs: Any,
@@ -455,7 +410,7 @@ class PsycopgSyncDriver(
455
410
  The first row of results.
456
411
  """
457
412
  connection = self._connection(connection)
458
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
413
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
459
414
  with self._with_cursor(connection) as cursor:
460
415
  cursor.execute(sql, parameters)
461
416
  result = cursor.fetchone()
@@ -463,16 +418,13 @@ class PsycopgSyncDriver(
463
418
  if result is None:
464
419
  return None
465
420
 
466
- if schema_type is not None:
467
- return cast("ModelDTOT", schema_type(**result)) # pyright: ignore[reportUnknownArgumentType]
468
- return cast("dict[str, Any]", result) # pyright: ignore[reportUnknownArgumentType,reportUnknownVariableType]
421
+ return self.to_schema(cast("dict[str, Any]", result), schema_type=schema_type)
469
422
 
470
423
  def execute_script(
471
424
  self,
472
425
  sql: str,
473
426
  parameters: "Optional[StatementParameterType]" = None,
474
427
  /,
475
- *,
476
428
  connection: "Optional[PsycopgSyncConnection]" = None,
477
429
  **kwargs: Any,
478
430
  ) -> str:
@@ -492,11 +444,11 @@ class PsycopgAsyncDriver(
492
444
  PsycopgDriverBase,
493
445
  SQLTranslatorMixin["PsycopgAsyncConnection"],
494
446
  AsyncDriverAdapterProtocol["PsycopgAsyncConnection"],
447
+ ResultConverter,
495
448
  ):
496
449
  """Psycopg Async Driver Adapter."""
497
450
 
498
451
  connection: "PsycopgAsyncConnection"
499
- dialect: str = "postgres"
500
452
 
501
453
  def __init__(self, connection: "PsycopgAsyncConnection") -> None:
502
454
  self.connection = connection
@@ -517,7 +469,7 @@ class PsycopgAsyncDriver(
517
469
  sql: str,
518
470
  parameters: "Optional[StatementParameterType]" = None,
519
471
  /,
520
- *,
472
+ *filters: "StatementFilter",
521
473
  connection: "Optional[PsycopgAsyncConnection]" = None,
522
474
  schema_type: None = None,
523
475
  **kwargs: Any,
@@ -528,7 +480,7 @@ class PsycopgAsyncDriver(
528
480
  sql: str,
529
481
  parameters: "Optional[StatementParameterType]" = None,
530
482
  /,
531
- *,
483
+ *filters: "StatementFilter",
532
484
  connection: "Optional[PsycopgAsyncConnection]" = None,
533
485
  schema_type: "type[ModelDTOT]",
534
486
  **kwargs: Any,
@@ -538,9 +490,9 @@ class PsycopgAsyncDriver(
538
490
  sql: str,
539
491
  parameters: "Optional[StatementParameterType]" = None,
540
492
  /,
541
- *,
542
- connection: "Optional[PsycopgAsyncConnection]" = None,
493
+ *filters: "StatementFilter",
543
494
  schema_type: "Optional[type[ModelDTOT]]" = None,
495
+ connection: "Optional[PsycopgAsyncConnection]" = None,
544
496
  **kwargs: Any,
545
497
  ) -> "Sequence[Union[ModelDTOT, dict[str, Any]]]":
546
498
  """Fetch data from the database.
@@ -549,17 +501,13 @@ class PsycopgAsyncDriver(
549
501
  List of row data as either model instances or dictionaries.
550
502
  """
551
503
  connection = self._connection(connection)
552
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
553
- results: list[Union[ModelDTOT, dict[str, Any]]] = []
554
-
504
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
555
505
  async with self._with_cursor(connection) as cursor:
556
506
  await cursor.execute(sql, parameters)
557
507
  results = await cursor.fetchall()
558
508
  if not results:
559
509
  return []
560
- if schema_type is not None:
561
- return [cast("ModelDTOT", schema_type(**cast("dict[str,Any]", row))) for row in results] # pyright: ignore[reportUnknownArgumentType]
562
- return [cast("dict[str,Any]", row) for row in results] # pyright: ignore[reportUnknownArgumentType]
510
+ return self.to_schema(cast("Sequence[dict[str, Any]]", results), schema_type=schema_type)
563
511
 
564
512
  @overload
565
513
  async def select_one(
@@ -567,7 +515,7 @@ class PsycopgAsyncDriver(
567
515
  sql: str,
568
516
  parameters: "Optional[StatementParameterType]" = None,
569
517
  /,
570
- *,
518
+ *filters: "StatementFilter",
571
519
  connection: "Optional[PsycopgAsyncConnection]" = None,
572
520
  schema_type: None = None,
573
521
  **kwargs: Any,
@@ -578,7 +526,7 @@ class PsycopgAsyncDriver(
578
526
  sql: str,
579
527
  parameters: "Optional[StatementParameterType]" = None,
580
528
  /,
581
- *,
529
+ *filters: "StatementFilter",
582
530
  connection: "Optional[PsycopgAsyncConnection]" = None,
583
531
  schema_type: "type[ModelDTOT]",
584
532
  **kwargs: Any,
@@ -588,7 +536,7 @@ class PsycopgAsyncDriver(
588
536
  sql: str,
589
537
  parameters: "Optional[StatementParameterType]" = None,
590
538
  /,
591
- *,
539
+ *filters: "StatementFilter",
592
540
  connection: "Optional[PsycopgAsyncConnection]" = None,
593
541
  schema_type: "Optional[type[ModelDTOT]]" = None,
594
542
  **kwargs: Any,
@@ -599,15 +547,13 @@ class PsycopgAsyncDriver(
599
547
  The first row of the query results.
600
548
  """
601
549
  connection = self._connection(connection)
602
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
603
-
550
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
604
551
  async with self._with_cursor(connection) as cursor:
605
552
  await cursor.execute(sql, parameters)
606
553
  row = await cursor.fetchone()
607
554
  row = self.check_not_found(row)
608
- if schema_type is not None:
609
- return cast("ModelDTOT", schema_type(**cast("dict[str,Any]", row)))
610
- return cast("dict[str,Any]", row)
555
+
556
+ return self.to_schema(cast("dict[str, Any]", row), schema_type=schema_type)
611
557
 
612
558
  @overload
613
559
  async def select_one_or_none(
@@ -615,7 +561,7 @@ class PsycopgAsyncDriver(
615
561
  sql: str,
616
562
  parameters: "Optional[StatementParameterType]" = None,
617
563
  /,
618
- *,
564
+ *filters: "StatementFilter",
619
565
  connection: "Optional[PsycopgAsyncConnection]" = None,
620
566
  schema_type: None = None,
621
567
  **kwargs: Any,
@@ -626,7 +572,7 @@ class PsycopgAsyncDriver(
626
572
  sql: str,
627
573
  parameters: "Optional[StatementParameterType]" = None,
628
574
  /,
629
- *,
575
+ *filters: "StatementFilter",
630
576
  connection: "Optional[PsycopgAsyncConnection]" = None,
631
577
  schema_type: "type[ModelDTOT]",
632
578
  **kwargs: Any,
@@ -636,7 +582,7 @@ class PsycopgAsyncDriver(
636
582
  sql: str,
637
583
  parameters: "Optional[StatementParameterType]" = None,
638
584
  /,
639
- *,
585
+ *filters: "StatementFilter",
640
586
  schema_type: "Optional[type[ModelDTOT]]" = None,
641
587
  connection: "Optional[PsycopgAsyncConnection]" = None,
642
588
  **kwargs: Any,
@@ -647,16 +593,15 @@ class PsycopgAsyncDriver(
647
593
  The first row of the query results.
648
594
  """
649
595
  connection = self._connection(connection)
650
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
651
-
596
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
652
597
  async with self._with_cursor(connection) as cursor:
653
598
  await cursor.execute(sql, parameters)
654
599
  row = await cursor.fetchone()
655
600
  if row is None:
656
601
  return None
657
- if schema_type is not None:
658
- return cast("ModelDTOT", schema_type(**cast("dict[str,Any]", row)))
659
- return cast("dict[str,Any]", row)
602
+
603
+ # Use self.to_schema from ResultConverter mixin
604
+ return self.to_schema(cast("dict[str, Any]", row), schema_type=schema_type)
660
605
 
661
606
  @overload
662
607
  async def select_value(
@@ -664,7 +609,7 @@ class PsycopgAsyncDriver(
664
609
  sql: str,
665
610
  parameters: "Optional[StatementParameterType]" = None,
666
611
  /,
667
- *,
612
+ *filters: "StatementFilter",
668
613
  connection: "Optional[PsycopgAsyncConnection]" = None,
669
614
  schema_type: None = None,
670
615
  **kwargs: Any,
@@ -675,7 +620,7 @@ class PsycopgAsyncDriver(
675
620
  sql: str,
676
621
  parameters: "Optional[StatementParameterType]" = None,
677
622
  /,
678
- *,
623
+ *filters: "StatementFilter",
679
624
  connection: "Optional[PsycopgAsyncConnection]" = None,
680
625
  schema_type: "type[T]",
681
626
  **kwargs: Any,
@@ -685,7 +630,7 @@ class PsycopgAsyncDriver(
685
630
  sql: str,
686
631
  parameters: "Optional[StatementParameterType]" = None,
687
632
  /,
688
- *,
633
+ *filters: "StatementFilter",
689
634
  connection: "Optional[PsycopgAsyncConnection]" = None,
690
635
  schema_type: "Optional[type[T]]" = None,
691
636
  **kwargs: Any,
@@ -696,8 +641,7 @@ class PsycopgAsyncDriver(
696
641
  The first value from the first row of results, or None if no results.
697
642
  """
698
643
  connection = self._connection(connection)
699
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
700
-
644
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
701
645
  async with self._with_cursor(connection) as cursor:
702
646
  await cursor.execute(sql, parameters)
703
647
  row = await cursor.fetchone()
@@ -708,12 +652,34 @@ class PsycopgAsyncDriver(
708
652
  return schema_type(val) # type: ignore[call-arg]
709
653
  return val
710
654
 
655
+ @overload
711
656
  async def select_value_or_none(
712
657
  self,
713
658
  sql: str,
714
659
  parameters: "Optional[StatementParameterType]" = None,
715
660
  /,
716
- *,
661
+ *filters: "StatementFilter",
662
+ connection: "Optional[PsycopgAsyncConnection]" = None,
663
+ schema_type: None = None,
664
+ **kwargs: Any,
665
+ ) -> "Optional[Any]": ...
666
+ @overload
667
+ async def select_value_or_none(
668
+ self,
669
+ sql: str,
670
+ parameters: "Optional[StatementParameterType]" = None,
671
+ /,
672
+ *filters: "StatementFilter",
673
+ connection: "Optional[PsycopgAsyncConnection]" = None,
674
+ schema_type: "type[T]",
675
+ **kwargs: Any,
676
+ ) -> "Optional[T]": ...
677
+ async def select_value_or_none(
678
+ self,
679
+ sql: str,
680
+ parameters: "Optional[StatementParameterType]" = None,
681
+ /,
682
+ *filters: "StatementFilter",
717
683
  connection: "Optional[PsycopgAsyncConnection]" = None,
718
684
  schema_type: "Optional[type[T]]" = None,
719
685
  **kwargs: Any,
@@ -724,8 +690,7 @@ class PsycopgAsyncDriver(
724
690
  The first value from the first row of results, or None if no results.
725
691
  """
726
692
  connection = self._connection(connection)
727
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
728
-
693
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
729
694
  async with self._with_cursor(connection) as cursor:
730
695
  await cursor.execute(sql, parameters)
731
696
  row = await cursor.fetchone()
@@ -743,7 +708,7 @@ class PsycopgAsyncDriver(
743
708
  sql: str,
744
709
  parameters: "Optional[StatementParameterType]" = None,
745
710
  /,
746
- *,
711
+ *filters: "StatementFilter",
747
712
  connection: "Optional[PsycopgAsyncConnection]" = None,
748
713
  **kwargs: Any,
749
714
  ) -> int:
@@ -753,15 +718,10 @@ class PsycopgAsyncDriver(
753
718
  The number of rows affected by the operation.
754
719
  """
755
720
  connection = self._connection(connection)
756
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
757
-
721
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
758
722
  async with self._with_cursor(connection) as cursor:
759
723
  await cursor.execute(sql, parameters)
760
- try:
761
- rowcount = int(cursor.rowcount)
762
- except (TypeError, ValueError):
763
- rowcount = -1
764
- return rowcount
724
+ return getattr(cursor, "rowcount", -1) # pyright: ignore[reportUnknownMemberType]
765
725
 
766
726
  @overload
767
727
  async def insert_update_delete_returning(
@@ -769,7 +729,7 @@ class PsycopgAsyncDriver(
769
729
  sql: str,
770
730
  parameters: "Optional[StatementParameterType]" = None,
771
731
  /,
772
- *,
732
+ *filters: "StatementFilter",
773
733
  connection: "Optional[PsycopgAsyncConnection]" = None,
774
734
  schema_type: None = None,
775
735
  **kwargs: Any,
@@ -780,7 +740,7 @@ class PsycopgAsyncDriver(
780
740
  sql: str,
781
741
  parameters: "Optional[StatementParameterType]" = None,
782
742
  /,
783
- *,
743
+ *filters: "StatementFilter",
784
744
  connection: "Optional[PsycopgAsyncConnection]" = None,
785
745
  schema_type: "type[ModelDTOT]",
786
746
  **kwargs: Any,
@@ -790,7 +750,7 @@ class PsycopgAsyncDriver(
790
750
  sql: str,
791
751
  parameters: "Optional[StatementParameterType]" = None,
792
752
  /,
793
- *,
753
+ *filters: "StatementFilter",
794
754
  connection: "Optional[PsycopgAsyncConnection]" = None,
795
755
  schema_type: "Optional[type[ModelDTOT]]" = None,
796
756
  **kwargs: Any,
@@ -801,25 +761,20 @@ class PsycopgAsyncDriver(
801
761
  The first row of results.
802
762
  """
803
763
  connection = self._connection(connection)
804
- sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
805
-
764
+ sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
806
765
  async with self._with_cursor(connection) as cursor:
807
766
  await cursor.execute(sql, parameters)
808
767
  result = await cursor.fetchone()
809
-
810
768
  if result is None:
811
769
  return None
812
770
 
813
- if schema_type is not None:
814
- return cast("ModelDTOT", schema_type(**result)) # pyright: ignore[reportUnknownArgumentType]
815
- return cast("dict[str, Any]", result) # pyright: ignore[reportUnknownArgumentType,reportUnknownVariableType]
771
+ return self.to_schema(cast("dict[str, Any]", result), schema_type=schema_type)
816
772
 
817
773
  async def execute_script(
818
774
  self,
819
775
  sql: str,
820
776
  parameters: "Optional[StatementParameterType]" = None,
821
777
  /,
822
- *,
823
778
  connection: "Optional[PsycopgAsyncConnection]" = None,
824
779
  **kwargs: Any,
825
780
  ) -> str:
@@ -830,7 +785,6 @@ class PsycopgAsyncDriver(
830
785
  """
831
786
  connection = self._connection(connection)
832
787
  sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
833
-
834
788
  async with self._with_cursor(connection) as cursor:
835
789
  await cursor.execute(sql, parameters)
836
790
  return str(cursor.statusmessage) if cursor.statusmessage is not None else "DONE"