sqlspec 0.10.1__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.
- sqlspec/adapters/adbc/config.py +1 -1
- sqlspec/adapters/adbc/driver.py +336 -165
- sqlspec/adapters/aiosqlite/driver.py +211 -126
- sqlspec/adapters/asyncmy/driver.py +164 -68
- sqlspec/adapters/asyncpg/config.py +3 -1
- sqlspec/adapters/asyncpg/driver.py +190 -231
- sqlspec/adapters/bigquery/driver.py +178 -169
- sqlspec/adapters/duckdb/driver.py +175 -84
- sqlspec/adapters/oracledb/driver.py +224 -90
- sqlspec/adapters/psqlpy/driver.py +267 -187
- sqlspec/adapters/psycopg/driver.py +138 -184
- sqlspec/adapters/sqlite/driver.py +153 -121
- sqlspec/base.py +57 -45
- sqlspec/extensions/litestar/__init__.py +3 -12
- sqlspec/extensions/litestar/config.py +22 -7
- sqlspec/extensions/litestar/handlers.py +142 -85
- sqlspec/extensions/litestar/plugin.py +9 -8
- sqlspec/extensions/litestar/providers.py +521 -0
- sqlspec/filters.py +214 -11
- sqlspec/mixins.py +152 -2
- sqlspec/statement.py +276 -271
- sqlspec/typing.py +18 -1
- sqlspec/utils/__init__.py +2 -2
- sqlspec/utils/singleton.py +35 -0
- sqlspec/utils/sync_tools.py +90 -151
- sqlspec/utils/text.py +68 -5
- {sqlspec-0.10.1.dist-info → sqlspec-0.11.0.dist-info}/METADATA +5 -1
- {sqlspec-0.10.1.dist-info → sqlspec-0.11.0.dist-info}/RECORD +31 -29
- {sqlspec-0.10.1.dist-info → sqlspec-0.11.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.10.1.dist-info → sqlspec-0.11.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.10.1.dist-info → sqlspec-0.11.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,17 +1,20 @@
|
|
|
1
|
-
# ruff: noqa: PLR0915, PLR0914, PLR0912, C901
|
|
2
1
|
"""Psqlpy Driver Implementation."""
|
|
3
2
|
|
|
4
3
|
import logging
|
|
5
4
|
import re
|
|
6
|
-
from
|
|
5
|
+
from re import Match
|
|
6
|
+
from typing import TYPE_CHECKING, Any, Optional, Union, overload
|
|
7
7
|
|
|
8
8
|
from psqlpy import Connection, QueryResult
|
|
9
9
|
from psqlpy.exceptions import RustPSQLDriverPyBaseError
|
|
10
|
+
from sqlglot import exp
|
|
10
11
|
|
|
11
12
|
from sqlspec.base import AsyncDriverAdapterProtocol
|
|
12
13
|
from sqlspec.exceptions import SQLParsingError
|
|
13
|
-
from sqlspec.
|
|
14
|
-
from sqlspec.
|
|
14
|
+
from sqlspec.filters import StatementFilter
|
|
15
|
+
from sqlspec.mixins import ResultConverter, SQLTranslatorMixin
|
|
16
|
+
from sqlspec.statement import SQLStatement
|
|
17
|
+
from sqlspec.typing import is_dict
|
|
15
18
|
|
|
16
19
|
if TYPE_CHECKING:
|
|
17
20
|
from collections.abc import Sequence
|
|
@@ -22,23 +25,32 @@ if TYPE_CHECKING:
|
|
|
22
25
|
|
|
23
26
|
__all__ = ("PsqlpyConnection", "PsqlpyDriver")
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
#
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
# Improved regex to match question mark placeholders only when they are outside string literals and comments
|
|
29
|
+
# This pattern handles:
|
|
30
|
+
# 1. Single quoted strings with escaped quotes
|
|
31
|
+
# 2. Double quoted strings with escaped quotes
|
|
32
|
+
# 3. Single-line comments (-- to end of line)
|
|
33
|
+
# 4. Multi-line comments (/* to */)
|
|
34
|
+
# 5. Only question marks outside of these contexts are considered parameters
|
|
35
|
+
QUESTION_MARK_PATTERN = re.compile(
|
|
36
|
+
r"""
|
|
37
|
+
(?:'[^']*(?:''[^']*)*') | # Skip single-quoted strings (with '' escapes)
|
|
38
|
+
(?:"[^"]*(?:""[^"]*)*") | # Skip double-quoted strings (with "" escapes)
|
|
39
|
+
(?:--.*?(?:\n|$)) | # Skip single-line comments
|
|
40
|
+
(?:/\*(?:[^*]|\*(?!/))*\*/) | # Skip multi-line comments
|
|
41
|
+
(\?) # Capture only question marks outside of these contexts
|
|
42
|
+
""",
|
|
34
43
|
re.VERBOSE | re.DOTALL,
|
|
35
44
|
)
|
|
45
|
+
|
|
46
|
+
PsqlpyConnection = Connection
|
|
36
47
|
logger = logging.getLogger("sqlspec")
|
|
37
48
|
|
|
38
49
|
|
|
39
50
|
class PsqlpyDriver(
|
|
40
51
|
SQLTranslatorMixin["PsqlpyConnection"],
|
|
41
52
|
AsyncDriverAdapterProtocol["PsqlpyConnection"],
|
|
53
|
+
ResultConverter,
|
|
42
54
|
):
|
|
43
55
|
"""Psqlpy Postgres Driver Adapter."""
|
|
44
56
|
|
|
@@ -53,125 +65,96 @@ class PsqlpyDriver(
|
|
|
53
65
|
sql: str,
|
|
54
66
|
parameters: "Optional[StatementParameterType]" = None,
|
|
55
67
|
/,
|
|
68
|
+
*filters: StatementFilter,
|
|
56
69
|
**kwargs: Any,
|
|
57
|
-
) -> "tuple[str, Optional[Union[tuple[Any, ...],
|
|
70
|
+
) -> "tuple[str, Optional[Union[tuple[Any, ...], dict[str, Any]]]]":
|
|
58
71
|
"""Process SQL and parameters for psqlpy.
|
|
59
72
|
|
|
60
|
-
psqlpy uses $1, $2 style parameters natively.
|
|
61
|
-
This method converts '?' (tuple/list) and ':name' (dict) styles to $n.
|
|
62
|
-
It relies on SQLStatement for initial parameter validation and merging.
|
|
63
|
-
|
|
64
73
|
Args:
|
|
65
|
-
sql:
|
|
66
|
-
parameters:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
Raises:
|
|
70
|
-
SQLParsingError: If the SQL is invalid.
|
|
74
|
+
sql: SQL statement.
|
|
75
|
+
parameters: Query parameters.
|
|
76
|
+
*filters: Statement filters to apply.
|
|
77
|
+
**kwargs: Additional keyword arguments.
|
|
71
78
|
|
|
72
79
|
Returns:
|
|
73
|
-
|
|
80
|
+
The SQL statement and parameters.
|
|
81
|
+
|
|
82
|
+
Raises:
|
|
83
|
+
SQLParsingError: If the SQL parsing fails.
|
|
74
84
|
"""
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
# Case 1: Parameters are a dictionary
|
|
79
|
-
if isinstance(parameters, dict):
|
|
80
|
-
processed_sql_parts: list[str] = []
|
|
81
|
-
ordered_params = []
|
|
82
|
-
last_end = 0
|
|
83
|
-
param_index = 1
|
|
84
|
-
found_params_regex: list[str] = []
|
|
85
|
-
|
|
86
|
-
for match in PARAM_REGEX.finditer(sql):
|
|
87
|
-
if match.group("dquote") or match.group("squote") or match.group("comment"):
|
|
88
|
-
continue
|
|
89
|
-
|
|
90
|
-
if match.group("var_name"): # Finds :var_name
|
|
91
|
-
var_name = match.group("var_name")
|
|
92
|
-
found_params_regex.append(var_name)
|
|
93
|
-
start = match.start("var_name") - 1
|
|
94
|
-
end = match.end("var_name")
|
|
95
|
-
|
|
96
|
-
if var_name not in parameters:
|
|
97
|
-
msg = f"Named parameter ':{var_name}' missing from parameters. SQL: {sql}"
|
|
98
|
-
raise SQLParsingError(msg)
|
|
99
|
-
|
|
100
|
-
processed_sql_parts.extend((sql[last_end:start], f"${param_index}"))
|
|
101
|
-
ordered_params.append(parameters[var_name])
|
|
102
|
-
last_end = end
|
|
103
|
-
param_index += 1
|
|
85
|
+
# Handle scalar parameter by converting to a single-item tuple
|
|
86
|
+
if parameters is not None and not isinstance(parameters, (list, tuple, dict)):
|
|
87
|
+
parameters = (parameters,)
|
|
104
88
|
|
|
105
|
-
|
|
106
|
-
|
|
89
|
+
# Create and process the statement
|
|
90
|
+
statement = SQLStatement(sql=sql, parameters=parameters, kwargs=kwargs, dialect=self.dialect)
|
|
107
91
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
param_index = 1
|
|
128
|
-
last_end = 0
|
|
129
|
-
qmark_found = False
|
|
130
|
-
|
|
131
|
-
for match in QMARK_REGEX.finditer(sql):
|
|
132
|
-
if match.group("dquote") or match.group("squote") or match.group("comment"):
|
|
133
|
-
continue
|
|
134
|
-
|
|
135
|
-
if match.group("qmark"):
|
|
136
|
-
qmark_found = True
|
|
137
|
-
start = match.start("qmark")
|
|
138
|
-
end = match.end("qmark")
|
|
139
|
-
sequence_processed_parts.extend((sql[last_end:start], f"${param_index}"))
|
|
140
|
-
last_end = end
|
|
92
|
+
# Apply any filters
|
|
93
|
+
for filter_obj in filters:
|
|
94
|
+
statement = statement.apply_filter(filter_obj)
|
|
95
|
+
|
|
96
|
+
# Process the statement
|
|
97
|
+
sql, validated_params, parsed_expr = statement.process()
|
|
98
|
+
|
|
99
|
+
if validated_params is None:
|
|
100
|
+
return sql, None # psqlpy can handle None
|
|
101
|
+
|
|
102
|
+
# Convert positional parameters from question mark style to PostgreSQL's $N style
|
|
103
|
+
if isinstance(validated_params, (list, tuple)):
|
|
104
|
+
# Use a counter to generate $1, $2, etc. for each ? in the SQL that's outside strings/comments
|
|
105
|
+
param_index = 0
|
|
106
|
+
|
|
107
|
+
def replace_question_mark(match: Match[str]) -> str:
|
|
108
|
+
# Only process the match if it's not in a skipped context (string/comment)
|
|
109
|
+
if match.group(1): # This is a question mark outside string/comment
|
|
110
|
+
nonlocal param_index
|
|
141
111
|
param_index += 1
|
|
112
|
+
return f"${param_index}"
|
|
113
|
+
# Return the entire matched text unchanged for strings/comments
|
|
114
|
+
return match.group(0)
|
|
115
|
+
|
|
116
|
+
return QUESTION_MARK_PATTERN.sub(replace_question_mark, sql), tuple(validated_params)
|
|
117
|
+
|
|
118
|
+
# If no parsed expression is available, we can't safely transform dictionary parameters
|
|
119
|
+
if is_dict(validated_params) and parsed_expr is None:
|
|
120
|
+
msg = f"psqlpy: SQL parsing failed and dictionary parameters were provided. Cannot determine parameter order without successful parse. SQL: {sql}"
|
|
121
|
+
raise SQLParsingError(msg)
|
|
122
|
+
|
|
123
|
+
# Convert dictionary parameters to the format expected by psqlpy
|
|
124
|
+
if is_dict(validated_params) and parsed_expr is not None:
|
|
125
|
+
# Find all named parameters in the SQL expression
|
|
126
|
+
named_params = []
|
|
127
|
+
|
|
128
|
+
for node in parsed_expr.find_all(exp.Parameter, exp.Placeholder):
|
|
129
|
+
if isinstance(node, exp.Parameter) and node.name and node.name in validated_params:
|
|
130
|
+
named_params.append(node.name)
|
|
131
|
+
elif isinstance(node, exp.Placeholder) and isinstance(node.this, str) and node.this in validated_params:
|
|
132
|
+
named_params.append(node.this)
|
|
133
|
+
|
|
134
|
+
if named_params:
|
|
135
|
+
# Transform the SQL to use $1, $2, etc.
|
|
136
|
+
def convert_named_to_dollar(node: exp.Expression) -> exp.Expression:
|
|
137
|
+
if isinstance(node, exp.Parameter) and node.name and node.name in validated_params:
|
|
138
|
+
idx = named_params.index(node.name) + 1
|
|
139
|
+
return exp.Parameter(this=str(idx))
|
|
140
|
+
if (
|
|
141
|
+
isinstance(node, exp.Placeholder)
|
|
142
|
+
and isinstance(node.this, str)
|
|
143
|
+
and node.this in validated_params
|
|
144
|
+
):
|
|
145
|
+
idx = named_params.index(node.this) + 1
|
|
146
|
+
return exp.Parameter(this=str(idx))
|
|
147
|
+
return node
|
|
148
|
+
|
|
149
|
+
return parsed_expr.transform(convert_named_to_dollar, copy=True).sql(dialect=self.dialect), tuple(
|
|
150
|
+
validated_params[name] for name in named_params
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
# If no named parameters were found in the SQL but dictionary was provided
|
|
154
|
+
return sql, tuple(validated_params.values())
|
|
142
155
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
if parameters and not qmark_found:
|
|
147
|
-
logger.warning("Sequence parameters provided, but no '?' placeholders found. SQL: %s", sql)
|
|
148
|
-
return sql, parameters
|
|
149
|
-
|
|
150
|
-
expected_params = param_index - 1
|
|
151
|
-
actual_params = len(parameters)
|
|
152
|
-
if expected_params != actual_params:
|
|
153
|
-
msg = f"Parameter count mismatch: Expected {expected_params}, got {actual_params}. SQL: {final_sql}"
|
|
154
|
-
raise SQLParsingError(msg)
|
|
155
|
-
|
|
156
|
-
return final_sql, parameters
|
|
157
|
-
|
|
158
|
-
# Case 3: Parameters are None
|
|
159
|
-
if PARAM_REGEX.search(sql) or QMARK_REGEX.search(sql):
|
|
160
|
-
# Perform a simpler check if any placeholders might exist if no params are given
|
|
161
|
-
for match in PARAM_REGEX.finditer(sql):
|
|
162
|
-
if not (match.group("dquote") or match.group("squote") or match.group("comment")) and match.group(
|
|
163
|
-
"var_name"
|
|
164
|
-
):
|
|
165
|
-
msg = f"SQL contains named parameters (:name) but no parameters provided. SQL: {sql}"
|
|
166
|
-
raise SQLParsingError(msg)
|
|
167
|
-
for match in QMARK_REGEX.finditer(sql):
|
|
168
|
-
if not (match.group("dquote") or match.group("squote") or match.group("comment")) and match.group(
|
|
169
|
-
"qmark"
|
|
170
|
-
):
|
|
171
|
-
msg = f"SQL contains positional parameters (?) but no parameters provided. SQL: {sql}"
|
|
172
|
-
raise SQLParsingError(msg)
|
|
173
|
-
|
|
174
|
-
return sql, ()
|
|
156
|
+
# For any other case, return validated params
|
|
157
|
+
return sql, (validated_params,) if not isinstance(validated_params, (list, tuple)) else tuple(validated_params) # type: ignore[unreachable]
|
|
175
158
|
|
|
176
159
|
# --- Public API Methods --- #
|
|
177
160
|
@overload
|
|
@@ -180,7 +163,7 @@ class PsqlpyDriver(
|
|
|
180
163
|
sql: str,
|
|
181
164
|
parameters: "Optional[StatementParameterType]" = None,
|
|
182
165
|
/,
|
|
183
|
-
|
|
166
|
+
*filters: StatementFilter,
|
|
184
167
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
185
168
|
schema_type: None = None,
|
|
186
169
|
**kwargs: Any,
|
|
@@ -191,7 +174,7 @@ class PsqlpyDriver(
|
|
|
191
174
|
sql: str,
|
|
192
175
|
parameters: "Optional[StatementParameterType]" = None,
|
|
193
176
|
/,
|
|
194
|
-
|
|
177
|
+
*filters: StatementFilter,
|
|
195
178
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
196
179
|
schema_type: "type[ModelDTOT]",
|
|
197
180
|
**kwargs: Any,
|
|
@@ -199,22 +182,35 @@ class PsqlpyDriver(
|
|
|
199
182
|
async def select(
|
|
200
183
|
self,
|
|
201
184
|
sql: str,
|
|
202
|
-
parameters: Optional[
|
|
185
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
203
186
|
/,
|
|
204
|
-
|
|
205
|
-
connection: Optional[
|
|
187
|
+
*filters: StatementFilter,
|
|
188
|
+
connection: "Optional[PsqlpyConnection]" = None,
|
|
206
189
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
207
190
|
**kwargs: Any,
|
|
208
191
|
) -> "Sequence[Union[ModelDTOT, dict[str, Any]]]":
|
|
192
|
+
"""Fetch data from the database.
|
|
193
|
+
|
|
194
|
+
Args:
|
|
195
|
+
sql: The SQL query string.
|
|
196
|
+
parameters: The parameters for the query (dict, tuple, list, or None).
|
|
197
|
+
*filters: Statement filters to apply.
|
|
198
|
+
connection: Optional connection override.
|
|
199
|
+
schema_type: Optional schema class for the result.
|
|
200
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
201
|
+
|
|
202
|
+
Returns:
|
|
203
|
+
List of row data as either model instances or dictionaries.
|
|
204
|
+
"""
|
|
209
205
|
connection = self._connection(connection)
|
|
210
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
206
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
211
207
|
parameters = parameters or [] # psqlpy expects a list/tuple
|
|
212
208
|
|
|
213
209
|
results: QueryResult = await connection.fetch(sql, parameters=parameters)
|
|
214
210
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
return
|
|
211
|
+
# Convert to dicts and use ResultConverter
|
|
212
|
+
dict_results = results.result()
|
|
213
|
+
return self.to_schema(dict_results, schema_type=schema_type)
|
|
218
214
|
|
|
219
215
|
@overload
|
|
220
216
|
async def select_one(
|
|
@@ -222,7 +218,7 @@ class PsqlpyDriver(
|
|
|
222
218
|
sql: str,
|
|
223
219
|
parameters: "Optional[StatementParameterType]" = None,
|
|
224
220
|
/,
|
|
225
|
-
|
|
221
|
+
*filters: StatementFilter,
|
|
226
222
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
227
223
|
schema_type: None = None,
|
|
228
224
|
**kwargs: Any,
|
|
@@ -233,7 +229,7 @@ class PsqlpyDriver(
|
|
|
233
229
|
sql: str,
|
|
234
230
|
parameters: "Optional[StatementParameterType]" = None,
|
|
235
231
|
/,
|
|
236
|
-
|
|
232
|
+
*filters: StatementFilter,
|
|
237
233
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
238
234
|
schema_type: "type[ModelDTOT]",
|
|
239
235
|
**kwargs: Any,
|
|
@@ -241,23 +237,38 @@ class PsqlpyDriver(
|
|
|
241
237
|
async def select_one(
|
|
242
238
|
self,
|
|
243
239
|
sql: str,
|
|
244
|
-
parameters: Optional[
|
|
240
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
245
241
|
/,
|
|
246
|
-
|
|
247
|
-
connection: Optional[
|
|
242
|
+
*filters: StatementFilter,
|
|
243
|
+
connection: "Optional[PsqlpyConnection]" = None,
|
|
248
244
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
249
245
|
**kwargs: Any,
|
|
250
246
|
) -> "Union[ModelDTOT, dict[str, Any]]":
|
|
247
|
+
"""Fetch one row from the database.
|
|
248
|
+
|
|
249
|
+
Args:
|
|
250
|
+
sql: The SQL query string.
|
|
251
|
+
parameters: The parameters for the query (dict, tuple, list, or None).
|
|
252
|
+
*filters: Statement filters to apply.
|
|
253
|
+
connection: Optional connection override.
|
|
254
|
+
schema_type: Optional schema class for the result.
|
|
255
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
The first row of the query results.
|
|
259
|
+
"""
|
|
251
260
|
connection = self._connection(connection)
|
|
252
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
261
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
253
262
|
parameters = parameters or []
|
|
254
263
|
|
|
255
264
|
result = await connection.fetch(sql, parameters=parameters)
|
|
256
265
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
266
|
+
# Convert to dict and use ResultConverter
|
|
267
|
+
dict_results = result.result()
|
|
268
|
+
if not dict_results:
|
|
269
|
+
self.check_not_found(None)
|
|
270
|
+
|
|
271
|
+
return self.to_schema(dict_results[0], schema_type=schema_type)
|
|
261
272
|
|
|
262
273
|
@overload
|
|
263
274
|
async def select_one_or_none(
|
|
@@ -265,7 +276,7 @@ class PsqlpyDriver(
|
|
|
265
276
|
sql: str,
|
|
266
277
|
parameters: "Optional[StatementParameterType]" = None,
|
|
267
278
|
/,
|
|
268
|
-
|
|
279
|
+
*filters: StatementFilter,
|
|
269
280
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
270
281
|
schema_type: None = None,
|
|
271
282
|
**kwargs: Any,
|
|
@@ -276,7 +287,7 @@ class PsqlpyDriver(
|
|
|
276
287
|
sql: str,
|
|
277
288
|
parameters: "Optional[StatementParameterType]" = None,
|
|
278
289
|
/,
|
|
279
|
-
|
|
290
|
+
*filters: StatementFilter,
|
|
280
291
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
281
292
|
schema_type: "type[ModelDTOT]",
|
|
282
293
|
**kwargs: Any,
|
|
@@ -284,27 +295,37 @@ class PsqlpyDriver(
|
|
|
284
295
|
async def select_one_or_none(
|
|
285
296
|
self,
|
|
286
297
|
sql: str,
|
|
287
|
-
parameters: Optional[
|
|
298
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
288
299
|
/,
|
|
289
|
-
|
|
290
|
-
connection: Optional[
|
|
300
|
+
*filters: StatementFilter,
|
|
301
|
+
connection: "Optional[PsqlpyConnection]" = None,
|
|
291
302
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
292
303
|
**kwargs: Any,
|
|
293
304
|
) -> "Optional[Union[ModelDTOT, dict[str, Any]]]":
|
|
305
|
+
"""Fetch one row from the database or return None if no rows found.
|
|
306
|
+
|
|
307
|
+
Args:
|
|
308
|
+
sql: The SQL query string.
|
|
309
|
+
parameters: The parameters for the query (dict, tuple, list, or None).
|
|
310
|
+
*filters: Statement filters to apply.
|
|
311
|
+
connection: Optional connection override.
|
|
312
|
+
schema_type: Optional schema class for the result.
|
|
313
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
The first row of the query results, or None if no results found.
|
|
317
|
+
"""
|
|
294
318
|
connection = self._connection(connection)
|
|
295
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
319
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
296
320
|
parameters = parameters or []
|
|
297
321
|
|
|
298
322
|
result = await connection.fetch(sql, parameters=parameters)
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
return None
|
|
303
|
-
return cast("dict[str, Any]", result[0]) # type: ignore[index]
|
|
304
|
-
result = cast("list[ModelDTOT]", result.as_class(as_class=schema_type)) # type: ignore[assignment]
|
|
305
|
-
if len(result) == 0: # type: ignore[arg-type]
|
|
323
|
+
dict_results = result.result()
|
|
324
|
+
|
|
325
|
+
if not dict_results:
|
|
306
326
|
return None
|
|
307
|
-
|
|
327
|
+
|
|
328
|
+
return self.to_schema(dict_results[0], schema_type=schema_type)
|
|
308
329
|
|
|
309
330
|
@overload
|
|
310
331
|
async def select_value(
|
|
@@ -312,7 +333,7 @@ class PsqlpyDriver(
|
|
|
312
333
|
sql: str,
|
|
313
334
|
parameters: "Optional[StatementParameterType]" = None,
|
|
314
335
|
/,
|
|
315
|
-
|
|
336
|
+
*filters: StatementFilter,
|
|
316
337
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
317
338
|
schema_type: None = None,
|
|
318
339
|
**kwargs: Any,
|
|
@@ -323,7 +344,7 @@ class PsqlpyDriver(
|
|
|
323
344
|
sql: str,
|
|
324
345
|
parameters: "Optional[StatementParameterType]" = None,
|
|
325
346
|
/,
|
|
326
|
-
|
|
347
|
+
*filters: StatementFilter,
|
|
327
348
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
328
349
|
schema_type: "type[T]",
|
|
329
350
|
**kwargs: Any,
|
|
@@ -333,16 +354,30 @@ class PsqlpyDriver(
|
|
|
333
354
|
sql: str,
|
|
334
355
|
parameters: "Optional[StatementParameterType]" = None,
|
|
335
356
|
/,
|
|
336
|
-
|
|
357
|
+
*filters: StatementFilter,
|
|
337
358
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
338
359
|
schema_type: "Optional[type[T]]" = None,
|
|
339
360
|
**kwargs: Any,
|
|
340
361
|
) -> "Union[T, Any]":
|
|
362
|
+
"""Fetch a single value from the database.
|
|
363
|
+
|
|
364
|
+
Args:
|
|
365
|
+
sql: The SQL query string.
|
|
366
|
+
parameters: The parameters for the query (dict, tuple, list, or None).
|
|
367
|
+
*filters: Statement filters to apply.
|
|
368
|
+
connection: Optional connection override.
|
|
369
|
+
schema_type: Optional type to convert the result to.
|
|
370
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
The first value of the first row of the query results.
|
|
374
|
+
"""
|
|
341
375
|
connection = self._connection(connection)
|
|
342
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
376
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
343
377
|
parameters = parameters or []
|
|
344
378
|
|
|
345
379
|
value = await connection.fetch_val(sql, parameters=parameters)
|
|
380
|
+
value = self.check_not_found(value)
|
|
346
381
|
|
|
347
382
|
if schema_type is None:
|
|
348
383
|
return value
|
|
@@ -354,7 +389,7 @@ class PsqlpyDriver(
|
|
|
354
389
|
sql: str,
|
|
355
390
|
parameters: "Optional[StatementParameterType]" = None,
|
|
356
391
|
/,
|
|
357
|
-
|
|
392
|
+
*filters: StatementFilter,
|
|
358
393
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
359
394
|
schema_type: None = None,
|
|
360
395
|
**kwargs: Any,
|
|
@@ -365,7 +400,7 @@ class PsqlpyDriver(
|
|
|
365
400
|
sql: str,
|
|
366
401
|
parameters: "Optional[StatementParameterType]" = None,
|
|
367
402
|
/,
|
|
368
|
-
|
|
403
|
+
*filters: StatementFilter,
|
|
369
404
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
370
405
|
schema_type: "type[T]",
|
|
371
406
|
**kwargs: Any,
|
|
@@ -375,13 +410,26 @@ class PsqlpyDriver(
|
|
|
375
410
|
sql: str,
|
|
376
411
|
parameters: "Optional[StatementParameterType]" = None,
|
|
377
412
|
/,
|
|
378
|
-
|
|
413
|
+
*filters: StatementFilter,
|
|
379
414
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
380
415
|
schema_type: "Optional[type[T]]" = None,
|
|
381
416
|
**kwargs: Any,
|
|
382
417
|
) -> "Optional[Union[T, Any]]":
|
|
418
|
+
"""Fetch a single value or None if not found.
|
|
419
|
+
|
|
420
|
+
Args:
|
|
421
|
+
sql: The SQL query string.
|
|
422
|
+
parameters: The parameters for the query (dict, tuple, list, or None).
|
|
423
|
+
*filters: Statement filters to apply.
|
|
424
|
+
connection: Optional connection override.
|
|
425
|
+
schema_type: Optional type to convert the result to.
|
|
426
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
427
|
+
|
|
428
|
+
Returns:
|
|
429
|
+
The first value of the first row of the query results, or None if no results found.
|
|
430
|
+
"""
|
|
383
431
|
connection = self._connection(connection)
|
|
384
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
432
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
385
433
|
parameters = parameters or []
|
|
386
434
|
try:
|
|
387
435
|
value = await connection.fetch_val(sql, parameters=parameters)
|
|
@@ -397,14 +445,26 @@ class PsqlpyDriver(
|
|
|
397
445
|
async def insert_update_delete(
|
|
398
446
|
self,
|
|
399
447
|
sql: str,
|
|
400
|
-
parameters: Optional[
|
|
448
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
401
449
|
/,
|
|
402
|
-
|
|
403
|
-
connection: Optional[
|
|
450
|
+
*filters: StatementFilter,
|
|
451
|
+
connection: "Optional[PsqlpyConnection]" = None,
|
|
404
452
|
**kwargs: Any,
|
|
405
453
|
) -> int:
|
|
454
|
+
"""Execute an insert, update, or delete statement.
|
|
455
|
+
|
|
456
|
+
Args:
|
|
457
|
+
sql: The SQL statement to execute.
|
|
458
|
+
parameters: The parameters for the statement (dict, tuple, list, or None).
|
|
459
|
+
*filters: Statement filters to apply.
|
|
460
|
+
connection: Optional connection override.
|
|
461
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
462
|
+
|
|
463
|
+
Returns:
|
|
464
|
+
The number of rows affected by the statement.
|
|
465
|
+
"""
|
|
406
466
|
connection = self._connection(connection)
|
|
407
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
467
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
408
468
|
parameters = parameters or []
|
|
409
469
|
|
|
410
470
|
await connection.execute(sql, parameters=parameters)
|
|
@@ -418,7 +478,7 @@ class PsqlpyDriver(
|
|
|
418
478
|
sql: str,
|
|
419
479
|
parameters: "Optional[StatementParameterType]" = None,
|
|
420
480
|
/,
|
|
421
|
-
|
|
481
|
+
*filters: StatementFilter,
|
|
422
482
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
423
483
|
schema_type: None = None,
|
|
424
484
|
**kwargs: Any,
|
|
@@ -429,7 +489,7 @@ class PsqlpyDriver(
|
|
|
429
489
|
sql: str,
|
|
430
490
|
parameters: "Optional[StatementParameterType]" = None,
|
|
431
491
|
/,
|
|
432
|
-
|
|
492
|
+
*filters: StatementFilter,
|
|
433
493
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
434
494
|
schema_type: "type[ModelDTOT]",
|
|
435
495
|
**kwargs: Any,
|
|
@@ -437,45 +497,65 @@ class PsqlpyDriver(
|
|
|
437
497
|
async def insert_update_delete_returning(
|
|
438
498
|
self,
|
|
439
499
|
sql: str,
|
|
440
|
-
parameters: Optional[
|
|
500
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
441
501
|
/,
|
|
442
|
-
|
|
443
|
-
connection: Optional[
|
|
502
|
+
*filters: StatementFilter,
|
|
503
|
+
connection: "Optional[PsqlpyConnection]" = None,
|
|
444
504
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
445
505
|
**kwargs: Any,
|
|
446
|
-
) -> "
|
|
506
|
+
) -> "Union[ModelDTOT, dict[str, Any]]":
|
|
507
|
+
"""Insert, update, or delete data from the database and return result.
|
|
508
|
+
|
|
509
|
+
Args:
|
|
510
|
+
sql: The SQL statement to execute.
|
|
511
|
+
parameters: The parameters for the statement (dict, tuple, list, or None).
|
|
512
|
+
*filters: Statement filters to apply.
|
|
513
|
+
connection: Optional connection override.
|
|
514
|
+
schema_type: Optional schema class for the result.
|
|
515
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
The first row of results.
|
|
519
|
+
"""
|
|
447
520
|
connection = self._connection(connection)
|
|
448
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
521
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
449
522
|
parameters = parameters or []
|
|
450
523
|
|
|
451
524
|
result = await connection.execute(sql, parameters=parameters)
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
if len(result) == 0: # type: ignore[arg-type]
|
|
459
|
-
return None
|
|
460
|
-
return cast("ModelDTOT", result[0]) # type: ignore[index]
|
|
525
|
+
dict_results = result.result()
|
|
526
|
+
|
|
527
|
+
if not dict_results:
|
|
528
|
+
self.check_not_found(None)
|
|
529
|
+
|
|
530
|
+
return self.to_schema(dict_results[0], schema_type=schema_type)
|
|
461
531
|
|
|
462
532
|
async def execute_script(
|
|
463
533
|
self,
|
|
464
534
|
sql: str,
|
|
465
|
-
parameters: Optional[
|
|
535
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
466
536
|
/,
|
|
467
|
-
|
|
468
|
-
connection: Optional["PsqlpyConnection"] = None,
|
|
537
|
+
connection: "Optional[PsqlpyConnection]" = None,
|
|
469
538
|
**kwargs: Any,
|
|
470
539
|
) -> str:
|
|
540
|
+
"""Execute a SQL script.
|
|
541
|
+
|
|
542
|
+
Args:
|
|
543
|
+
sql: The SQL script to execute.
|
|
544
|
+
parameters: The parameters for the script (dict, tuple, list, or None).
|
|
545
|
+
connection: Optional connection override.
|
|
546
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
547
|
+
|
|
548
|
+
Returns:
|
|
549
|
+
A success message.
|
|
550
|
+
"""
|
|
471
551
|
connection = self._connection(connection)
|
|
472
552
|
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
473
553
|
parameters = parameters or []
|
|
474
554
|
|
|
475
555
|
await connection.execute(sql, parameters=parameters)
|
|
476
|
-
return
|
|
556
|
+
return "Script executed successfully"
|
|
477
557
|
|
|
478
|
-
def _connection(self, connection: Optional[
|
|
558
|
+
def _connection(self, connection: "Optional[PsqlpyConnection]" = None) -> "PsqlpyConnection":
|
|
479
559
|
"""Get the connection to use.
|
|
480
560
|
|
|
481
561
|
Args:
|