sqlspec 0.11.1__py3-none-any.whl → 0.12.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/__init__.py +16 -3
- sqlspec/_serialization.py +3 -10
- sqlspec/_sql.py +1147 -0
- sqlspec/_typing.py +343 -41
- sqlspec/adapters/adbc/__init__.py +2 -6
- sqlspec/adapters/adbc/config.py +474 -149
- sqlspec/adapters/adbc/driver.py +330 -621
- sqlspec/adapters/aiosqlite/__init__.py +2 -6
- sqlspec/adapters/aiosqlite/config.py +143 -57
- sqlspec/adapters/aiosqlite/driver.py +269 -431
- sqlspec/adapters/asyncmy/__init__.py +3 -8
- sqlspec/adapters/asyncmy/config.py +247 -202
- sqlspec/adapters/asyncmy/driver.py +218 -436
- sqlspec/adapters/asyncpg/__init__.py +4 -7
- sqlspec/adapters/asyncpg/config.py +329 -176
- sqlspec/adapters/asyncpg/driver.py +417 -487
- sqlspec/adapters/bigquery/__init__.py +2 -2
- sqlspec/adapters/bigquery/config.py +407 -0
- sqlspec/adapters/bigquery/driver.py +600 -553
- sqlspec/adapters/duckdb/__init__.py +4 -1
- sqlspec/adapters/duckdb/config.py +432 -321
- sqlspec/adapters/duckdb/driver.py +392 -406
- sqlspec/adapters/oracledb/__init__.py +3 -8
- sqlspec/adapters/oracledb/config.py +625 -0
- sqlspec/adapters/oracledb/driver.py +548 -921
- sqlspec/adapters/psqlpy/__init__.py +4 -7
- sqlspec/adapters/psqlpy/config.py +372 -203
- sqlspec/adapters/psqlpy/driver.py +197 -533
- sqlspec/adapters/psycopg/__init__.py +3 -8
- sqlspec/adapters/psycopg/config.py +725 -0
- sqlspec/adapters/psycopg/driver.py +734 -694
- sqlspec/adapters/sqlite/__init__.py +2 -6
- sqlspec/adapters/sqlite/config.py +146 -81
- sqlspec/adapters/sqlite/driver.py +242 -405
- sqlspec/base.py +220 -784
- sqlspec/config.py +354 -0
- sqlspec/driver/__init__.py +22 -0
- sqlspec/driver/_async.py +252 -0
- sqlspec/driver/_common.py +338 -0
- sqlspec/driver/_sync.py +261 -0
- sqlspec/driver/mixins/__init__.py +17 -0
- sqlspec/driver/mixins/_pipeline.py +523 -0
- sqlspec/driver/mixins/_result_utils.py +122 -0
- sqlspec/driver/mixins/_sql_translator.py +35 -0
- sqlspec/driver/mixins/_storage.py +993 -0
- sqlspec/driver/mixins/_type_coercion.py +131 -0
- sqlspec/exceptions.py +299 -7
- sqlspec/extensions/aiosql/__init__.py +10 -0
- sqlspec/extensions/aiosql/adapter.py +474 -0
- sqlspec/extensions/litestar/__init__.py +1 -6
- sqlspec/extensions/litestar/_utils.py +1 -5
- sqlspec/extensions/litestar/config.py +5 -6
- sqlspec/extensions/litestar/handlers.py +13 -12
- sqlspec/extensions/litestar/plugin.py +22 -24
- sqlspec/extensions/litestar/providers.py +37 -55
- sqlspec/loader.py +528 -0
- sqlspec/service/__init__.py +3 -0
- sqlspec/service/base.py +24 -0
- sqlspec/service/pagination.py +26 -0
- sqlspec/statement/__init__.py +21 -0
- sqlspec/statement/builder/__init__.py +54 -0
- sqlspec/statement/builder/_ddl_utils.py +119 -0
- sqlspec/statement/builder/_parsing_utils.py +135 -0
- sqlspec/statement/builder/base.py +328 -0
- sqlspec/statement/builder/ddl.py +1379 -0
- sqlspec/statement/builder/delete.py +80 -0
- sqlspec/statement/builder/insert.py +274 -0
- sqlspec/statement/builder/merge.py +95 -0
- sqlspec/statement/builder/mixins/__init__.py +65 -0
- sqlspec/statement/builder/mixins/_aggregate_functions.py +151 -0
- sqlspec/statement/builder/mixins/_case_builder.py +91 -0
- sqlspec/statement/builder/mixins/_common_table_expr.py +91 -0
- sqlspec/statement/builder/mixins/_delete_from.py +34 -0
- sqlspec/statement/builder/mixins/_from.py +61 -0
- sqlspec/statement/builder/mixins/_group_by.py +119 -0
- sqlspec/statement/builder/mixins/_having.py +35 -0
- sqlspec/statement/builder/mixins/_insert_from_select.py +48 -0
- sqlspec/statement/builder/mixins/_insert_into.py +36 -0
- sqlspec/statement/builder/mixins/_insert_values.py +69 -0
- sqlspec/statement/builder/mixins/_join.py +110 -0
- sqlspec/statement/builder/mixins/_limit_offset.py +53 -0
- sqlspec/statement/builder/mixins/_merge_clauses.py +405 -0
- sqlspec/statement/builder/mixins/_order_by.py +46 -0
- sqlspec/statement/builder/mixins/_pivot.py +82 -0
- sqlspec/statement/builder/mixins/_returning.py +37 -0
- sqlspec/statement/builder/mixins/_select_columns.py +60 -0
- sqlspec/statement/builder/mixins/_set_ops.py +122 -0
- sqlspec/statement/builder/mixins/_unpivot.py +80 -0
- sqlspec/statement/builder/mixins/_update_from.py +54 -0
- sqlspec/statement/builder/mixins/_update_set.py +91 -0
- sqlspec/statement/builder/mixins/_update_table.py +29 -0
- sqlspec/statement/builder/mixins/_where.py +374 -0
- sqlspec/statement/builder/mixins/_window_functions.py +86 -0
- sqlspec/statement/builder/protocols.py +20 -0
- sqlspec/statement/builder/select.py +206 -0
- sqlspec/statement/builder/update.py +178 -0
- sqlspec/statement/filters.py +571 -0
- sqlspec/statement/parameters.py +736 -0
- sqlspec/statement/pipelines/__init__.py +67 -0
- sqlspec/statement/pipelines/analyzers/__init__.py +9 -0
- sqlspec/statement/pipelines/analyzers/_analyzer.py +649 -0
- sqlspec/statement/pipelines/base.py +315 -0
- sqlspec/statement/pipelines/context.py +119 -0
- sqlspec/statement/pipelines/result_types.py +41 -0
- sqlspec/statement/pipelines/transformers/__init__.py +8 -0
- sqlspec/statement/pipelines/transformers/_expression_simplifier.py +256 -0
- sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +623 -0
- sqlspec/statement/pipelines/transformers/_remove_comments.py +66 -0
- sqlspec/statement/pipelines/transformers/_remove_hints.py +81 -0
- sqlspec/statement/pipelines/validators/__init__.py +23 -0
- sqlspec/statement/pipelines/validators/_dml_safety.py +275 -0
- sqlspec/statement/pipelines/validators/_parameter_style.py +297 -0
- sqlspec/statement/pipelines/validators/_performance.py +703 -0
- sqlspec/statement/pipelines/validators/_security.py +990 -0
- sqlspec/statement/pipelines/validators/base.py +67 -0
- sqlspec/statement/result.py +527 -0
- sqlspec/statement/splitter.py +701 -0
- sqlspec/statement/sql.py +1198 -0
- sqlspec/storage/__init__.py +15 -0
- sqlspec/storage/backends/__init__.py +0 -0
- sqlspec/storage/backends/base.py +166 -0
- sqlspec/storage/backends/fsspec.py +315 -0
- sqlspec/storage/backends/obstore.py +464 -0
- sqlspec/storage/protocol.py +170 -0
- sqlspec/storage/registry.py +315 -0
- sqlspec/typing.py +157 -36
- sqlspec/utils/correlation.py +155 -0
- sqlspec/utils/deprecation.py +3 -6
- sqlspec/utils/fixtures.py +6 -11
- sqlspec/utils/logging.py +135 -0
- sqlspec/utils/module_loader.py +45 -43
- sqlspec/utils/serializers.py +4 -0
- sqlspec/utils/singleton.py +6 -8
- sqlspec/utils/sync_tools.py +15 -27
- sqlspec/utils/text.py +58 -26
- {sqlspec-0.11.1.dist-info → sqlspec-0.12.1.dist-info}/METADATA +97 -26
- sqlspec-0.12.1.dist-info/RECORD +145 -0
- sqlspec/adapters/bigquery/config/__init__.py +0 -3
- sqlspec/adapters/bigquery/config/_common.py +0 -40
- sqlspec/adapters/bigquery/config/_sync.py +0 -87
- sqlspec/adapters/oracledb/config/__init__.py +0 -9
- sqlspec/adapters/oracledb/config/_asyncio.py +0 -186
- sqlspec/adapters/oracledb/config/_common.py +0 -131
- sqlspec/adapters/oracledb/config/_sync.py +0 -186
- sqlspec/adapters/psycopg/config/__init__.py +0 -19
- sqlspec/adapters/psycopg/config/_async.py +0 -169
- sqlspec/adapters/psycopg/config/_common.py +0 -56
- sqlspec/adapters/psycopg/config/_sync.py +0 -168
- sqlspec/filters.py +0 -331
- sqlspec/mixins.py +0 -305
- sqlspec/statement.py +0 -378
- sqlspec-0.11.1.dist-info/RECORD +0 -69
- {sqlspec-0.11.1.dist-info → sqlspec-0.12.1.dist-info}/WHEEL +0 -0
- {sqlspec-0.11.1.dist-info → sqlspec-0.12.1.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.11.1.dist-info → sqlspec-0.12.1.dist-info}/licenses/NOTICE +0 -0
sqlspec/statement.py
DELETED
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
# ruff: noqa: RUF100, PLR6301, PLR0912, PLR0915, C901, PLR0911, PLR0914, N806
|
|
2
|
-
import logging
|
|
3
|
-
from collections.abc import Sequence
|
|
4
|
-
from dataclasses import dataclass, field
|
|
5
|
-
from typing import (
|
|
6
|
-
TYPE_CHECKING,
|
|
7
|
-
Any,
|
|
8
|
-
Optional,
|
|
9
|
-
Union,
|
|
10
|
-
)
|
|
11
|
-
|
|
12
|
-
import sqlglot
|
|
13
|
-
from sqlglot import exp
|
|
14
|
-
|
|
15
|
-
from sqlspec.exceptions import ParameterStyleMismatchError, SQLParsingError
|
|
16
|
-
from sqlspec.typing import StatementParameterType
|
|
17
|
-
|
|
18
|
-
if TYPE_CHECKING:
|
|
19
|
-
from sqlspec.filters import StatementFilter
|
|
20
|
-
|
|
21
|
-
__all__ = ("SQLStatement",)
|
|
22
|
-
|
|
23
|
-
logger = logging.getLogger("sqlspec")
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
@dataclass()
|
|
27
|
-
class SQLStatement:
|
|
28
|
-
"""An immutable representation of a SQL statement with its parameters.
|
|
29
|
-
|
|
30
|
-
This class encapsulates the SQL statement and its parameters, providing
|
|
31
|
-
a clean interface for parameter binding and SQL statement formatting.
|
|
32
|
-
"""
|
|
33
|
-
|
|
34
|
-
sql: str
|
|
35
|
-
"""The raw SQL statement."""
|
|
36
|
-
parameters: Optional[StatementParameterType] = None
|
|
37
|
-
"""The parameters for the SQL statement."""
|
|
38
|
-
kwargs: Optional[dict[str, Any]] = None
|
|
39
|
-
"""Keyword arguments passed for parameter binding."""
|
|
40
|
-
dialect: Optional[str] = None
|
|
41
|
-
"""SQL dialect to use for parsing. If not provided, sqlglot will try to auto-detect."""
|
|
42
|
-
|
|
43
|
-
_merged_parameters: Optional[Union[StatementParameterType, dict[str, Any]]] = field(default=None, init=False)
|
|
44
|
-
_parsed_expression: Optional[exp.Expression] = field(default=None, init=False)
|
|
45
|
-
_param_counter: int = field(default=0, init=False)
|
|
46
|
-
|
|
47
|
-
def __post_init__(self) -> None:
|
|
48
|
-
"""Merge parameters and kwargs after initialization."""
|
|
49
|
-
merged_params = self.parameters
|
|
50
|
-
|
|
51
|
-
if self.kwargs:
|
|
52
|
-
if merged_params is None:
|
|
53
|
-
merged_params = self.kwargs
|
|
54
|
-
elif isinstance(merged_params, dict):
|
|
55
|
-
# Merge kwargs into parameters dict, kwargs take precedence
|
|
56
|
-
merged_params = {**merged_params, **self.kwargs}
|
|
57
|
-
else:
|
|
58
|
-
# If parameters is sequence or scalar, kwargs replace it
|
|
59
|
-
# Consider adding a warning here if this behavior is surprising
|
|
60
|
-
merged_params = self.kwargs
|
|
61
|
-
|
|
62
|
-
self._merged_parameters = merged_params
|
|
63
|
-
|
|
64
|
-
def process(
|
|
65
|
-
self,
|
|
66
|
-
) -> "tuple[str, Optional[Union[tuple[Any, ...], list[Any], dict[str, Any]]], Optional[exp.Expression]]":
|
|
67
|
-
"""Process the SQL statement and merged parameters for execution.
|
|
68
|
-
|
|
69
|
-
This method validates the parameters against the SQL statement using sqlglot
|
|
70
|
-
parsing but returns the *original* SQL string, the merged parameters,
|
|
71
|
-
and the parsed sqlglot expression if successful.
|
|
72
|
-
The actual formatting of SQL placeholders and parameter structures for the
|
|
73
|
-
DBAPI driver is delegated to the specific adapter.
|
|
74
|
-
|
|
75
|
-
Returns:
|
|
76
|
-
A tuple containing the *original* SQL string, the merged/validated
|
|
77
|
-
parameters (dict, tuple, list, or None), and the parsed sqlglot expression
|
|
78
|
-
(or None if parsing failed).
|
|
79
|
-
|
|
80
|
-
Raises:
|
|
81
|
-
SQLParsingError: If the SQL statement contains parameter placeholders
|
|
82
|
-
but no parameters were provided, or if parsing fails unexpectedly.
|
|
83
|
-
"""
|
|
84
|
-
# Parse the SQL to find expected parameters
|
|
85
|
-
try:
|
|
86
|
-
expression = self._parse_sql()
|
|
87
|
-
# Find all parameter expressions (:name, ?, @name, $1, etc.)
|
|
88
|
-
# These are nodes that sqlglot considers as bind parameters.
|
|
89
|
-
all_sqlglot_placeholders = list(expression.find_all(exp.Placeholder, exp.Parameter))
|
|
90
|
-
except SQLParsingError as e:
|
|
91
|
-
logger.debug(
|
|
92
|
-
"SQL parsing failed during validation: %s. Returning original SQL and parameters for adapter.", e
|
|
93
|
-
)
|
|
94
|
-
self._parsed_expression = None
|
|
95
|
-
return self.sql, self._merged_parameters, None
|
|
96
|
-
|
|
97
|
-
if self._merged_parameters is None:
|
|
98
|
-
# If no parameters were provided, but the parsed SQL expects them, raise an error.
|
|
99
|
-
if all_sqlglot_placeholders:
|
|
100
|
-
placeholder_types_desc = []
|
|
101
|
-
for p_node in all_sqlglot_placeholders:
|
|
102
|
-
if isinstance(p_node, exp.Parameter) and p_node.name:
|
|
103
|
-
placeholder_types_desc.append(f"named (e.g., :{p_node.name}, @{p_node.name})")
|
|
104
|
-
elif (
|
|
105
|
-
isinstance(p_node, exp.Placeholder)
|
|
106
|
-
and p_node.this
|
|
107
|
-
and not isinstance(p_node.this, (exp.Identifier, exp.Literal))
|
|
108
|
-
and not str(p_node.this).isdigit()
|
|
109
|
-
):
|
|
110
|
-
placeholder_types_desc.append(f"named (e.g., :{p_node.this})")
|
|
111
|
-
elif isinstance(p_node, exp.Parameter) and p_node.name and p_node.name.isdigit():
|
|
112
|
-
placeholder_types_desc.append("positional (e.g., $1, :1)")
|
|
113
|
-
elif isinstance(p_node, exp.Placeholder) and p_node.this is None:
|
|
114
|
-
placeholder_types_desc.append("positional (?)")
|
|
115
|
-
desc_str = ", ".join(sorted(set(placeholder_types_desc))) or "unknown"
|
|
116
|
-
msg = f"SQL statement contains {desc_str} parameter placeholders, but no parameters were provided. SQL: {self.sql}"
|
|
117
|
-
raise SQLParsingError(msg)
|
|
118
|
-
return self.sql, None, self._parsed_expression
|
|
119
|
-
|
|
120
|
-
# Validate provided parameters against parsed SQL parameters
|
|
121
|
-
if isinstance(self._merged_parameters, dict):
|
|
122
|
-
self._validate_dict_params(all_sqlglot_placeholders, self._merged_parameters)
|
|
123
|
-
elif isinstance(self._merged_parameters, (tuple, list)):
|
|
124
|
-
self._validate_sequence_params(all_sqlglot_placeholders, self._merged_parameters)
|
|
125
|
-
else: # Scalar parameter
|
|
126
|
-
self._validate_scalar_param(all_sqlglot_placeholders, self._merged_parameters)
|
|
127
|
-
|
|
128
|
-
# Return the original SQL and the merged parameters for the adapter to process
|
|
129
|
-
return self.sql, self._merged_parameters, self._parsed_expression
|
|
130
|
-
|
|
131
|
-
def _parse_sql(self) -> exp.Expression:
|
|
132
|
-
"""Parse the SQL using sqlglot.
|
|
133
|
-
|
|
134
|
-
Raises:
|
|
135
|
-
SQLParsingError: If the SQL statement cannot be parsed.
|
|
136
|
-
|
|
137
|
-
Returns:
|
|
138
|
-
The parsed SQL expression.
|
|
139
|
-
"""
|
|
140
|
-
try:
|
|
141
|
-
if not self.sql.strip():
|
|
142
|
-
self._parsed_expression = exp.Select()
|
|
143
|
-
return self._parsed_expression
|
|
144
|
-
# Use the provided dialect if available, otherwise sqlglot will try to auto-detect
|
|
145
|
-
self._parsed_expression = sqlglot.parse_one(self.sql, dialect=self.dialect)
|
|
146
|
-
if self._parsed_expression is None:
|
|
147
|
-
self._parsed_expression = exp.Select() # type: ignore[unreachable]
|
|
148
|
-
except Exception as e:
|
|
149
|
-
msg = f"Failed to parse SQL for validation: {e!s}\nSQL: {self.sql}"
|
|
150
|
-
self._parsed_expression = None
|
|
151
|
-
raise SQLParsingError(msg) from e
|
|
152
|
-
else:
|
|
153
|
-
return self._parsed_expression
|
|
154
|
-
|
|
155
|
-
def _validate_dict_params(
|
|
156
|
-
self, all_sqlglot_placeholders: Sequence[exp.Expression], parameter_dict: dict[str, Any]
|
|
157
|
-
) -> None:
|
|
158
|
-
sqlglot_named_params: dict[str, Union[exp.Parameter, exp.Placeholder]] = {}
|
|
159
|
-
has_positional_qmark = False
|
|
160
|
-
|
|
161
|
-
for p_node in all_sqlglot_placeholders:
|
|
162
|
-
if (
|
|
163
|
-
isinstance(p_node, exp.Parameter) and p_node.name and not p_node.name.isdigit()
|
|
164
|
-
): # @name, $name (non-numeric)
|
|
165
|
-
sqlglot_named_params[p_node.name] = p_node
|
|
166
|
-
elif (
|
|
167
|
-
isinstance(p_node, exp.Placeholder)
|
|
168
|
-
and p_node.this
|
|
169
|
-
and not isinstance(p_node.this, (exp.Identifier, exp.Literal))
|
|
170
|
-
and not str(p_node.this).isdigit()
|
|
171
|
-
): # :name
|
|
172
|
-
sqlglot_named_params[str(p_node.this)] = p_node
|
|
173
|
-
elif isinstance(p_node, exp.Placeholder) and p_node.this is None: # ?
|
|
174
|
-
has_positional_qmark = True
|
|
175
|
-
# Ignores numeric placeholders like $1, :1 for dict validation for now
|
|
176
|
-
|
|
177
|
-
if has_positional_qmark:
|
|
178
|
-
msg = f"Dictionary parameters provided, but found unnamed placeholders ('?') in SQL: {self.sql}"
|
|
179
|
-
raise ParameterStyleMismatchError(msg)
|
|
180
|
-
|
|
181
|
-
if not sqlglot_named_params and parameter_dict:
|
|
182
|
-
msg = f"Dictionary parameters provided, but no named placeholders (e.g., ':name', '$name', '@name') found by sqlglot in SQL: {self.sql}"
|
|
183
|
-
raise ParameterStyleMismatchError(msg)
|
|
184
|
-
|
|
185
|
-
missing_keys = set(sqlglot_named_params.keys()) - set(parameter_dict.keys())
|
|
186
|
-
if missing_keys:
|
|
187
|
-
msg = f"Named parameters found in SQL by sqlglot but not provided: {missing_keys}. SQL: {self.sql}"
|
|
188
|
-
raise SQLParsingError(msg)
|
|
189
|
-
|
|
190
|
-
def _validate_sequence_params(
|
|
191
|
-
self,
|
|
192
|
-
all_sqlglot_placeholders: Sequence[exp.Expression],
|
|
193
|
-
params: Union[tuple[Any, ...], list[Any]],
|
|
194
|
-
) -> None:
|
|
195
|
-
sqlglot_named_param_names = [] # For detecting named params
|
|
196
|
-
sqlglot_positional_count = 0 # For counting ?, $1, :1 etc.
|
|
197
|
-
|
|
198
|
-
for p_node in all_sqlglot_placeholders:
|
|
199
|
-
if isinstance(p_node, exp.Parameter) and p_node.name and not p_node.name.isdigit(): # @name, $name
|
|
200
|
-
sqlglot_named_param_names.append(p_node.name)
|
|
201
|
-
elif (
|
|
202
|
-
isinstance(p_node, exp.Placeholder)
|
|
203
|
-
and p_node.this
|
|
204
|
-
and not isinstance(p_node.this, (exp.Identifier, exp.Literal))
|
|
205
|
-
and not str(p_node.this).isdigit()
|
|
206
|
-
): # :name
|
|
207
|
-
sqlglot_named_param_names.append(str(p_node.this))
|
|
208
|
-
elif isinstance(p_node, exp.Placeholder) and p_node.this is None: # ?
|
|
209
|
-
sqlglot_positional_count += 1
|
|
210
|
-
elif isinstance(p_node, exp.Parameter) and ( # noqa: PLR0916
|
|
211
|
-
(p_node.name and p_node.name.isdigit())
|
|
212
|
-
or (
|
|
213
|
-
not p_node.name
|
|
214
|
-
and p_node.this
|
|
215
|
-
and isinstance(p_node.this, (str, exp.Identifier, exp.Literal))
|
|
216
|
-
and str(p_node.this).isdigit()
|
|
217
|
-
)
|
|
218
|
-
):
|
|
219
|
-
# $1, :1 style (parsed as Parameter with name="1" or this="1" or this=Identifier(this="1") or this=Literal(this=1))
|
|
220
|
-
sqlglot_positional_count += 1
|
|
221
|
-
elif (
|
|
222
|
-
isinstance(p_node, exp.Placeholder) and p_node.this and str(p_node.this).isdigit()
|
|
223
|
-
): # :1 style (Placeholder with this="1")
|
|
224
|
-
sqlglot_positional_count += 1
|
|
225
|
-
|
|
226
|
-
if sqlglot_named_param_names:
|
|
227
|
-
msg = f"Sequence parameters provided, but found named placeholders ({', '.join(sorted(set(sqlglot_named_param_names)))}) in SQL: {self.sql}"
|
|
228
|
-
raise ParameterStyleMismatchError(msg)
|
|
229
|
-
|
|
230
|
-
actual_count_provided = len(params)
|
|
231
|
-
|
|
232
|
-
if sqlglot_positional_count != actual_count_provided:
|
|
233
|
-
msg = (
|
|
234
|
-
f"Parameter count mismatch. SQL expects {sqlglot_positional_count} (sqlglot) positional "
|
|
235
|
-
f"parameters, but {actual_count_provided} were provided. SQL: {self.sql}"
|
|
236
|
-
)
|
|
237
|
-
raise SQLParsingError(msg)
|
|
238
|
-
|
|
239
|
-
def _validate_scalar_param(self, all_sqlglot_placeholders: Sequence[exp.Expression], param_value: Any) -> None:
|
|
240
|
-
"""Validates a single scalar parameter against parsed SQL parameters."""
|
|
241
|
-
self._validate_sequence_params(
|
|
242
|
-
all_sqlglot_placeholders, (param_value,)
|
|
243
|
-
) # Treat scalar as a single-element sequence
|
|
244
|
-
|
|
245
|
-
def get_expression(self) -> exp.Expression:
|
|
246
|
-
"""Get the parsed SQLglot expression, parsing if necessary.
|
|
247
|
-
|
|
248
|
-
Returns:
|
|
249
|
-
The SQLglot expression.
|
|
250
|
-
"""
|
|
251
|
-
if self._parsed_expression is None:
|
|
252
|
-
self._parse_sql()
|
|
253
|
-
if self._parsed_expression is None: # Still None after parsing attempt
|
|
254
|
-
return exp.Select() # Return an empty SELECT as fallback
|
|
255
|
-
return self._parsed_expression
|
|
256
|
-
|
|
257
|
-
def generate_param_name(self, base_name: str) -> str:
|
|
258
|
-
"""Generates a unique parameter name.
|
|
259
|
-
|
|
260
|
-
Args:
|
|
261
|
-
base_name: The base name for the parameter.
|
|
262
|
-
|
|
263
|
-
Returns:
|
|
264
|
-
The generated parameter name.
|
|
265
|
-
"""
|
|
266
|
-
self._param_counter += 1
|
|
267
|
-
safe_base_name = "".join(c if c.isalnum() else "_" for c in base_name if c.isalnum() or c == "_")
|
|
268
|
-
return f"param_{safe_base_name}_{self._param_counter}"
|
|
269
|
-
|
|
270
|
-
def add_condition(self, condition: exp.Condition, params: Optional[dict[str, Any]] = None) -> None:
|
|
271
|
-
"""Adds a condition to the WHERE clause of the query.
|
|
272
|
-
|
|
273
|
-
Args:
|
|
274
|
-
condition: The condition to add to the WHERE clause.
|
|
275
|
-
params: The parameters to add to the statement parameters.
|
|
276
|
-
"""
|
|
277
|
-
expression = self.get_expression()
|
|
278
|
-
if not isinstance(expression, (exp.Select, exp.Update, exp.Delete)):
|
|
279
|
-
return # Cannot add WHERE to some expressions
|
|
280
|
-
|
|
281
|
-
# Update the expression
|
|
282
|
-
expression.where(condition, copy=False)
|
|
283
|
-
|
|
284
|
-
# Update the parameters
|
|
285
|
-
if params:
|
|
286
|
-
if self._merged_parameters is None:
|
|
287
|
-
self._merged_parameters = params
|
|
288
|
-
elif isinstance(self._merged_parameters, dict):
|
|
289
|
-
self._merged_parameters.update(params)
|
|
290
|
-
else:
|
|
291
|
-
# Convert to dict if not already
|
|
292
|
-
self._merged_parameters = params
|
|
293
|
-
|
|
294
|
-
# Update the SQL string
|
|
295
|
-
self.sql = expression.sql(dialect=self.dialect)
|
|
296
|
-
|
|
297
|
-
def add_order_by(self, field_name: str, direction: str = "asc") -> None:
|
|
298
|
-
"""Adds an ORDER BY clause.
|
|
299
|
-
|
|
300
|
-
Args:
|
|
301
|
-
field_name: The name of the field to order by.
|
|
302
|
-
direction: The direction to order by ("asc" or "desc").
|
|
303
|
-
"""
|
|
304
|
-
expression = self.get_expression()
|
|
305
|
-
if not isinstance(expression, exp.Select):
|
|
306
|
-
return
|
|
307
|
-
|
|
308
|
-
expression.order_by(exp.Ordered(this=exp.column(field_name), desc=direction.lower() == "desc"), copy=False)
|
|
309
|
-
self.sql = expression.sql(dialect=self.dialect)
|
|
310
|
-
|
|
311
|
-
def add_limit(self, limit_val: int, param_name: Optional[str] = None) -> None:
|
|
312
|
-
"""Adds a LIMIT clause.
|
|
313
|
-
|
|
314
|
-
Args:
|
|
315
|
-
limit_val: The value for the LIMIT clause.
|
|
316
|
-
param_name: Optional name for the parameter.
|
|
317
|
-
"""
|
|
318
|
-
expression = self.get_expression()
|
|
319
|
-
if not isinstance(expression, exp.Select):
|
|
320
|
-
return
|
|
321
|
-
|
|
322
|
-
if param_name:
|
|
323
|
-
expression.limit(exp.Placeholder(this=param_name), copy=False)
|
|
324
|
-
if self._merged_parameters is None:
|
|
325
|
-
self._merged_parameters = {param_name: limit_val}
|
|
326
|
-
elif isinstance(self._merged_parameters, dict):
|
|
327
|
-
self._merged_parameters[param_name] = limit_val
|
|
328
|
-
else:
|
|
329
|
-
expression.limit(exp.Literal.number(limit_val), copy=False)
|
|
330
|
-
|
|
331
|
-
self.sql = expression.sql(dialect=self.dialect)
|
|
332
|
-
|
|
333
|
-
def add_offset(self, offset_val: int, param_name: Optional[str] = None) -> None:
|
|
334
|
-
"""Adds an OFFSET clause.
|
|
335
|
-
|
|
336
|
-
Args:
|
|
337
|
-
offset_val: The value for the OFFSET clause.
|
|
338
|
-
param_name: Optional name for the parameter.
|
|
339
|
-
"""
|
|
340
|
-
expression = self.get_expression()
|
|
341
|
-
if not isinstance(expression, exp.Select):
|
|
342
|
-
return
|
|
343
|
-
|
|
344
|
-
if param_name:
|
|
345
|
-
expression.offset(exp.Placeholder(this=param_name), copy=False)
|
|
346
|
-
if self._merged_parameters is None:
|
|
347
|
-
self._merged_parameters = {param_name: offset_val}
|
|
348
|
-
elif isinstance(self._merged_parameters, dict):
|
|
349
|
-
self._merged_parameters[param_name] = offset_val
|
|
350
|
-
else:
|
|
351
|
-
expression.offset(exp.Literal.number(offset_val), copy=False)
|
|
352
|
-
|
|
353
|
-
self.sql = expression.sql(dialect=self.dialect)
|
|
354
|
-
|
|
355
|
-
def apply_filter(self, filter_obj: "StatementFilter") -> "SQLStatement":
|
|
356
|
-
"""Apply a statement filter to this statement.
|
|
357
|
-
|
|
358
|
-
Args:
|
|
359
|
-
filter_obj: The filter to apply.
|
|
360
|
-
|
|
361
|
-
Returns:
|
|
362
|
-
The modified statement.
|
|
363
|
-
"""
|
|
364
|
-
from sqlspec.filters import apply_filter
|
|
365
|
-
|
|
366
|
-
return apply_filter(self, filter_obj)
|
|
367
|
-
|
|
368
|
-
def to_sql(self, dialect: Optional[str] = None) -> str:
|
|
369
|
-
"""Generate SQL string using the specified dialect.
|
|
370
|
-
|
|
371
|
-
Args:
|
|
372
|
-
dialect: SQL dialect to use for SQL generation. If None, uses the statement's dialect.
|
|
373
|
-
|
|
374
|
-
Returns:
|
|
375
|
-
SQL string in the specified dialect.
|
|
376
|
-
"""
|
|
377
|
-
expression = self.get_expression()
|
|
378
|
-
return expression.sql(dialect=dialect or self.dialect)
|
sqlspec-0.11.1.dist-info/RECORD
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
sqlspec/__init__.py,sha256=Dl1QOZAK21--ORQcRn15n4YPFklFMe2-Lb7TWWq_WQE,338
|
|
2
|
-
sqlspec/__metadata__.py,sha256=hNP3wXvtk8fQVPKGjRLpZ9mP-gaPJqzrmgm3UqpDIXQ,460
|
|
3
|
-
sqlspec/_serialization.py,sha256=tSwWwFImlYviC6ARdXRz0Bp4QXbCdc8cKGgZr33OglY,2657
|
|
4
|
-
sqlspec/_typing.py,sha256=a8QTy3oqdUfjrA6Iu4PxfQcUf9DuG9pZcalcevM_xIU,7037
|
|
5
|
-
sqlspec/base.py,sha256=sKN8t3tpq6p2wriBcNn8YPwb3tSXubNVm4J5GRZOpN0,33689
|
|
6
|
-
sqlspec/exceptions.py,sha256=WnA56CdDSSdOjA5UE4jnfXXtrfClgwjCRxT-id0eVAo,4302
|
|
7
|
-
sqlspec/filters.py,sha256=oLC5a0__i1B91OwNLCm_shoUsqxA3sqHlwJB6cA61JQ,12037
|
|
8
|
-
sqlspec/mixins.py,sha256=q_N5HBcfA-gETtP3zQn8gd7CNB86fqaenx2tlBqIIZI,10552
|
|
9
|
-
sqlspec/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
|
-
sqlspec/statement.py,sha256=LNagGHKt-ixjom5WW5VLRzT9YIJC0lOfvwnn41Zz-fw,16433
|
|
11
|
-
sqlspec/typing.py,sha256=XAQpyOPAcHzlGScY3DCxIfiaQpfbFP4PntIgvYdcLwk,16313
|
|
12
|
-
sqlspec/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
sqlspec/adapters/adbc/__init__.py,sha256=Ih0mdciGyhmOlqmz4uiByj7mEaqvP4upRjYhlUNopAQ,193
|
|
14
|
-
sqlspec/adapters/adbc/config.py,sha256=yayt62Mx789_B7Xo8DrlWCyeXclFchL4OxUxZIFnh9I,9646
|
|
15
|
-
sqlspec/adapters/adbc/driver.py,sha256=XI0xJRYZ7uordzR7Vpt3BRTH1DLpRZn7ZXC5-GUZAZc,28631
|
|
16
|
-
sqlspec/adapters/aiosqlite/__init__.py,sha256=CndH49GREnHuIB-jY1XAxVlLwZTvKNuk8XJu9jiiM7A,233
|
|
17
|
-
sqlspec/adapters/aiosqlite/config.py,sha256=tE-DnjKbdy5FaqqRyA1q3s06rMSAglt0HkhO2oWQT2w,4603
|
|
18
|
-
sqlspec/adapters/aiosqlite/driver.py,sha256=bwJOV-iqqn5FS4LZK6Wjt-jH0y922XCIoFqINziXFs0,17045
|
|
19
|
-
sqlspec/adapters/asyncmy/__init__.py,sha256=7jQFc0WOgc58XPeIiDgX9a95scMa6PuFgGXQaxG33nI,291
|
|
20
|
-
sqlspec/adapters/asyncmy/config.py,sha256=5ILV46FWjc9Cy46aMuhinpfc70b_708h9ubHj9HRCmE,9412
|
|
21
|
-
sqlspec/adapters/asyncmy/driver.py,sha256=ZXZt8ixuD74UvU5D-v4nrRHH_wU8apPzbROPma8Jm-k,17472
|
|
22
|
-
sqlspec/adapters/asyncpg/__init__.py,sha256=iR-vJYxImcw5hLaDgMqPLLRxW_vOYylEpemSg5G4dP4,261
|
|
23
|
-
sqlspec/adapters/asyncpg/config.py,sha256=cuQ_cCQ6xtqluaNO5Ax9vqZ-4JZoaUR31-QE6xAVbns,10011
|
|
24
|
-
sqlspec/adapters/asyncpg/driver.py,sha256=5FJDA0tVJYtsQK4Y9XpmiDwPtdjnyRkSAMk8MwVdKZ0,21197
|
|
25
|
-
sqlspec/adapters/bigquery/__init__.py,sha256=8kcl0_tnvnk8omVKnAljIY9n2XVnzkPRCfhfedGO0m4,264
|
|
26
|
-
sqlspec/adapters/bigquery/driver.py,sha256=BLD9EyKdbo85LZs6l7MMw5OE_n7dLlBtclM1kKIQlvM,23494
|
|
27
|
-
sqlspec/adapters/bigquery/config/__init__.py,sha256=4ij4LAS-kIt-vOK8KetewVoYN90i1RV_BjtowKWDIs0,150
|
|
28
|
-
sqlspec/adapters/bigquery/config/_common.py,sha256=LSbBC302-Ewx8XHTRzYR-tInMYywVW9I6UcL8cO7-HQ,1901
|
|
29
|
-
sqlspec/adapters/bigquery/config/_sync.py,sha256=oglaEErXuce1TnE2wj5KrfFLkScRGFYAoOTCVV5BFcw,3323
|
|
30
|
-
sqlspec/adapters/duckdb/__init__.py,sha256=GMSHigyHgtaJKu751kE_Sxl5Ry4hnQOnFUSIeLSg1qs,209
|
|
31
|
-
sqlspec/adapters/duckdb/config.py,sha256=b-Ev7UnpguC12CJSxK9DDqcPw0R2dZUZXaVIePf_EEg,15700
|
|
32
|
-
sqlspec/adapters/duckdb/driver.py,sha256=uMIsa6ipMmL8on1Q4vPfxbem6s7uq5_-PLCqFifFvzE,16096
|
|
33
|
-
sqlspec/adapters/oracledb/__init__.py,sha256=vVe8cXZJLFvBA6LPx4NzGRLdOeRugzRjz92UYjb0lC0,521
|
|
34
|
-
sqlspec/adapters/oracledb/driver.py,sha256=UsYa3Z0lM4w39PVgL0edDRkO35M7Rha2NxcvCLv5NR8,38755
|
|
35
|
-
sqlspec/adapters/oracledb/config/__init__.py,sha256=emx5jWXqw3ifoW-m_tNI7sTz_duq2vRkubc0J2QqEQ4,306
|
|
36
|
-
sqlspec/adapters/oracledb/config/_asyncio.py,sha256=k0wGr4FflFR03jUSgrw-4LC4mYtRlyH9gnbbBXNcMRM,7310
|
|
37
|
-
sqlspec/adapters/oracledb/config/_common.py,sha256=UJZL2DQQZM3uOn1E1A_gnsB8nX3-yCDXGd66PDI29_s,5691
|
|
38
|
-
sqlspec/adapters/oracledb/config/_sync.py,sha256=nm5FnrRG1ScrNviw3MR_40Vq8WJCXX5mJGtHhhRTPb0,7055
|
|
39
|
-
sqlspec/adapters/psqlpy/__init__.py,sha256=K8UlQrKfbCZmxGAaT4CHzQvdwGPxPLB9TDOHJgkESFc,251
|
|
40
|
-
sqlspec/adapters/psqlpy/config.py,sha256=qssXcu_Nd6o_X8QU1i61sAXwi9FiTApWxiTRU0gyBhk,10205
|
|
41
|
-
sqlspec/adapters/psqlpy/driver.py,sha256=7CG3vGjOjbmjOjW2Zd0yf73WHIRU0tNtj4YoPQ4fNFs,21325
|
|
42
|
-
sqlspec/adapters/psycopg/__init__.py,sha256=xmFWHSB6hwPNQS1wpBmAczJThABmhRv2PDNKqaMFX3E,535
|
|
43
|
-
sqlspec/adapters/psycopg/driver.py,sha256=ADzb482mbxBwmSt4WmADev2RmwRtDPSMA4uepLMx0-M,27519
|
|
44
|
-
sqlspec/adapters/psycopg/config/__init__.py,sha256=hUmtNkSma4M-Y66B-l1gh601Lx7FVYVhU0BWo9ssdJo,570
|
|
45
|
-
sqlspec/adapters/psycopg/config/_async.py,sha256=oKitbfpsJVLhMnbMUmICQub3rBzVZ5n9lwsU_ZvB45s,6661
|
|
46
|
-
sqlspec/adapters/psycopg/config/_common.py,sha256=UqqvqPE9zlSO9G_Gh6fI190cHfCDG98S0GaznGAHpdU,2181
|
|
47
|
-
sqlspec/adapters/psycopg/config/_sync.py,sha256=GYPgE7jfb15gbVdTWRoGGS3B7hauN8wk61CW6Bdrjcs,6512
|
|
48
|
-
sqlspec/adapters/sqlite/__init__.py,sha256=N8VL4Y850OOt63qz-Yvu6rIaCXiziASmn_qDY5tsRuA,209
|
|
49
|
-
sqlspec/adapters/sqlite/config.py,sha256=lGz0G-JFd7dZLhbUrITn9V-ipbhlekwNUr0bXEzM-8k,4498
|
|
50
|
-
sqlspec/adapters/sqlite/driver.py,sha256=e-EmOrr91TWNvmHuvAq58qI_eyhwL20EwprRu3YlQds,14889
|
|
51
|
-
sqlspec/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
52
|
-
sqlspec/extensions/litestar/__init__.py,sha256=ifiR2AXv3m4iDncA2XsVsgk8NnEjcEKxjK2jhFe6A-8,262
|
|
53
|
-
sqlspec/extensions/litestar/_utils.py,sha256=UgwFxqLnjDw9S8G0H24DP2GsbMGas81W1lfhfTY68m8,1969
|
|
54
|
-
sqlspec/extensions/litestar/config.py,sha256=Lz5_NqZ-XDXvaLh7fsPqxvuSDJ1JLJg1XUqbC-PdZPU,4666
|
|
55
|
-
sqlspec/extensions/litestar/handlers.py,sha256=3RJJ5rCEQCbmrhDhkz9Tp03UxqPYhyL8PYR7WKyvuP8,10372
|
|
56
|
-
sqlspec/extensions/litestar/plugin.py,sha256=0isCQ_VFC33_xyRf1Zcb2oWYbZpjFTg-OOZ2EGwTV5M,5175
|
|
57
|
-
sqlspec/extensions/litestar/providers.py,sha256=dwdw7o0jfhDGNbwgabx41dROQ3HCgA0cdXNhWsJPVuw,21805
|
|
58
|
-
sqlspec/utils/__init__.py,sha256=_Ya8IZuc2cZIstXr_xjgnSfxICXHXvu5mfWsi2USDrw,183
|
|
59
|
-
sqlspec/utils/deprecation.py,sha256=4pwGxoQYI3dAc3L1lh4tszZG6e2jp5m4e0ICk8SJx5M,3886
|
|
60
|
-
sqlspec/utils/fixtures.py,sha256=ni51rAuen6S1wuSi1kUwn6Qh25B-XrewPEsjV8G4gQ0,2029
|
|
61
|
-
sqlspec/utils/module_loader.py,sha256=tmMy9JcTTQETcwT8Wt8adCIuqr4zinQnPbCiBJ6JTSQ,2703
|
|
62
|
-
sqlspec/utils/singleton.py,sha256=YsHuo8_blPRQJRvXnjWXa8-Y-TO5V7jFGRwql-rNpO0,1363
|
|
63
|
-
sqlspec/utils/sync_tools.py,sha256=IRzgLMvdkYIwPGUG_EBYGwoOiBBmQSaphwaGachkV4M,9146
|
|
64
|
-
sqlspec/utils/text.py,sha256=-RlwmGjXtfD_ZkqIesZTHAPi46xg-uqlpVKDMcNwFrU,3539
|
|
65
|
-
sqlspec-0.11.1.dist-info/METADATA,sha256=o1D1jHZ58W2Xz6DNlEapfqAA7Nr1PWGZmAK7qwurucg,14467
|
|
66
|
-
sqlspec-0.11.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
|
|
67
|
-
sqlspec-0.11.1.dist-info/licenses/LICENSE,sha256=MdujfZ6l5HuLz4mElxlu049itenOR3gnhN1_Nd3nVcM,1078
|
|
68
|
-
sqlspec-0.11.1.dist-info/licenses/NOTICE,sha256=Lyir8ozXWov7CyYS4huVaOCNrtgL17P-bNV-5daLntQ,1634
|
|
69
|
-
sqlspec-0.11.1.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|