sqlspec 0.10.1__py3-none-any.whl → 0.11.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sqlspec might be problematic. Click here for more details.
- sqlspec/adapters/adbc/config.py +1 -1
- sqlspec/adapters/adbc/driver.py +340 -192
- sqlspec/adapters/aiosqlite/driver.py +183 -129
- sqlspec/adapters/asyncmy/driver.py +168 -88
- sqlspec/adapters/asyncpg/config.py +3 -1
- sqlspec/adapters/asyncpg/driver.py +208 -259
- sqlspec/adapters/bigquery/driver.py +184 -264
- sqlspec/adapters/duckdb/driver.py +172 -110
- sqlspec/adapters/oracledb/driver.py +274 -160
- sqlspec/adapters/psqlpy/driver.py +274 -211
- sqlspec/adapters/psycopg/driver.py +196 -283
- sqlspec/adapters/sqlite/driver.py +154 -142
- sqlspec/base.py +56 -85
- 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 +215 -11
- sqlspec/mixins.py +161 -12
- 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.1.dist-info}/METADATA +8 -1
- {sqlspec-0.10.1.dist-info → sqlspec-0.11.1.dist-info}/RECORD +31 -29
- {sqlspec-0.10.1.dist-info → sqlspec-0.11.1.dist-info}/WHEEL +0 -0
- {sqlspec-0.10.1.dist-info → sqlspec-0.11.1.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.10.1.dist-info → sqlspec-0.11.1.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,20 +1,23 @@
|
|
|
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
|
-
from collections.abc import Sequence
|
|
20
|
+
from collections.abc import Mapping, Sequence
|
|
18
21
|
|
|
19
22
|
from psqlpy import QueryResult
|
|
20
23
|
|
|
@@ -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
|
|
|
@@ -52,126 +64,100 @@ class PsqlpyDriver(
|
|
|
52
64
|
self,
|
|
53
65
|
sql: str,
|
|
54
66
|
parameters: "Optional[StatementParameterType]" = None,
|
|
55
|
-
|
|
67
|
+
*filters: "StatementFilter",
|
|
56
68
|
**kwargs: Any,
|
|
57
|
-
) -> "tuple[str, Optional[Union[tuple[Any, ...],
|
|
69
|
+
) -> "tuple[str, Optional[Union[tuple[Any, ...], dict[str, Any]]]]":
|
|
58
70
|
"""Process SQL and parameters for psqlpy.
|
|
59
71
|
|
|
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
72
|
Args:
|
|
65
|
-
sql:
|
|
66
|
-
parameters:
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
Raises:
|
|
70
|
-
SQLParsingError: If the SQL is invalid.
|
|
73
|
+
sql: SQL statement.
|
|
74
|
+
parameters: Query parameters.
|
|
75
|
+
*filters: Statement filters to apply.
|
|
76
|
+
**kwargs: Additional keyword arguments.
|
|
71
77
|
|
|
72
78
|
Returns:
|
|
73
|
-
|
|
79
|
+
The SQL statement and parameters.
|
|
80
|
+
|
|
81
|
+
Raises:
|
|
82
|
+
SQLParsingError: If the SQL parsing fails.
|
|
74
83
|
"""
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
84
|
+
data_params_for_statement: Optional[Union[Mapping[str, Any], Sequence[Any]]] = None
|
|
85
|
+
combined_filters_list: list[StatementFilter] = list(filters)
|
|
86
|
+
|
|
87
|
+
if parameters is not None:
|
|
88
|
+
if isinstance(parameters, StatementFilter):
|
|
89
|
+
combined_filters_list.insert(0, parameters)
|
|
90
|
+
else:
|
|
91
|
+
data_params_for_statement = parameters
|
|
92
|
+
if data_params_for_statement is not None and not isinstance(data_params_for_statement, (list, tuple, dict)):
|
|
93
|
+
data_params_for_statement = (data_params_for_statement,)
|
|
94
|
+
statement = SQLStatement(sql, data_params_for_statement, kwargs=kwargs, dialect=self.dialect)
|
|
95
|
+
|
|
96
|
+
for filter_obj in combined_filters_list:
|
|
97
|
+
statement = statement.apply_filter(filter_obj)
|
|
98
|
+
|
|
99
|
+
# Process the statement
|
|
100
|
+
sql, validated_params, parsed_expr = statement.process()
|
|
101
|
+
|
|
102
|
+
if validated_params is None:
|
|
103
|
+
return sql, None # psqlpy can handle None
|
|
104
|
+
|
|
105
|
+
# Convert positional parameters from question mark style to PostgreSQL's $N style
|
|
106
|
+
if isinstance(validated_params, (list, tuple)):
|
|
107
|
+
# Use a counter to generate $1, $2, etc. for each ? in the SQL that's outside strings/comments
|
|
108
|
+
param_index = 0
|
|
109
|
+
|
|
110
|
+
def replace_question_mark(match: Match[str]) -> str:
|
|
111
|
+
# Only process the match if it's not in a skipped context (string/comment)
|
|
112
|
+
if match.group(1): # This is a question mark outside string/comment
|
|
113
|
+
nonlocal param_index
|
|
103
114
|
param_index += 1
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
115
|
+
return f"${param_index}"
|
|
116
|
+
# Return the entire matched text unchanged for strings/comments
|
|
117
|
+
return match.group(0)
|
|
118
|
+
|
|
119
|
+
return QUESTION_MARK_PATTERN.sub(replace_question_mark, sql), tuple(validated_params)
|
|
120
|
+
|
|
121
|
+
# If no parsed expression is available, we can't safely transform dictionary parameters
|
|
122
|
+
if is_dict(validated_params) and parsed_expr is None:
|
|
123
|
+
msg = f"psqlpy: SQL parsing failed and dictionary parameters were provided. Cannot determine parameter order without successful parse. SQL: {sql}"
|
|
124
|
+
raise SQLParsingError(msg)
|
|
125
|
+
|
|
126
|
+
# Convert dictionary parameters to the format expected by psqlpy
|
|
127
|
+
if is_dict(validated_params) and parsed_expr is not None:
|
|
128
|
+
# Find all named parameters in the SQL expression
|
|
129
|
+
named_params = []
|
|
130
|
+
|
|
131
|
+
for node in parsed_expr.find_all(exp.Parameter, exp.Placeholder):
|
|
132
|
+
if isinstance(node, exp.Parameter) and node.name and node.name in validated_params:
|
|
133
|
+
named_params.append(node.name)
|
|
134
|
+
elif isinstance(node, exp.Placeholder) and isinstance(node.this, str) and node.this in validated_params:
|
|
135
|
+
named_params.append(node.this)
|
|
136
|
+
|
|
137
|
+
if named_params:
|
|
138
|
+
# Transform the SQL to use $1, $2, etc.
|
|
139
|
+
def convert_named_to_dollar(node: exp.Expression) -> exp.Expression:
|
|
140
|
+
if isinstance(node, exp.Parameter) and node.name and node.name in validated_params:
|
|
141
|
+
idx = named_params.index(node.name) + 1
|
|
142
|
+
return exp.Parameter(this=str(idx))
|
|
143
|
+
if (
|
|
144
|
+
isinstance(node, exp.Placeholder)
|
|
145
|
+
and isinstance(node.this, str)
|
|
146
|
+
and node.this in validated_params
|
|
147
|
+
):
|
|
148
|
+
idx = named_params.index(node.this) + 1
|
|
149
|
+
return exp.Parameter(this=str(idx))
|
|
150
|
+
return node
|
|
151
|
+
|
|
152
|
+
return parsed_expr.transform(convert_named_to_dollar, copy=True).sql(dialect=self.dialect), tuple(
|
|
153
|
+
validated_params[name] for name in named_params
|
|
113
154
|
)
|
|
114
|
-
return sql, ()
|
|
115
|
-
|
|
116
|
-
provided_keys = set(parameters.keys())
|
|
117
|
-
found_keys = set(found_params_regex)
|
|
118
|
-
unused_keys = provided_keys - found_keys
|
|
119
|
-
if unused_keys:
|
|
120
|
-
logger.warning("Unused parameters provided: %s. SQL: %s", unused_keys, sql)
|
|
121
|
-
|
|
122
|
-
return final_sql, tuple(ordered_params)
|
|
123
|
-
|
|
124
|
-
# Case 2: Parameters are a sequence/scalar
|
|
125
|
-
if isinstance(parameters, (list, tuple)):
|
|
126
|
-
sequence_processed_parts: list[str] = []
|
|
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
|
|
141
|
-
param_index += 1
|
|
142
155
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
+
# If no named parameters were found in the SQL but dictionary was provided
|
|
157
|
+
return sql, tuple(validated_params.values())
|
|
158
|
+
|
|
159
|
+
# For any other case, return validated params
|
|
160
|
+
return sql, (validated_params,) if not isinstance(validated_params, (list, tuple)) else tuple(validated_params) # type: ignore[unreachable]
|
|
175
161
|
|
|
176
162
|
# --- Public API Methods --- #
|
|
177
163
|
@overload
|
|
@@ -179,8 +165,7 @@ class PsqlpyDriver(
|
|
|
179
165
|
self,
|
|
180
166
|
sql: str,
|
|
181
167
|
parameters: "Optional[StatementParameterType]" = None,
|
|
182
|
-
|
|
183
|
-
*,
|
|
168
|
+
*filters: "StatementFilter",
|
|
184
169
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
185
170
|
schema_type: None = None,
|
|
186
171
|
**kwargs: Any,
|
|
@@ -190,8 +175,7 @@ class PsqlpyDriver(
|
|
|
190
175
|
self,
|
|
191
176
|
sql: str,
|
|
192
177
|
parameters: "Optional[StatementParameterType]" = None,
|
|
193
|
-
|
|
194
|
-
*,
|
|
178
|
+
*filters: "StatementFilter",
|
|
195
179
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
196
180
|
schema_type: "type[ModelDTOT]",
|
|
197
181
|
**kwargs: Any,
|
|
@@ -199,30 +183,41 @@ class PsqlpyDriver(
|
|
|
199
183
|
async def select(
|
|
200
184
|
self,
|
|
201
185
|
sql: str,
|
|
202
|
-
parameters: Optional[
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
connection: Optional["PsqlpyConnection"] = None,
|
|
186
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
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(
|
|
221
217
|
self,
|
|
222
218
|
sql: str,
|
|
223
219
|
parameters: "Optional[StatementParameterType]" = None,
|
|
224
|
-
|
|
225
|
-
*,
|
|
220
|
+
*filters: "StatementFilter",
|
|
226
221
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
227
222
|
schema_type: None = None,
|
|
228
223
|
**kwargs: Any,
|
|
@@ -232,8 +227,7 @@ class PsqlpyDriver(
|
|
|
232
227
|
self,
|
|
233
228
|
sql: str,
|
|
234
229
|
parameters: "Optional[StatementParameterType]" = None,
|
|
235
|
-
|
|
236
|
-
*,
|
|
230
|
+
*filters: "StatementFilter",
|
|
237
231
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
238
232
|
schema_type: "type[ModelDTOT]",
|
|
239
233
|
**kwargs: Any,
|
|
@@ -241,31 +235,44 @@ class PsqlpyDriver(
|
|
|
241
235
|
async def select_one(
|
|
242
236
|
self,
|
|
243
237
|
sql: str,
|
|
244
|
-
parameters: Optional[
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
connection: Optional["PsqlpyConnection"] = None,
|
|
238
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
239
|
+
*filters: "StatementFilter",
|
|
240
|
+
connection: "Optional[PsqlpyConnection]" = None,
|
|
248
241
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
249
242
|
**kwargs: Any,
|
|
250
243
|
) -> "Union[ModelDTOT, dict[str, Any]]":
|
|
244
|
+
"""Fetch one row from the database.
|
|
245
|
+
|
|
246
|
+
Args:
|
|
247
|
+
sql: The SQL query string.
|
|
248
|
+
parameters: The parameters for the query (dict, tuple, list, or None).
|
|
249
|
+
*filters: Statement filters to apply.
|
|
250
|
+
connection: Optional connection override.
|
|
251
|
+
schema_type: Optional schema class for the result.
|
|
252
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
253
|
+
|
|
254
|
+
Returns:
|
|
255
|
+
The first row of the query results.
|
|
256
|
+
"""
|
|
251
257
|
connection = self._connection(connection)
|
|
252
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
258
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
253
259
|
parameters = parameters or []
|
|
254
260
|
|
|
255
261
|
result = await connection.fetch(sql, parameters=parameters)
|
|
256
262
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
263
|
+
# Convert to dict and use ResultConverter
|
|
264
|
+
dict_results = result.result()
|
|
265
|
+
if not dict_results:
|
|
266
|
+
self.check_not_found(None)
|
|
267
|
+
|
|
268
|
+
return self.to_schema(dict_results[0], schema_type=schema_type)
|
|
261
269
|
|
|
262
270
|
@overload
|
|
263
271
|
async def select_one_or_none(
|
|
264
272
|
self,
|
|
265
273
|
sql: str,
|
|
266
274
|
parameters: "Optional[StatementParameterType]" = None,
|
|
267
|
-
|
|
268
|
-
*,
|
|
275
|
+
*filters: "StatementFilter",
|
|
269
276
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
270
277
|
schema_type: None = None,
|
|
271
278
|
**kwargs: Any,
|
|
@@ -275,8 +282,7 @@ class PsqlpyDriver(
|
|
|
275
282
|
self,
|
|
276
283
|
sql: str,
|
|
277
284
|
parameters: "Optional[StatementParameterType]" = None,
|
|
278
|
-
|
|
279
|
-
*,
|
|
285
|
+
*filters: "StatementFilter",
|
|
280
286
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
281
287
|
schema_type: "type[ModelDTOT]",
|
|
282
288
|
**kwargs: Any,
|
|
@@ -284,35 +290,43 @@ class PsqlpyDriver(
|
|
|
284
290
|
async def select_one_or_none(
|
|
285
291
|
self,
|
|
286
292
|
sql: str,
|
|
287
|
-
parameters: Optional[
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
connection: Optional["PsqlpyConnection"] = None,
|
|
293
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
294
|
+
*filters: "StatementFilter",
|
|
295
|
+
connection: "Optional[PsqlpyConnection]" = None,
|
|
291
296
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
292
297
|
**kwargs: Any,
|
|
293
298
|
) -> "Optional[Union[ModelDTOT, dict[str, Any]]]":
|
|
299
|
+
"""Fetch one row from the database or return None if no rows found.
|
|
300
|
+
|
|
301
|
+
Args:
|
|
302
|
+
sql: The SQL query string.
|
|
303
|
+
parameters: The parameters for the query (dict, tuple, list, or None).
|
|
304
|
+
*filters: Statement filters to apply.
|
|
305
|
+
connection: Optional connection override.
|
|
306
|
+
schema_type: Optional schema class for the result.
|
|
307
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
308
|
+
|
|
309
|
+
Returns:
|
|
310
|
+
The first row of the query results, or None if no results found.
|
|
311
|
+
"""
|
|
294
312
|
connection = self._connection(connection)
|
|
295
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
313
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
296
314
|
parameters = parameters or []
|
|
297
315
|
|
|
298
316
|
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]
|
|
317
|
+
dict_results = result.result()
|
|
318
|
+
|
|
319
|
+
if not dict_results:
|
|
306
320
|
return None
|
|
307
|
-
|
|
321
|
+
|
|
322
|
+
return self.to_schema(dict_results[0], schema_type=schema_type)
|
|
308
323
|
|
|
309
324
|
@overload
|
|
310
325
|
async def select_value(
|
|
311
326
|
self,
|
|
312
327
|
sql: str,
|
|
313
328
|
parameters: "Optional[StatementParameterType]" = None,
|
|
314
|
-
|
|
315
|
-
*,
|
|
329
|
+
*filters: "StatementFilter",
|
|
316
330
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
317
331
|
schema_type: None = None,
|
|
318
332
|
**kwargs: Any,
|
|
@@ -322,8 +336,7 @@ class PsqlpyDriver(
|
|
|
322
336
|
self,
|
|
323
337
|
sql: str,
|
|
324
338
|
parameters: "Optional[StatementParameterType]" = None,
|
|
325
|
-
|
|
326
|
-
*,
|
|
339
|
+
*filters: "StatementFilter",
|
|
327
340
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
328
341
|
schema_type: "type[T]",
|
|
329
342
|
**kwargs: Any,
|
|
@@ -332,17 +345,30 @@ class PsqlpyDriver(
|
|
|
332
345
|
self,
|
|
333
346
|
sql: str,
|
|
334
347
|
parameters: "Optional[StatementParameterType]" = None,
|
|
335
|
-
|
|
336
|
-
*,
|
|
348
|
+
*filters: "StatementFilter",
|
|
337
349
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
338
350
|
schema_type: "Optional[type[T]]" = None,
|
|
339
351
|
**kwargs: Any,
|
|
340
352
|
) -> "Union[T, Any]":
|
|
353
|
+
"""Fetch a single value from the database.
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
sql: The SQL query string.
|
|
357
|
+
parameters: The parameters for the query (dict, tuple, list, or None).
|
|
358
|
+
*filters: Statement filters to apply.
|
|
359
|
+
connection: Optional connection override.
|
|
360
|
+
schema_type: Optional type to convert the result to.
|
|
361
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
362
|
+
|
|
363
|
+
Returns:
|
|
364
|
+
The first value of the first row of the query results.
|
|
365
|
+
"""
|
|
341
366
|
connection = self._connection(connection)
|
|
342
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
367
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
343
368
|
parameters = parameters or []
|
|
344
369
|
|
|
345
370
|
value = await connection.fetch_val(sql, parameters=parameters)
|
|
371
|
+
value = self.check_not_found(value)
|
|
346
372
|
|
|
347
373
|
if schema_type is None:
|
|
348
374
|
return value
|
|
@@ -353,8 +379,7 @@ class PsqlpyDriver(
|
|
|
353
379
|
self,
|
|
354
380
|
sql: str,
|
|
355
381
|
parameters: "Optional[StatementParameterType]" = None,
|
|
356
|
-
|
|
357
|
-
*,
|
|
382
|
+
*filters: "StatementFilter",
|
|
358
383
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
359
384
|
schema_type: None = None,
|
|
360
385
|
**kwargs: Any,
|
|
@@ -364,8 +389,7 @@ class PsqlpyDriver(
|
|
|
364
389
|
self,
|
|
365
390
|
sql: str,
|
|
366
391
|
parameters: "Optional[StatementParameterType]" = None,
|
|
367
|
-
|
|
368
|
-
*,
|
|
392
|
+
*filters: "StatementFilter",
|
|
369
393
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
370
394
|
schema_type: "type[T]",
|
|
371
395
|
**kwargs: Any,
|
|
@@ -374,14 +398,26 @@ class PsqlpyDriver(
|
|
|
374
398
|
self,
|
|
375
399
|
sql: str,
|
|
376
400
|
parameters: "Optional[StatementParameterType]" = None,
|
|
377
|
-
|
|
378
|
-
*,
|
|
401
|
+
*filters: "StatementFilter",
|
|
379
402
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
380
403
|
schema_type: "Optional[type[T]]" = None,
|
|
381
404
|
**kwargs: Any,
|
|
382
405
|
) -> "Optional[Union[T, Any]]":
|
|
406
|
+
"""Fetch a single value or None if not found.
|
|
407
|
+
|
|
408
|
+
Args:
|
|
409
|
+
sql: The SQL query string.
|
|
410
|
+
parameters: The parameters for the query (dict, tuple, list, or None).
|
|
411
|
+
*filters: Statement filters to apply.
|
|
412
|
+
connection: Optional connection override.
|
|
413
|
+
schema_type: Optional type to convert the result to.
|
|
414
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
415
|
+
|
|
416
|
+
Returns:
|
|
417
|
+
The first value of the first row of the query results, or None if no results found.
|
|
418
|
+
"""
|
|
383
419
|
connection = self._connection(connection)
|
|
384
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
420
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
385
421
|
parameters = parameters or []
|
|
386
422
|
try:
|
|
387
423
|
value = await connection.fetch_val(sql, parameters=parameters)
|
|
@@ -397,14 +433,25 @@ class PsqlpyDriver(
|
|
|
397
433
|
async def insert_update_delete(
|
|
398
434
|
self,
|
|
399
435
|
sql: str,
|
|
400
|
-
parameters: Optional[
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
connection: Optional["PsqlpyConnection"] = None,
|
|
436
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
437
|
+
*filters: "StatementFilter",
|
|
438
|
+
connection: "Optional[PsqlpyConnection]" = None,
|
|
404
439
|
**kwargs: Any,
|
|
405
440
|
) -> int:
|
|
441
|
+
"""Execute an insert, update, or delete statement.
|
|
442
|
+
|
|
443
|
+
Args:
|
|
444
|
+
sql: The SQL statement to execute.
|
|
445
|
+
parameters: The parameters for the statement (dict, tuple, list, or None).
|
|
446
|
+
*filters: Statement filters to apply.
|
|
447
|
+
connection: Optional connection override.
|
|
448
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
The number of rows affected by the statement.
|
|
452
|
+
"""
|
|
406
453
|
connection = self._connection(connection)
|
|
407
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
454
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
408
455
|
parameters = parameters or []
|
|
409
456
|
|
|
410
457
|
await connection.execute(sql, parameters=parameters)
|
|
@@ -417,8 +464,7 @@ class PsqlpyDriver(
|
|
|
417
464
|
self,
|
|
418
465
|
sql: str,
|
|
419
466
|
parameters: "Optional[StatementParameterType]" = None,
|
|
420
|
-
|
|
421
|
-
*,
|
|
467
|
+
*filters: "StatementFilter",
|
|
422
468
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
423
469
|
schema_type: None = None,
|
|
424
470
|
**kwargs: Any,
|
|
@@ -428,8 +474,7 @@ class PsqlpyDriver(
|
|
|
428
474
|
self,
|
|
429
475
|
sql: str,
|
|
430
476
|
parameters: "Optional[StatementParameterType]" = None,
|
|
431
|
-
|
|
432
|
-
*,
|
|
477
|
+
*filters: "StatementFilter",
|
|
433
478
|
connection: "Optional[PsqlpyConnection]" = None,
|
|
434
479
|
schema_type: "type[ModelDTOT]",
|
|
435
480
|
**kwargs: Any,
|
|
@@ -437,45 +482,63 @@ class PsqlpyDriver(
|
|
|
437
482
|
async def insert_update_delete_returning(
|
|
438
483
|
self,
|
|
439
484
|
sql: str,
|
|
440
|
-
parameters: Optional[
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
connection: Optional["PsqlpyConnection"] = None,
|
|
485
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
486
|
+
*filters: "StatementFilter",
|
|
487
|
+
connection: "Optional[PsqlpyConnection]" = None,
|
|
444
488
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
445
489
|
**kwargs: Any,
|
|
446
|
-
) -> "
|
|
490
|
+
) -> "Union[ModelDTOT, dict[str, Any]]":
|
|
491
|
+
"""Insert, update, or delete data with RETURNING clause.
|
|
492
|
+
|
|
493
|
+
Args:
|
|
494
|
+
sql: The SQL statement with RETURNING clause.
|
|
495
|
+
parameters: The parameters for the statement (dict, tuple, list, or None).
|
|
496
|
+
*filters: Statement filters to apply.
|
|
497
|
+
connection: Optional connection override.
|
|
498
|
+
schema_type: Optional schema class for the result.
|
|
499
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
500
|
+
|
|
501
|
+
Returns:
|
|
502
|
+
The returned row data, as either a model instance or dictionary.
|
|
503
|
+
"""
|
|
447
504
|
connection = self._connection(connection)
|
|
448
|
-
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
505
|
+
sql, parameters = self._process_sql_params(sql, parameters, *filters, **kwargs)
|
|
449
506
|
parameters = parameters or []
|
|
450
507
|
|
|
451
|
-
result = await connection.
|
|
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]
|
|
508
|
+
result = await connection.fetch(sql, parameters=parameters)
|
|
509
|
+
|
|
510
|
+
dict_results = result.result()
|
|
511
|
+
if not dict_results:
|
|
512
|
+
self.check_not_found(None)
|
|
513
|
+
|
|
514
|
+
return self.to_schema(dict_results[0], schema_type=schema_type)
|
|
461
515
|
|
|
462
516
|
async def execute_script(
|
|
463
517
|
self,
|
|
464
518
|
sql: str,
|
|
465
|
-
parameters: Optional[
|
|
466
|
-
|
|
467
|
-
*,
|
|
468
|
-
connection: Optional["PsqlpyConnection"] = None,
|
|
519
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
520
|
+
connection: "Optional[PsqlpyConnection]" = None,
|
|
469
521
|
**kwargs: Any,
|
|
470
522
|
) -> str:
|
|
523
|
+
"""Execute a SQL script.
|
|
524
|
+
|
|
525
|
+
Args:
|
|
526
|
+
sql: The SQL script to execute.
|
|
527
|
+
parameters: The parameters for the script (dict, tuple, list, or None).
|
|
528
|
+
connection: Optional connection override.
|
|
529
|
+
**kwargs: Additional keyword arguments to merge with parameters if parameters is a dict.
|
|
530
|
+
|
|
531
|
+
Returns:
|
|
532
|
+
A success message.
|
|
533
|
+
"""
|
|
471
534
|
connection = self._connection(connection)
|
|
472
535
|
sql, parameters = self._process_sql_params(sql, parameters, **kwargs)
|
|
473
536
|
parameters = parameters or []
|
|
474
537
|
|
|
475
538
|
await connection.execute(sql, parameters=parameters)
|
|
476
|
-
return
|
|
539
|
+
return "Script executed successfully"
|
|
477
540
|
|
|
478
|
-
def _connection(self, connection: Optional[
|
|
541
|
+
def _connection(self, connection: "Optional[PsqlpyConnection]" = None) -> "PsqlpyConnection":
|
|
479
542
|
"""Get the connection to use.
|
|
480
543
|
|
|
481
544
|
Args:
|