sqlspec 0.8.0__py3-none-any.whl → 0.9.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.
- sqlspec/_typing.py +39 -6
- sqlspec/adapters/adbc/__init__.py +2 -2
- sqlspec/adapters/adbc/config.py +34 -11
- sqlspec/adapters/adbc/driver.py +302 -111
- sqlspec/adapters/aiosqlite/__init__.py +2 -2
- sqlspec/adapters/aiosqlite/config.py +2 -2
- sqlspec/adapters/aiosqlite/driver.py +164 -42
- sqlspec/adapters/asyncmy/__init__.py +3 -3
- sqlspec/adapters/asyncmy/config.py +11 -12
- sqlspec/adapters/asyncmy/driver.py +161 -37
- sqlspec/adapters/asyncpg/__init__.py +5 -5
- sqlspec/adapters/asyncpg/config.py +17 -19
- sqlspec/adapters/asyncpg/driver.py +386 -96
- sqlspec/adapters/duckdb/__init__.py +2 -2
- sqlspec/adapters/duckdb/config.py +2 -2
- sqlspec/adapters/duckdb/driver.py +190 -60
- sqlspec/adapters/oracledb/__init__.py +8 -8
- sqlspec/adapters/oracledb/config/__init__.py +6 -6
- sqlspec/adapters/oracledb/config/_asyncio.py +9 -10
- sqlspec/adapters/oracledb/config/_sync.py +8 -9
- sqlspec/adapters/oracledb/driver.py +384 -45
- sqlspec/adapters/psqlpy/__init__.py +0 -0
- sqlspec/adapters/psqlpy/config.py +250 -0
- sqlspec/adapters/psqlpy/driver.py +481 -0
- sqlspec/adapters/psycopg/__init__.py +10 -5
- sqlspec/adapters/psycopg/config/__init__.py +6 -6
- sqlspec/adapters/psycopg/config/_async.py +12 -12
- sqlspec/adapters/psycopg/config/_sync.py +13 -13
- sqlspec/adapters/psycopg/driver.py +432 -222
- sqlspec/adapters/sqlite/__init__.py +2 -2
- sqlspec/adapters/sqlite/config.py +2 -2
- sqlspec/adapters/sqlite/driver.py +176 -72
- sqlspec/base.py +687 -161
- sqlspec/exceptions.py +30 -0
- sqlspec/extensions/litestar/config.py +6 -0
- sqlspec/extensions/litestar/handlers.py +25 -0
- sqlspec/extensions/litestar/plugin.py +8 -1
- sqlspec/statement.py +373 -0
- sqlspec/typing.py +10 -1
- {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/METADATA +144 -2
- sqlspec-0.9.1.dist-info/RECORD +61 -0
- sqlspec-0.8.0.dist-info/RECORD +0 -57
- {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/WHEEL +0 -0
- {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.8.0.dist-info → sqlspec-0.9.1.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,29 +1,142 @@
|
|
|
1
|
+
import logging
|
|
1
2
|
from contextlib import asynccontextmanager, contextmanager
|
|
2
|
-
from typing import TYPE_CHECKING, Any, Optional, Union, cast
|
|
3
|
+
from typing import TYPE_CHECKING, Any, Optional, Union, cast, overload
|
|
3
4
|
|
|
4
5
|
from psycopg.rows import dict_row
|
|
5
6
|
|
|
6
|
-
from sqlspec.base import
|
|
7
|
+
from sqlspec.base import AsyncDriverAdapterProtocol, SyncDriverAdapterProtocol, T
|
|
8
|
+
from sqlspec.exceptions import SQLParsingError
|
|
9
|
+
from sqlspec.statement import PARAM_REGEX, SQLStatement
|
|
7
10
|
|
|
8
11
|
if TYPE_CHECKING:
|
|
9
|
-
from collections.abc import AsyncGenerator, Generator
|
|
12
|
+
from collections.abc import AsyncGenerator, Generator, Sequence
|
|
10
13
|
|
|
11
14
|
from psycopg import AsyncConnection, Connection
|
|
12
15
|
|
|
13
16
|
from sqlspec.typing import ModelDTOT, StatementParameterType
|
|
14
17
|
|
|
18
|
+
logger = logging.getLogger("sqlspec")
|
|
19
|
+
|
|
15
20
|
__all__ = ("PsycopgAsyncDriver", "PsycopgSyncDriver")
|
|
16
21
|
|
|
17
22
|
|
|
18
|
-
class
|
|
23
|
+
class PsycopgParameterParser:
|
|
24
|
+
dialect: str
|
|
25
|
+
|
|
26
|
+
def _process_sql_params(
|
|
27
|
+
self,
|
|
28
|
+
sql: str,
|
|
29
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
30
|
+
/,
|
|
31
|
+
**kwargs: Any,
|
|
32
|
+
) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]]]":
|
|
33
|
+
"""Process SQL and parameters, converting :name -> %(name)s if needed."""
|
|
34
|
+
stmt = SQLStatement(sql=sql, parameters=parameters, dialect=self.dialect, kwargs=kwargs or None)
|
|
35
|
+
processed_sql, processed_params = stmt.process()
|
|
36
|
+
|
|
37
|
+
if isinstance(processed_params, dict):
|
|
38
|
+
parameter_dict = processed_params
|
|
39
|
+
processed_sql_parts: list[str] = []
|
|
40
|
+
last_end = 0
|
|
41
|
+
found_params_regex: list[str] = []
|
|
42
|
+
|
|
43
|
+
for match in PARAM_REGEX.finditer(processed_sql):
|
|
44
|
+
if match.group("dquote") or match.group("squote") or match.group("comment"):
|
|
45
|
+
continue
|
|
46
|
+
|
|
47
|
+
if match.group("var_name"):
|
|
48
|
+
var_name = match.group("var_name")
|
|
49
|
+
found_params_regex.append(var_name)
|
|
50
|
+
start = match.start("var_name") - 1
|
|
51
|
+
end = match.end("var_name")
|
|
52
|
+
|
|
53
|
+
if var_name not in parameter_dict:
|
|
54
|
+
msg = (
|
|
55
|
+
f"Named parameter ':{var_name}' found in SQL but missing from processed parameters. "
|
|
56
|
+
f"Processed SQL: {processed_sql}"
|
|
57
|
+
)
|
|
58
|
+
raise SQLParsingError(msg)
|
|
59
|
+
|
|
60
|
+
processed_sql_parts.extend((processed_sql[last_end:start], f"%({var_name})s"))
|
|
61
|
+
last_end = end
|
|
62
|
+
|
|
63
|
+
processed_sql_parts.append(processed_sql[last_end:])
|
|
64
|
+
final_sql = "".join(processed_sql_parts)
|
|
65
|
+
|
|
66
|
+
if not found_params_regex and parameter_dict:
|
|
67
|
+
logger.warning(
|
|
68
|
+
"Dict params provided (%s), but no :name placeholders found. SQL: %s",
|
|
69
|
+
list(parameter_dict.keys()),
|
|
70
|
+
processed_sql,
|
|
71
|
+
)
|
|
72
|
+
return processed_sql, parameter_dict
|
|
73
|
+
|
|
74
|
+
return final_sql, parameter_dict
|
|
75
|
+
|
|
76
|
+
return processed_sql, processed_params
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class PsycopgSyncDriver(PsycopgParameterParser, SyncDriverAdapterProtocol["Connection"]):
|
|
19
80
|
"""Psycopg Sync Driver Adapter."""
|
|
20
81
|
|
|
21
82
|
connection: "Connection"
|
|
22
|
-
|
|
83
|
+
dialect: str = "postgres"
|
|
23
84
|
|
|
24
85
|
def __init__(self, connection: "Connection") -> None:
|
|
25
86
|
self.connection = connection
|
|
26
87
|
|
|
88
|
+
def _process_sql_params(
|
|
89
|
+
self,
|
|
90
|
+
sql: str,
|
|
91
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
92
|
+
/,
|
|
93
|
+
**kwargs: Any,
|
|
94
|
+
) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]]]":
|
|
95
|
+
"""Process SQL and parameters, converting :name -> %(name)s if needed."""
|
|
96
|
+
stmt = SQLStatement(sql=sql, parameters=parameters, dialect=self.dialect, kwargs=kwargs or None)
|
|
97
|
+
processed_sql, processed_params = stmt.process()
|
|
98
|
+
|
|
99
|
+
if isinstance(processed_params, dict):
|
|
100
|
+
parameter_dict = processed_params
|
|
101
|
+
processed_sql_parts: list[str] = []
|
|
102
|
+
last_end = 0
|
|
103
|
+
found_params_regex: list[str] = []
|
|
104
|
+
|
|
105
|
+
for match in PARAM_REGEX.finditer(processed_sql):
|
|
106
|
+
if match.group("dquote") or match.group("squote") or match.group("comment"):
|
|
107
|
+
continue
|
|
108
|
+
|
|
109
|
+
if match.group("var_name"):
|
|
110
|
+
var_name = match.group("var_name")
|
|
111
|
+
found_params_regex.append(var_name)
|
|
112
|
+
start = match.start("var_name") - 1
|
|
113
|
+
end = match.end("var_name")
|
|
114
|
+
|
|
115
|
+
if var_name not in parameter_dict:
|
|
116
|
+
msg = (
|
|
117
|
+
f"Named parameter ':{var_name}' found in SQL but missing from processed parameters. "
|
|
118
|
+
f"Processed SQL: {processed_sql}"
|
|
119
|
+
)
|
|
120
|
+
raise SQLParsingError(msg)
|
|
121
|
+
|
|
122
|
+
processed_sql_parts.extend((processed_sql[last_end:start], f"%({var_name})s"))
|
|
123
|
+
last_end = end
|
|
124
|
+
|
|
125
|
+
processed_sql_parts.append(processed_sql[last_end:])
|
|
126
|
+
final_sql = "".join(processed_sql_parts)
|
|
127
|
+
|
|
128
|
+
if not found_params_regex and parameter_dict:
|
|
129
|
+
logger.warning(
|
|
130
|
+
"Dict params provided (%s), but no :name placeholders found. SQL: %s",
|
|
131
|
+
list(parameter_dict.keys()),
|
|
132
|
+
processed_sql,
|
|
133
|
+
)
|
|
134
|
+
return processed_sql, parameter_dict
|
|
135
|
+
|
|
136
|
+
return final_sql, parameter_dict
|
|
137
|
+
|
|
138
|
+
return processed_sql, processed_params
|
|
139
|
+
|
|
27
140
|
@staticmethod
|
|
28
141
|
@contextmanager
|
|
29
142
|
def _with_cursor(connection: "Connection") -> "Generator[Any, None, None]":
|
|
@@ -33,83 +146,46 @@ class PsycopgSyncDriver(SyncDriverAdapterProtocol["Connection"]):
|
|
|
33
146
|
finally:
|
|
34
147
|
cursor.close()
|
|
35
148
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]]]":
|
|
39
|
-
"""Process SQL query and parameters for DB-API execution.
|
|
40
|
-
|
|
41
|
-
Converts named parameters (:name) to positional parameters (%s)
|
|
42
|
-
if the input parameters are a dictionary.
|
|
43
|
-
|
|
44
|
-
Args:
|
|
45
|
-
sql: The SQL query string.
|
|
46
|
-
parameters: The parameters for the query (dict, tuple, list, or None).
|
|
47
|
-
|
|
48
|
-
Returns:
|
|
49
|
-
A tuple containing the processed SQL string and the processed parameters
|
|
50
|
-
(always a tuple or None if the input was a dictionary, otherwise the original type).
|
|
51
|
-
|
|
52
|
-
Raises:
|
|
53
|
-
ValueError: If a named parameter in the SQL is not found in the dictionary
|
|
54
|
-
or if a parameter in the dictionary is not used in the SQL.
|
|
55
|
-
"""
|
|
56
|
-
if not isinstance(parameters, dict) or not parameters:
|
|
57
|
-
# If parameters are not a dict, or empty dict, assume positional/no params
|
|
58
|
-
# Let the underlying driver handle tuples/lists directly
|
|
59
|
-
return sql, parameters
|
|
60
|
-
|
|
61
|
-
processed_sql = ""
|
|
62
|
-
processed_params_list: list[Any] = []
|
|
63
|
-
last_end = 0
|
|
64
|
-
found_params: set[str] = set()
|
|
65
|
-
|
|
66
|
-
for match in PARAM_REGEX.finditer(sql):
|
|
67
|
-
if match.group("dquote") is not None or match.group("squote") is not None:
|
|
68
|
-
# Skip placeholders within quotes
|
|
69
|
-
continue
|
|
70
|
-
|
|
71
|
-
var_name = match.group("var_name")
|
|
72
|
-
if var_name is None: # Should not happen with the regex, but safeguard
|
|
73
|
-
continue
|
|
74
|
-
|
|
75
|
-
if var_name not in parameters:
|
|
76
|
-
msg = f"Named parameter ':{var_name}' found in SQL but not provided in parameters dictionary."
|
|
77
|
-
raise ValueError(msg)
|
|
78
|
-
|
|
79
|
-
# Append segment before the placeholder + the driver's positional placeholder
|
|
80
|
-
processed_sql += sql[last_end : match.start("var_name") - 1] + "%s"
|
|
81
|
-
processed_params_list.append(parameters[var_name])
|
|
82
|
-
found_params.add(var_name)
|
|
83
|
-
last_end = match.end("var_name")
|
|
84
|
-
|
|
85
|
-
# Append the rest of the SQL string
|
|
86
|
-
processed_sql += sql[last_end:]
|
|
87
|
-
|
|
88
|
-
# Check if all provided parameters were used
|
|
89
|
-
unused_params = set(parameters.keys()) - found_params
|
|
90
|
-
if unused_params:
|
|
91
|
-
msg = f"Parameters provided but not found in SQL: {unused_params}"
|
|
92
|
-
# Depending on desired strictness, this could be a warning or an error
|
|
93
|
-
# For now, let's raise an error for clarity
|
|
94
|
-
raise ValueError(msg)
|
|
95
|
-
|
|
96
|
-
return processed_sql, tuple(processed_params_list)
|
|
97
|
-
|
|
149
|
+
# --- Public API Methods --- #
|
|
150
|
+
@overload
|
|
98
151
|
def select(
|
|
99
152
|
self,
|
|
100
153
|
sql: str,
|
|
101
154
|
parameters: "Optional[StatementParameterType]" = None,
|
|
102
155
|
/,
|
|
156
|
+
*,
|
|
103
157
|
connection: "Optional[Connection]" = None,
|
|
158
|
+
schema_type: None = None,
|
|
159
|
+
**kwargs: Any,
|
|
160
|
+
) -> "Sequence[dict[str, Any]]": ...
|
|
161
|
+
@overload
|
|
162
|
+
def select(
|
|
163
|
+
self,
|
|
164
|
+
sql: str,
|
|
165
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
166
|
+
/,
|
|
167
|
+
*,
|
|
168
|
+
connection: "Optional[Connection]" = None,
|
|
169
|
+
schema_type: "type[ModelDTOT]",
|
|
170
|
+
**kwargs: Any,
|
|
171
|
+
) -> "Sequence[ModelDTOT]": ...
|
|
172
|
+
def select(
|
|
173
|
+
self,
|
|
174
|
+
sql: str,
|
|
175
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
176
|
+
/,
|
|
177
|
+
*,
|
|
104
178
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
105
|
-
|
|
179
|
+
connection: "Optional[Connection]" = None,
|
|
180
|
+
**kwargs: Any,
|
|
181
|
+
) -> "Sequence[Union[ModelDTOT, dict[str, Any]]]":
|
|
106
182
|
"""Fetch data from the database.
|
|
107
183
|
|
|
108
184
|
Returns:
|
|
109
185
|
List of row data as either model instances or dictionaries.
|
|
110
186
|
"""
|
|
111
187
|
connection = self._connection(connection)
|
|
112
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
188
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
113
189
|
with self._with_cursor(connection) as cursor:
|
|
114
190
|
cursor.execute(sql, parameters)
|
|
115
191
|
results = cursor.fetchall()
|
|
@@ -120,13 +196,37 @@ class PsycopgSyncDriver(SyncDriverAdapterProtocol["Connection"]):
|
|
|
120
196
|
return [cast("ModelDTOT", schema_type(**row)) for row in results] # pyright: ignore[reportUnknownArgumentType]
|
|
121
197
|
return [cast("dict[str,Any]", row) for row in results] # pyright: ignore[reportUnknownArgumentType]
|
|
122
198
|
|
|
199
|
+
@overload
|
|
200
|
+
def select_one(
|
|
201
|
+
self,
|
|
202
|
+
sql: str,
|
|
203
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
204
|
+
/,
|
|
205
|
+
*,
|
|
206
|
+
connection: "Optional[Connection]" = None,
|
|
207
|
+
schema_type: None = None,
|
|
208
|
+
**kwargs: Any,
|
|
209
|
+
) -> "dict[str, Any]": ...
|
|
210
|
+
@overload
|
|
211
|
+
def select_one(
|
|
212
|
+
self,
|
|
213
|
+
sql: str,
|
|
214
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
215
|
+
/,
|
|
216
|
+
*,
|
|
217
|
+
connection: "Optional[Connection]" = None,
|
|
218
|
+
schema_type: "type[ModelDTOT]",
|
|
219
|
+
**kwargs: Any,
|
|
220
|
+
) -> "ModelDTOT": ...
|
|
123
221
|
def select_one(
|
|
124
222
|
self,
|
|
125
223
|
sql: str,
|
|
126
224
|
parameters: "Optional[StatementParameterType]" = None,
|
|
127
225
|
/,
|
|
226
|
+
*,
|
|
128
227
|
connection: "Optional[Connection]" = None,
|
|
129
228
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
229
|
+
**kwargs: Any,
|
|
130
230
|
) -> "Union[ModelDTOT, dict[str, Any]]":
|
|
131
231
|
"""Fetch one row from the database.
|
|
132
232
|
|
|
@@ -134,8 +234,7 @@ class PsycopgSyncDriver(SyncDriverAdapterProtocol["Connection"]):
|
|
|
134
234
|
The first row of the query results.
|
|
135
235
|
"""
|
|
136
236
|
connection = self._connection(connection)
|
|
137
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
138
|
-
|
|
237
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
139
238
|
with self._with_cursor(connection) as cursor:
|
|
140
239
|
cursor.execute(sql, parameters)
|
|
141
240
|
row = cursor.fetchone()
|
|
@@ -144,13 +243,37 @@ class PsycopgSyncDriver(SyncDriverAdapterProtocol["Connection"]):
|
|
|
144
243
|
return cast("ModelDTOT", schema_type(**cast("dict[str,Any]", row)))
|
|
145
244
|
return cast("dict[str,Any]", row)
|
|
146
245
|
|
|
246
|
+
@overload
|
|
247
|
+
def select_one_or_none(
|
|
248
|
+
self,
|
|
249
|
+
sql: str,
|
|
250
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
251
|
+
/,
|
|
252
|
+
*,
|
|
253
|
+
connection: "Optional[Connection]" = None,
|
|
254
|
+
schema_type: None = None,
|
|
255
|
+
**kwargs: Any,
|
|
256
|
+
) -> "Optional[dict[str, Any]]": ...
|
|
257
|
+
@overload
|
|
147
258
|
def select_one_or_none(
|
|
148
259
|
self,
|
|
149
260
|
sql: str,
|
|
150
261
|
parameters: "Optional[StatementParameterType]" = None,
|
|
151
262
|
/,
|
|
263
|
+
*,
|
|
264
|
+
connection: "Optional[Connection]" = None,
|
|
265
|
+
schema_type: "type[ModelDTOT]",
|
|
266
|
+
**kwargs: Any,
|
|
267
|
+
) -> "Optional[ModelDTOT]": ...
|
|
268
|
+
def select_one_or_none(
|
|
269
|
+
self,
|
|
270
|
+
sql: str,
|
|
271
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
272
|
+
/,
|
|
273
|
+
*,
|
|
152
274
|
connection: "Optional[Connection]" = None,
|
|
153
275
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
276
|
+
**kwargs: Any,
|
|
154
277
|
) -> "Optional[Union[ModelDTOT, dict[str, Any]]]":
|
|
155
278
|
"""Fetch one row from the database.
|
|
156
279
|
|
|
@@ -158,8 +281,7 @@ class PsycopgSyncDriver(SyncDriverAdapterProtocol["Connection"]):
|
|
|
158
281
|
The first row of the query results.
|
|
159
282
|
"""
|
|
160
283
|
connection = self._connection(connection)
|
|
161
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
162
|
-
|
|
284
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
163
285
|
with self._with_cursor(connection) as cursor:
|
|
164
286
|
cursor.execute(sql, parameters)
|
|
165
287
|
row = cursor.fetchone()
|
|
@@ -169,13 +291,37 @@ class PsycopgSyncDriver(SyncDriverAdapterProtocol["Connection"]):
|
|
|
169
291
|
return cast("ModelDTOT", schema_type(**cast("dict[str,Any]", row)))
|
|
170
292
|
return cast("dict[str,Any]", row)
|
|
171
293
|
|
|
294
|
+
@overload
|
|
295
|
+
def select_value(
|
|
296
|
+
self,
|
|
297
|
+
sql: str,
|
|
298
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
299
|
+
/,
|
|
300
|
+
*,
|
|
301
|
+
connection: "Optional[Connection]" = None,
|
|
302
|
+
schema_type: None = None,
|
|
303
|
+
**kwargs: Any,
|
|
304
|
+
) -> "Any": ...
|
|
305
|
+
@overload
|
|
172
306
|
def select_value(
|
|
173
307
|
self,
|
|
174
308
|
sql: str,
|
|
175
309
|
parameters: "Optional[StatementParameterType]" = None,
|
|
176
310
|
/,
|
|
311
|
+
*,
|
|
312
|
+
connection: "Optional[Connection]" = None,
|
|
313
|
+
schema_type: "type[T]",
|
|
314
|
+
**kwargs: Any,
|
|
315
|
+
) -> "T": ...
|
|
316
|
+
def select_value(
|
|
317
|
+
self,
|
|
318
|
+
sql: str,
|
|
319
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
320
|
+
/,
|
|
321
|
+
*,
|
|
177
322
|
connection: "Optional[Connection]" = None,
|
|
178
323
|
schema_type: "Optional[type[T]]" = None,
|
|
324
|
+
**kwargs: Any,
|
|
179
325
|
) -> "Union[T, Any]":
|
|
180
326
|
"""Fetch a single value from the database.
|
|
181
327
|
|
|
@@ -183,24 +329,48 @@ class PsycopgSyncDriver(SyncDriverAdapterProtocol["Connection"]):
|
|
|
183
329
|
The first value from the first row of results, or None if no results.
|
|
184
330
|
"""
|
|
185
331
|
connection = self._connection(connection)
|
|
186
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
187
|
-
|
|
332
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
188
333
|
with self._with_cursor(connection) as cursor:
|
|
189
334
|
cursor.execute(sql, parameters)
|
|
190
335
|
row = cursor.fetchone()
|
|
191
336
|
row = self.check_not_found(row)
|
|
192
|
-
val = next(iter(row))
|
|
337
|
+
val = next(iter(row.values())) if row else None
|
|
338
|
+
val = self.check_not_found(val)
|
|
193
339
|
if schema_type is not None:
|
|
194
340
|
return schema_type(val) # type: ignore[call-arg]
|
|
195
341
|
return val
|
|
196
342
|
|
|
343
|
+
@overload
|
|
197
344
|
def select_value_or_none(
|
|
198
345
|
self,
|
|
199
346
|
sql: str,
|
|
200
347
|
parameters: "Optional[StatementParameterType]" = None,
|
|
201
348
|
/,
|
|
349
|
+
*,
|
|
350
|
+
connection: "Optional[Connection]" = None,
|
|
351
|
+
schema_type: None = None,
|
|
352
|
+
**kwargs: Any,
|
|
353
|
+
) -> "Optional[Any]": ...
|
|
354
|
+
@overload
|
|
355
|
+
def select_value_or_none(
|
|
356
|
+
self,
|
|
357
|
+
sql: str,
|
|
358
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
359
|
+
/,
|
|
360
|
+
*,
|
|
361
|
+
connection: "Optional[Connection]" = None,
|
|
362
|
+
schema_type: "type[T]",
|
|
363
|
+
**kwargs: Any,
|
|
364
|
+
) -> "Optional[T]": ...
|
|
365
|
+
def select_value_or_none(
|
|
366
|
+
self,
|
|
367
|
+
sql: str,
|
|
368
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
369
|
+
/,
|
|
370
|
+
*,
|
|
202
371
|
connection: "Optional[Connection]" = None,
|
|
203
372
|
schema_type: "Optional[type[T]]" = None,
|
|
373
|
+
**kwargs: Any,
|
|
204
374
|
) -> "Optional[Union[T, Any]]":
|
|
205
375
|
"""Fetch a single value from the database.
|
|
206
376
|
|
|
@@ -208,14 +378,15 @@ class PsycopgSyncDriver(SyncDriverAdapterProtocol["Connection"]):
|
|
|
208
378
|
The first value from the first row of results, or None if no results.
|
|
209
379
|
"""
|
|
210
380
|
connection = self._connection(connection)
|
|
211
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
212
|
-
|
|
381
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
213
382
|
with self._with_cursor(connection) as cursor:
|
|
214
383
|
cursor.execute(sql, parameters)
|
|
215
384
|
row = cursor.fetchone()
|
|
216
385
|
if row is None:
|
|
217
386
|
return None
|
|
218
|
-
val = next(iter(row))
|
|
387
|
+
val = next(iter(row.values())) if row else None
|
|
388
|
+
if val is None:
|
|
389
|
+
return None
|
|
219
390
|
if schema_type is not None:
|
|
220
391
|
return schema_type(val) # type: ignore[call-arg]
|
|
221
392
|
return val
|
|
@@ -225,27 +396,52 @@ class PsycopgSyncDriver(SyncDriverAdapterProtocol["Connection"]):
|
|
|
225
396
|
sql: str,
|
|
226
397
|
parameters: "Optional[StatementParameterType]" = None,
|
|
227
398
|
/,
|
|
399
|
+
*,
|
|
228
400
|
connection: "Optional[Connection]" = None,
|
|
401
|
+
**kwargs: Any,
|
|
229
402
|
) -> int:
|
|
230
|
-
"""
|
|
403
|
+
"""Execute an INSERT, UPDATE, or DELETE query and return the number of affected rows.
|
|
231
404
|
|
|
232
405
|
Returns:
|
|
233
|
-
|
|
406
|
+
The number of rows affected by the operation.
|
|
234
407
|
"""
|
|
235
408
|
connection = self._connection(connection)
|
|
236
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
237
|
-
|
|
409
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
238
410
|
with self._with_cursor(connection) as cursor:
|
|
239
411
|
cursor.execute(sql, parameters)
|
|
240
|
-
return
|
|
412
|
+
return getattr(cursor, "rowcount", -1) # pyright: ignore[reportUnknownMemberType]
|
|
241
413
|
|
|
414
|
+
@overload
|
|
242
415
|
def insert_update_delete_returning(
|
|
243
416
|
self,
|
|
244
417
|
sql: str,
|
|
245
418
|
parameters: "Optional[StatementParameterType]" = None,
|
|
246
419
|
/,
|
|
420
|
+
*,
|
|
421
|
+
connection: "Optional[Connection]" = None,
|
|
422
|
+
schema_type: None = None,
|
|
423
|
+
**kwargs: Any,
|
|
424
|
+
) -> "dict[str, Any]": ...
|
|
425
|
+
@overload
|
|
426
|
+
def insert_update_delete_returning(
|
|
427
|
+
self,
|
|
428
|
+
sql: str,
|
|
429
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
430
|
+
/,
|
|
431
|
+
*,
|
|
432
|
+
connection: "Optional[Connection]" = None,
|
|
433
|
+
schema_type: "type[ModelDTOT]",
|
|
434
|
+
**kwargs: Any,
|
|
435
|
+
) -> "ModelDTOT": ...
|
|
436
|
+
def insert_update_delete_returning(
|
|
437
|
+
self,
|
|
438
|
+
sql: str,
|
|
439
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
440
|
+
/,
|
|
441
|
+
*,
|
|
247
442
|
connection: "Optional[Connection]" = None,
|
|
248
443
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
444
|
+
**kwargs: Any,
|
|
249
445
|
) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
|
|
250
446
|
"""Insert, update, or delete data from the database and return result.
|
|
251
447
|
|
|
@@ -253,8 +449,7 @@ class PsycopgSyncDriver(SyncDriverAdapterProtocol["Connection"]):
|
|
|
253
449
|
The first row of results.
|
|
254
450
|
"""
|
|
255
451
|
connection = self._connection(connection)
|
|
256
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
257
|
-
|
|
452
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
258
453
|
with self._with_cursor(connection) as cursor:
|
|
259
454
|
cursor.execute(sql, parameters)
|
|
260
455
|
result = cursor.fetchone()
|
|
@@ -271,7 +466,9 @@ class PsycopgSyncDriver(SyncDriverAdapterProtocol["Connection"]):
|
|
|
271
466
|
sql: str,
|
|
272
467
|
parameters: "Optional[StatementParameterType]" = None,
|
|
273
468
|
/,
|
|
469
|
+
*,
|
|
274
470
|
connection: "Optional[Connection]" = None,
|
|
471
|
+
**kwargs: Any,
|
|
275
472
|
) -> str:
|
|
276
473
|
"""Execute a script.
|
|
277
474
|
|
|
@@ -279,45 +476,17 @@ class PsycopgSyncDriver(SyncDriverAdapterProtocol["Connection"]):
|
|
|
279
476
|
Status message for the operation.
|
|
280
477
|
"""
|
|
281
478
|
connection = self._connection(connection)
|
|
282
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
283
|
-
|
|
479
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
284
480
|
with self._with_cursor(connection) as cursor:
|
|
285
481
|
cursor.execute(sql, parameters)
|
|
286
|
-
return str(cursor.
|
|
287
|
-
|
|
288
|
-
def execute_script_returning(
|
|
289
|
-
self,
|
|
290
|
-
sql: str,
|
|
291
|
-
parameters: "Optional[StatementParameterType]" = None,
|
|
292
|
-
/,
|
|
293
|
-
connection: "Optional[Connection]" = None,
|
|
294
|
-
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
295
|
-
) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
|
|
296
|
-
"""Execute a script and return result.
|
|
297
|
-
|
|
298
|
-
Returns:
|
|
299
|
-
The first row of results.
|
|
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)
|
|
306
|
-
result = cursor.fetchone()
|
|
307
|
-
|
|
308
|
-
if result is None:
|
|
309
|
-
return None
|
|
310
|
-
|
|
311
|
-
if schema_type is not None:
|
|
312
|
-
return cast("ModelDTOT", schema_type(**result)) # pyright: ignore[reportUnknownArgumentType]
|
|
313
|
-
return cast("dict[str, Any]", result) # pyright: ignore[reportUnknownArgumentType,reportUnknownVariableType]
|
|
482
|
+
return str(cursor.statusmessage) if cursor.statusmessage is not None else "DONE"
|
|
314
483
|
|
|
315
484
|
|
|
316
|
-
class PsycopgAsyncDriver(AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
485
|
+
class PsycopgAsyncDriver(PsycopgParameterParser, AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
317
486
|
"""Psycopg Async Driver Adapter."""
|
|
318
487
|
|
|
319
488
|
connection: "AsyncConnection"
|
|
320
|
-
|
|
489
|
+
dialect: str = "postgres"
|
|
321
490
|
|
|
322
491
|
def __init__(self, connection: "AsyncConnection") -> None:
|
|
323
492
|
self.connection = connection
|
|
@@ -331,83 +500,46 @@ class PsycopgAsyncDriver(AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
|
331
500
|
finally:
|
|
332
501
|
await cursor.close()
|
|
333
502
|
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]]]":
|
|
337
|
-
"""Process SQL query and parameters for DB-API execution.
|
|
338
|
-
|
|
339
|
-
Converts named parameters (:name) to positional parameters (%s)
|
|
340
|
-
if the input parameters are a dictionary.
|
|
341
|
-
|
|
342
|
-
Args:
|
|
343
|
-
sql: The SQL query string.
|
|
344
|
-
parameters: The parameters for the query (dict, tuple, list, or None).
|
|
345
|
-
|
|
346
|
-
Returns:
|
|
347
|
-
A tuple containing the processed SQL string and the processed parameters
|
|
348
|
-
(always a tuple or None if the input was a dictionary, otherwise the original type).
|
|
349
|
-
|
|
350
|
-
Raises:
|
|
351
|
-
ValueError: If a named parameter in the SQL is not found in the dictionary
|
|
352
|
-
or if a parameter in the dictionary is not used in the SQL.
|
|
353
|
-
"""
|
|
354
|
-
if not isinstance(parameters, dict) or not parameters:
|
|
355
|
-
# If parameters are not a dict, or empty dict, assume positional/no params
|
|
356
|
-
# Let the underlying driver handle tuples/lists directly
|
|
357
|
-
return sql, parameters
|
|
358
|
-
|
|
359
|
-
processed_sql = ""
|
|
360
|
-
processed_params_list: list[Any] = []
|
|
361
|
-
last_end = 0
|
|
362
|
-
found_params: set[str] = set()
|
|
363
|
-
|
|
364
|
-
for match in PARAM_REGEX.finditer(sql):
|
|
365
|
-
if match.group("dquote") is not None or match.group("squote") is not None:
|
|
366
|
-
# Skip placeholders within quotes
|
|
367
|
-
continue
|
|
368
|
-
|
|
369
|
-
var_name = match.group("var_name")
|
|
370
|
-
if var_name is None: # Should not happen with the regex, but safeguard
|
|
371
|
-
continue
|
|
372
|
-
|
|
373
|
-
if var_name not in parameters:
|
|
374
|
-
msg = f"Named parameter ':{var_name}' found in SQL but not provided in parameters dictionary."
|
|
375
|
-
raise ValueError(msg)
|
|
376
|
-
|
|
377
|
-
# Append segment before the placeholder + the driver's positional placeholder
|
|
378
|
-
processed_sql += sql[last_end : match.start("var_name") - 1] + "%s"
|
|
379
|
-
processed_params_list.append(parameters[var_name])
|
|
380
|
-
found_params.add(var_name)
|
|
381
|
-
last_end = match.end("var_name")
|
|
382
|
-
|
|
383
|
-
# Append the rest of the SQL string
|
|
384
|
-
processed_sql += sql[last_end:]
|
|
385
|
-
|
|
386
|
-
# Check if all provided parameters were used
|
|
387
|
-
unused_params = set(parameters.keys()) - found_params
|
|
388
|
-
if unused_params:
|
|
389
|
-
msg = f"Parameters provided but not found in SQL: {unused_params}"
|
|
390
|
-
# Depending on desired strictness, this could be a warning or an error
|
|
391
|
-
# For now, let's raise an error for clarity
|
|
392
|
-
raise ValueError(msg)
|
|
393
|
-
|
|
394
|
-
return processed_sql, tuple(processed_params_list)
|
|
395
|
-
|
|
503
|
+
# --- Public API Methods --- #
|
|
504
|
+
@overload
|
|
396
505
|
async def select(
|
|
397
506
|
self,
|
|
398
507
|
sql: str,
|
|
399
508
|
parameters: "Optional[StatementParameterType]" = None,
|
|
400
509
|
/,
|
|
510
|
+
*,
|
|
511
|
+
connection: "Optional[AsyncConnection]" = None,
|
|
512
|
+
schema_type: None = None,
|
|
513
|
+
**kwargs: Any,
|
|
514
|
+
) -> "Sequence[dict[str, Any]]": ...
|
|
515
|
+
@overload
|
|
516
|
+
async def select(
|
|
517
|
+
self,
|
|
518
|
+
sql: str,
|
|
519
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
520
|
+
/,
|
|
521
|
+
*,
|
|
522
|
+
connection: "Optional[AsyncConnection]" = None,
|
|
523
|
+
schema_type: "type[ModelDTOT]",
|
|
524
|
+
**kwargs: Any,
|
|
525
|
+
) -> "Sequence[ModelDTOT]": ...
|
|
526
|
+
async def select(
|
|
527
|
+
self,
|
|
528
|
+
sql: str,
|
|
529
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
530
|
+
/,
|
|
531
|
+
*,
|
|
401
532
|
connection: "Optional[AsyncConnection]" = None,
|
|
402
533
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
403
|
-
|
|
534
|
+
**kwargs: Any,
|
|
535
|
+
) -> "Sequence[Union[ModelDTOT, dict[str, Any]]]":
|
|
404
536
|
"""Fetch data from the database.
|
|
405
537
|
|
|
406
538
|
Returns:
|
|
407
539
|
List of row data as either model instances or dictionaries.
|
|
408
540
|
"""
|
|
409
541
|
connection = self._connection(connection)
|
|
410
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
542
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
411
543
|
results: list[Union[ModelDTOT, dict[str, Any]]] = []
|
|
412
544
|
|
|
413
545
|
async with self._with_cursor(connection) as cursor:
|
|
@@ -419,13 +551,37 @@ class PsycopgAsyncDriver(AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
|
419
551
|
return [cast("ModelDTOT", schema_type(**cast("dict[str,Any]", row))) for row in results] # pyright: ignore[reportUnknownArgumentType]
|
|
420
552
|
return [cast("dict[str,Any]", row) for row in results] # pyright: ignore[reportUnknownArgumentType]
|
|
421
553
|
|
|
554
|
+
@overload
|
|
422
555
|
async def select_one(
|
|
423
556
|
self,
|
|
424
557
|
sql: str,
|
|
425
558
|
parameters: "Optional[StatementParameterType]" = None,
|
|
426
559
|
/,
|
|
560
|
+
*,
|
|
561
|
+
connection: "Optional[AsyncConnection]" = None,
|
|
562
|
+
schema_type: None = None,
|
|
563
|
+
**kwargs: Any,
|
|
564
|
+
) -> "dict[str, Any]": ...
|
|
565
|
+
@overload
|
|
566
|
+
async def select_one(
|
|
567
|
+
self,
|
|
568
|
+
sql: str,
|
|
569
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
570
|
+
/,
|
|
571
|
+
*,
|
|
572
|
+
connection: "Optional[AsyncConnection]" = None,
|
|
573
|
+
schema_type: "type[ModelDTOT]",
|
|
574
|
+
**kwargs: Any,
|
|
575
|
+
) -> "ModelDTOT": ...
|
|
576
|
+
async def select_one(
|
|
577
|
+
self,
|
|
578
|
+
sql: str,
|
|
579
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
580
|
+
/,
|
|
581
|
+
*,
|
|
427
582
|
connection: "Optional[AsyncConnection]" = None,
|
|
428
583
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
584
|
+
**kwargs: Any,
|
|
429
585
|
) -> "Union[ModelDTOT, dict[str, Any]]":
|
|
430
586
|
"""Fetch one row from the database.
|
|
431
587
|
|
|
@@ -433,7 +589,7 @@ class PsycopgAsyncDriver(AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
|
433
589
|
The first row of the query results.
|
|
434
590
|
"""
|
|
435
591
|
connection = self._connection(connection)
|
|
436
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
592
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
437
593
|
|
|
438
594
|
async with self._with_cursor(connection) as cursor:
|
|
439
595
|
await cursor.execute(sql, parameters)
|
|
@@ -443,13 +599,37 @@ class PsycopgAsyncDriver(AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
|
443
599
|
return cast("ModelDTOT", schema_type(**cast("dict[str,Any]", row)))
|
|
444
600
|
return cast("dict[str,Any]", row)
|
|
445
601
|
|
|
602
|
+
@overload
|
|
446
603
|
async def select_one_or_none(
|
|
447
604
|
self,
|
|
448
605
|
sql: str,
|
|
449
606
|
parameters: "Optional[StatementParameterType]" = None,
|
|
450
607
|
/,
|
|
608
|
+
*,
|
|
451
609
|
connection: "Optional[AsyncConnection]" = None,
|
|
610
|
+
schema_type: None = None,
|
|
611
|
+
**kwargs: Any,
|
|
612
|
+
) -> "Optional[dict[str, Any]]": ...
|
|
613
|
+
@overload
|
|
614
|
+
async def select_one_or_none(
|
|
615
|
+
self,
|
|
616
|
+
sql: str,
|
|
617
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
618
|
+
/,
|
|
619
|
+
*,
|
|
620
|
+
connection: "Optional[AsyncConnection]" = None,
|
|
621
|
+
schema_type: "type[ModelDTOT]",
|
|
622
|
+
**kwargs: Any,
|
|
623
|
+
) -> "Optional[ModelDTOT]": ...
|
|
624
|
+
async def select_one_or_none(
|
|
625
|
+
self,
|
|
626
|
+
sql: str,
|
|
627
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
628
|
+
/,
|
|
629
|
+
*,
|
|
452
630
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
631
|
+
connection: "Optional[AsyncConnection]" = None,
|
|
632
|
+
**kwargs: Any,
|
|
453
633
|
) -> "Optional[Union[ModelDTOT, dict[str, Any]]]":
|
|
454
634
|
"""Fetch one row from the database.
|
|
455
635
|
|
|
@@ -457,7 +637,7 @@ class PsycopgAsyncDriver(AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
|
457
637
|
The first row of the query results.
|
|
458
638
|
"""
|
|
459
639
|
connection = self._connection(connection)
|
|
460
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
640
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
461
641
|
|
|
462
642
|
async with self._with_cursor(connection) as cursor:
|
|
463
643
|
await cursor.execute(sql, parameters)
|
|
@@ -468,27 +648,52 @@ class PsycopgAsyncDriver(AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
|
468
648
|
return cast("ModelDTOT", schema_type(**cast("dict[str,Any]", row)))
|
|
469
649
|
return cast("dict[str,Any]", row)
|
|
470
650
|
|
|
651
|
+
@overload
|
|
471
652
|
async def select_value(
|
|
472
653
|
self,
|
|
473
654
|
sql: str,
|
|
474
655
|
parameters: "Optional[StatementParameterType]" = None,
|
|
475
656
|
/,
|
|
657
|
+
*,
|
|
658
|
+
connection: "Optional[AsyncConnection]" = None,
|
|
659
|
+
schema_type: None = None,
|
|
660
|
+
**kwargs: Any,
|
|
661
|
+
) -> "Any": ...
|
|
662
|
+
@overload
|
|
663
|
+
async def select_value(
|
|
664
|
+
self,
|
|
665
|
+
sql: str,
|
|
666
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
667
|
+
/,
|
|
668
|
+
*,
|
|
669
|
+
connection: "Optional[AsyncConnection]" = None,
|
|
670
|
+
schema_type: "type[T]",
|
|
671
|
+
**kwargs: Any,
|
|
672
|
+
) -> "T": ...
|
|
673
|
+
async def select_value(
|
|
674
|
+
self,
|
|
675
|
+
sql: str,
|
|
676
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
677
|
+
/,
|
|
678
|
+
*,
|
|
476
679
|
connection: "Optional[AsyncConnection]" = None,
|
|
477
680
|
schema_type: "Optional[type[T]]" = None,
|
|
478
|
-
|
|
681
|
+
**kwargs: Any,
|
|
682
|
+
) -> "Union[T, Any]":
|
|
479
683
|
"""Fetch a single value from the database.
|
|
480
684
|
|
|
481
685
|
Returns:
|
|
482
686
|
The first value from the first row of results, or None if no results.
|
|
483
687
|
"""
|
|
484
688
|
connection = self._connection(connection)
|
|
485
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
689
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
486
690
|
|
|
487
691
|
async with self._with_cursor(connection) as cursor:
|
|
488
692
|
await cursor.execute(sql, parameters)
|
|
489
693
|
row = await cursor.fetchone()
|
|
490
694
|
row = self.check_not_found(row)
|
|
491
|
-
val = next(iter(row))
|
|
695
|
+
val = next(iter(row.values())) if row else None
|
|
696
|
+
val = self.check_not_found(val)
|
|
492
697
|
if schema_type is not None:
|
|
493
698
|
return schema_type(val) # type: ignore[call-arg]
|
|
494
699
|
return val
|
|
@@ -498,8 +703,10 @@ class PsycopgAsyncDriver(AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
|
498
703
|
sql: str,
|
|
499
704
|
parameters: "Optional[StatementParameterType]" = None,
|
|
500
705
|
/,
|
|
706
|
+
*,
|
|
501
707
|
connection: "Optional[AsyncConnection]" = None,
|
|
502
708
|
schema_type: "Optional[type[T]]" = None,
|
|
709
|
+
**kwargs: Any,
|
|
503
710
|
) -> "Optional[Union[T, Any]]":
|
|
504
711
|
"""Fetch a single value from the database.
|
|
505
712
|
|
|
@@ -507,14 +714,16 @@ class PsycopgAsyncDriver(AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
|
507
714
|
The first value from the first row of results, or None if no results.
|
|
508
715
|
"""
|
|
509
716
|
connection = self._connection(connection)
|
|
510
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
717
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
511
718
|
|
|
512
719
|
async with self._with_cursor(connection) as cursor:
|
|
513
720
|
await cursor.execute(sql, parameters)
|
|
514
721
|
row = await cursor.fetchone()
|
|
515
722
|
if row is None:
|
|
516
723
|
return None
|
|
517
|
-
val = next(iter(row))
|
|
724
|
+
val = next(iter(row.values())) if row else None
|
|
725
|
+
if val is None:
|
|
726
|
+
return None
|
|
518
727
|
if schema_type is not None:
|
|
519
728
|
return schema_type(val) # type: ignore[call-arg]
|
|
520
729
|
return val
|
|
@@ -524,15 +733,17 @@ class PsycopgAsyncDriver(AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
|
524
733
|
sql: str,
|
|
525
734
|
parameters: "Optional[StatementParameterType]" = None,
|
|
526
735
|
/,
|
|
736
|
+
*,
|
|
527
737
|
connection: "Optional[AsyncConnection]" = None,
|
|
738
|
+
**kwargs: Any,
|
|
528
739
|
) -> int:
|
|
529
|
-
"""
|
|
740
|
+
"""Execute an INSERT, UPDATE, or DELETE query and return the number of affected rows.
|
|
530
741
|
|
|
531
742
|
Returns:
|
|
532
|
-
|
|
743
|
+
The number of rows affected by the operation.
|
|
533
744
|
"""
|
|
534
745
|
connection = self._connection(connection)
|
|
535
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
746
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
536
747
|
|
|
537
748
|
async with self._with_cursor(connection) as cursor:
|
|
538
749
|
await cursor.execute(sql, parameters)
|
|
@@ -542,13 +753,37 @@ class PsycopgAsyncDriver(AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
|
542
753
|
rowcount = -1
|
|
543
754
|
return rowcount
|
|
544
755
|
|
|
756
|
+
@overload
|
|
757
|
+
async def insert_update_delete_returning(
|
|
758
|
+
self,
|
|
759
|
+
sql: str,
|
|
760
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
761
|
+
/,
|
|
762
|
+
*,
|
|
763
|
+
connection: "Optional[AsyncConnection]" = None,
|
|
764
|
+
schema_type: None = None,
|
|
765
|
+
**kwargs: Any,
|
|
766
|
+
) -> "dict[str, Any]": ...
|
|
767
|
+
@overload
|
|
768
|
+
async def insert_update_delete_returning(
|
|
769
|
+
self,
|
|
770
|
+
sql: str,
|
|
771
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
772
|
+
/,
|
|
773
|
+
*,
|
|
774
|
+
connection: "Optional[AsyncConnection]" = None,
|
|
775
|
+
schema_type: "type[ModelDTOT]",
|
|
776
|
+
**kwargs: Any,
|
|
777
|
+
) -> "ModelDTOT": ...
|
|
545
778
|
async def insert_update_delete_returning(
|
|
546
779
|
self,
|
|
547
780
|
sql: str,
|
|
548
781
|
parameters: "Optional[StatementParameterType]" = None,
|
|
549
782
|
/,
|
|
783
|
+
*,
|
|
550
784
|
connection: "Optional[AsyncConnection]" = None,
|
|
551
785
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
786
|
+
**kwargs: Any,
|
|
552
787
|
) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
|
|
553
788
|
"""Insert, update, or delete data from the database and return result.
|
|
554
789
|
|
|
@@ -556,7 +791,7 @@ class PsycopgAsyncDriver(AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
|
556
791
|
The first row of results.
|
|
557
792
|
"""
|
|
558
793
|
connection = self._connection(connection)
|
|
559
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
794
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
560
795
|
|
|
561
796
|
async with self._with_cursor(connection) as cursor:
|
|
562
797
|
await cursor.execute(sql, parameters)
|
|
@@ -574,7 +809,9 @@ class PsycopgAsyncDriver(AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
|
574
809
|
sql: str,
|
|
575
810
|
parameters: "Optional[StatementParameterType]" = None,
|
|
576
811
|
/,
|
|
812
|
+
*,
|
|
577
813
|
connection: "Optional[AsyncConnection]" = None,
|
|
814
|
+
**kwargs: Any,
|
|
578
815
|
) -> str:
|
|
579
816
|
"""Execute a script.
|
|
580
817
|
|
|
@@ -582,35 +819,8 @@ class PsycopgAsyncDriver(AsyncDriverAdapterProtocol["AsyncConnection"]):
|
|
|
582
819
|
Status message for the operation.
|
|
583
820
|
"""
|
|
584
821
|
connection = self._connection(connection)
|
|
585
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
822
|
+
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
586
823
|
|
|
587
824
|
async with self._with_cursor(connection) as cursor:
|
|
588
825
|
await cursor.execute(sql, parameters)
|
|
589
|
-
return str(cursor.
|
|
590
|
-
|
|
591
|
-
async def execute_script_returning(
|
|
592
|
-
self,
|
|
593
|
-
sql: str,
|
|
594
|
-
parameters: "Optional[StatementParameterType]" = None,
|
|
595
|
-
/,
|
|
596
|
-
connection: "Optional[AsyncConnection]" = None,
|
|
597
|
-
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
598
|
-
) -> "Optional[Union[dict[str, Any], ModelDTOT]]":
|
|
599
|
-
"""Execute a script and return result.
|
|
600
|
-
|
|
601
|
-
Returns:
|
|
602
|
-
The first row of results.
|
|
603
|
-
"""
|
|
604
|
-
connection = self._connection(connection)
|
|
605
|
-
sql, parameters = self._process_sql_params(sql, parameters)
|
|
606
|
-
|
|
607
|
-
async with self._with_cursor(connection) as cursor:
|
|
608
|
-
await cursor.execute(sql, parameters)
|
|
609
|
-
result = await cursor.fetchone()
|
|
610
|
-
|
|
611
|
-
if result is None:
|
|
612
|
-
return None
|
|
613
|
-
|
|
614
|
-
if schema_type is not None:
|
|
615
|
-
return cast("ModelDTOT", schema_type(**result)) # pyright: ignore[reportUnknownArgumentType]
|
|
616
|
-
return cast("dict[str, Any]", result) # pyright: ignore[reportUnknownArgumentType,reportUnknownVariableType]
|
|
826
|
+
return str(cursor.statusmessage) if cursor.statusmessage is not None else "DONE"
|