sqlspec 0.15.0__py3-none-any.whl → 0.16.2__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/_sql.py +702 -44
- sqlspec/builder/_base.py +77 -44
- sqlspec/builder/_column.py +0 -4
- sqlspec/builder/_ddl.py +15 -52
- sqlspec/builder/_ddl_utils.py +0 -1
- sqlspec/builder/_delete.py +4 -5
- sqlspec/builder/_insert.py +235 -44
- sqlspec/builder/_merge.py +17 -2
- sqlspec/builder/_parsing_utils.py +42 -14
- sqlspec/builder/_select.py +29 -33
- sqlspec/builder/_update.py +4 -2
- sqlspec/builder/mixins/_cte_and_set_ops.py +47 -20
- sqlspec/builder/mixins/_delete_operations.py +6 -1
- sqlspec/builder/mixins/_insert_operations.py +126 -24
- sqlspec/builder/mixins/_join_operations.py +44 -10
- sqlspec/builder/mixins/_merge_operations.py +183 -25
- sqlspec/builder/mixins/_order_limit_operations.py +15 -3
- sqlspec/builder/mixins/_pivot_operations.py +11 -2
- sqlspec/builder/mixins/_select_operations.py +21 -14
- sqlspec/builder/mixins/_update_operations.py +80 -32
- sqlspec/builder/mixins/_where_clause.py +201 -66
- sqlspec/core/cache.py +26 -28
- sqlspec/core/compiler.py +58 -37
- sqlspec/core/filters.py +12 -10
- sqlspec/core/parameters.py +80 -52
- sqlspec/core/result.py +30 -17
- sqlspec/core/statement.py +47 -22
- sqlspec/driver/_async.py +76 -46
- sqlspec/driver/_common.py +25 -6
- sqlspec/driver/_sync.py +73 -43
- sqlspec/driver/mixins/_result_tools.py +62 -37
- sqlspec/driver/mixins/_sql_translator.py +61 -11
- sqlspec/extensions/litestar/cli.py +1 -1
- sqlspec/extensions/litestar/plugin.py +2 -2
- sqlspec/protocols.py +7 -0
- sqlspec/utils/sync_tools.py +1 -1
- sqlspec/utils/type_guards.py +7 -3
- {sqlspec-0.15.0.dist-info → sqlspec-0.16.2.dist-info}/METADATA +1 -1
- {sqlspec-0.15.0.dist-info → sqlspec-0.16.2.dist-info}/RECORD +43 -43
- {sqlspec-0.15.0.dist-info → sqlspec-0.16.2.dist-info}/WHEEL +0 -0
- {sqlspec-0.15.0.dist-info → sqlspec-0.16.2.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.15.0.dist-info → sqlspec-0.16.2.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.15.0.dist-info → sqlspec-0.16.2.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
"""Update operation mixins for SQL builders."""
|
|
2
2
|
|
|
3
3
|
from collections.abc import Mapping
|
|
4
|
-
from typing import Any, Optional, Union
|
|
4
|
+
from typing import Any, Optional, Union, cast
|
|
5
5
|
|
|
6
|
+
from mypy_extensions import trait
|
|
6
7
|
from sqlglot import exp
|
|
7
8
|
from typing_extensions import Self
|
|
8
9
|
|
|
@@ -14,10 +15,14 @@ __all__ = ("UpdateFromClauseMixin", "UpdateSetClauseMixin", "UpdateTableClauseMi
|
|
|
14
15
|
MIN_SET_ARGS = 2
|
|
15
16
|
|
|
16
17
|
|
|
18
|
+
@trait
|
|
17
19
|
class UpdateTableClauseMixin:
|
|
18
20
|
"""Mixin providing TABLE clause for UPDATE builders."""
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
__slots__ = ()
|
|
23
|
+
|
|
24
|
+
# Type annotation for PyRight - this will be provided by the base class
|
|
25
|
+
_expression: Optional[exp.Expression]
|
|
21
26
|
|
|
22
27
|
def table(self, table_name: str, alias: Optional[str] = None) -> Self:
|
|
23
28
|
"""Set the table to update.
|
|
@@ -37,10 +42,70 @@ class UpdateTableClauseMixin:
|
|
|
37
42
|
return self
|
|
38
43
|
|
|
39
44
|
|
|
45
|
+
@trait
|
|
40
46
|
class UpdateSetClauseMixin:
|
|
41
47
|
"""Mixin providing SET clause for UPDATE builders."""
|
|
42
48
|
|
|
43
|
-
|
|
49
|
+
__slots__ = ()
|
|
50
|
+
|
|
51
|
+
# Type annotation for PyRight - this will be provided by the base class
|
|
52
|
+
_expression: Optional[exp.Expression]
|
|
53
|
+
|
|
54
|
+
def add_parameter(self, value: Any, name: Optional[str] = None) -> tuple[Any, str]:
|
|
55
|
+
"""Add parameter - provided by QueryBuilder."""
|
|
56
|
+
msg = "Method must be provided by QueryBuilder subclass"
|
|
57
|
+
raise NotImplementedError(msg)
|
|
58
|
+
|
|
59
|
+
def _generate_unique_parameter_name(self, base_name: str) -> str:
|
|
60
|
+
"""Generate unique parameter name - provided by QueryBuilder."""
|
|
61
|
+
msg = "Method must be provided by QueryBuilder subclass"
|
|
62
|
+
raise NotImplementedError(msg)
|
|
63
|
+
|
|
64
|
+
def _process_update_value(self, val: Any, col: Any) -> exp.Expression:
|
|
65
|
+
"""Process a value for UPDATE assignment, handling SQL objects and parameters.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
val: The value to process
|
|
69
|
+
col: The column name for parameter naming
|
|
70
|
+
|
|
71
|
+
Returns:
|
|
72
|
+
The processed expression for the value
|
|
73
|
+
"""
|
|
74
|
+
if isinstance(val, exp.Expression):
|
|
75
|
+
return val
|
|
76
|
+
if has_query_builder_parameters(val):
|
|
77
|
+
subquery = val.build()
|
|
78
|
+
sql_str = subquery.sql if hasattr(subquery, "sql") and not callable(subquery.sql) else str(subquery)
|
|
79
|
+
value_expr = exp.paren(exp.maybe_parse(sql_str, dialect=getattr(self, "dialect", None)))
|
|
80
|
+
if has_query_builder_parameters(val):
|
|
81
|
+
for p_name, p_value in val.parameters.items():
|
|
82
|
+
self.add_parameter(p_value, name=p_name)
|
|
83
|
+
return value_expr
|
|
84
|
+
if hasattr(val, "expression") and hasattr(val, "sql"):
|
|
85
|
+
# Handle SQL objects (from sql.raw with parameters)
|
|
86
|
+
expression = getattr(val, "expression", None)
|
|
87
|
+
if expression is not None and isinstance(expression, exp.Expression):
|
|
88
|
+
# Merge parameters from SQL object into builder
|
|
89
|
+
if hasattr(val, "parameters"):
|
|
90
|
+
sql_parameters = getattr(val, "parameters", {})
|
|
91
|
+
for param_name, param_value in sql_parameters.items():
|
|
92
|
+
self.add_parameter(param_value, name=param_name)
|
|
93
|
+
return cast("exp.Expression", expression)
|
|
94
|
+
# If expression is None, fall back to parsing the raw SQL
|
|
95
|
+
sql_text = getattr(val, "sql", "")
|
|
96
|
+
# Merge parameters even when parsing raw SQL
|
|
97
|
+
if hasattr(val, "parameters"):
|
|
98
|
+
sql_parameters = getattr(val, "parameters", {})
|
|
99
|
+
for param_name, param_value in sql_parameters.items():
|
|
100
|
+
self.add_parameter(param_value, name=param_name)
|
|
101
|
+
parsed_expr = exp.maybe_parse(sql_text)
|
|
102
|
+
return parsed_expr if parsed_expr is not None else exp.convert(str(sql_text))
|
|
103
|
+
column_name = col if isinstance(col, str) else str(col)
|
|
104
|
+
if "." in column_name:
|
|
105
|
+
column_name = column_name.split(".")[-1]
|
|
106
|
+
param_name = self._generate_unique_parameter_name(column_name)
|
|
107
|
+
param_name = self.add_parameter(val, name=param_name)[1]
|
|
108
|
+
return exp.Placeholder(this=param_name)
|
|
44
109
|
|
|
45
110
|
def set(self, *args: Any, **kwargs: Any) -> Self:
|
|
46
111
|
"""Set columns and values for the UPDATE statement.
|
|
@@ -61,7 +126,6 @@ class UpdateSetClauseMixin:
|
|
|
61
126
|
Returns:
|
|
62
127
|
The current builder instance for method chaining.
|
|
63
128
|
"""
|
|
64
|
-
|
|
65
129
|
if self._expression is None:
|
|
66
130
|
self._expression = exp.Update()
|
|
67
131
|
if not isinstance(self._expression, exp.Update):
|
|
@@ -71,34 +135,12 @@ class UpdateSetClauseMixin:
|
|
|
71
135
|
if len(args) == MIN_SET_ARGS and not kwargs:
|
|
72
136
|
col, val = args
|
|
73
137
|
col_expr = col if isinstance(col, exp.Column) else exp.column(col)
|
|
74
|
-
|
|
75
|
-
value_expr = val
|
|
76
|
-
elif has_query_builder_parameters(val):
|
|
77
|
-
subquery = val.build()
|
|
78
|
-
sql_str = subquery.sql if hasattr(subquery, "sql") and not callable(subquery.sql) else str(subquery)
|
|
79
|
-
value_expr = exp.paren(exp.maybe_parse(sql_str, dialect=getattr(self, "dialect", None)))
|
|
80
|
-
if has_query_builder_parameters(val):
|
|
81
|
-
for p_name, p_value in val.parameters.items():
|
|
82
|
-
self.add_parameter(p_value, name=p_name) # type: ignore[attr-defined]
|
|
83
|
-
else:
|
|
84
|
-
param_name = self.add_parameter(val)[1] # type: ignore[attr-defined]
|
|
85
|
-
value_expr = exp.Placeholder(this=param_name)
|
|
138
|
+
value_expr = self._process_update_value(val, col)
|
|
86
139
|
assignments.append(exp.EQ(this=col_expr, expression=value_expr))
|
|
87
140
|
elif (len(args) == 1 and isinstance(args[0], Mapping)) or kwargs:
|
|
88
141
|
all_values = dict(args[0] if args else {}, **kwargs)
|
|
89
142
|
for col, val in all_values.items():
|
|
90
|
-
|
|
91
|
-
value_expr = val
|
|
92
|
-
elif has_query_builder_parameters(val):
|
|
93
|
-
subquery = val.build()
|
|
94
|
-
sql_str = subquery.sql if hasattr(subquery, "sql") and not callable(subquery.sql) else str(subquery)
|
|
95
|
-
value_expr = exp.paren(exp.maybe_parse(sql_str, dialect=getattr(self, "dialect", None)))
|
|
96
|
-
if has_query_builder_parameters(val):
|
|
97
|
-
for p_name, p_value in val.parameters.items():
|
|
98
|
-
self.add_parameter(p_value, name=p_name) # type: ignore[attr-defined]
|
|
99
|
-
else:
|
|
100
|
-
param_name = self.add_parameter(val)[1] # type: ignore[attr-defined]
|
|
101
|
-
value_expr = exp.Placeholder(this=param_name)
|
|
143
|
+
value_expr = self._process_update_value(val, col)
|
|
102
144
|
assignments.append(exp.EQ(this=exp.column(col), expression=value_expr))
|
|
103
145
|
else:
|
|
104
146
|
msg = "Invalid arguments for set(): use (column, value), mapping, or kwargs."
|
|
@@ -108,9 +150,15 @@ class UpdateSetClauseMixin:
|
|
|
108
150
|
return self
|
|
109
151
|
|
|
110
152
|
|
|
153
|
+
@trait
|
|
111
154
|
class UpdateFromClauseMixin:
|
|
112
155
|
"""Mixin providing FROM clause for UPDATE builders (e.g., PostgreSQL style)."""
|
|
113
156
|
|
|
157
|
+
__slots__ = ()
|
|
158
|
+
|
|
159
|
+
# Type annotation for PyRight - this will be provided by the base class
|
|
160
|
+
_expression: Optional[exp.Expression]
|
|
161
|
+
|
|
114
162
|
def from_(self, table: Union[str, exp.Expression, Any], alias: Optional[str] = None) -> Self:
|
|
115
163
|
"""Add a FROM clause to the UPDATE statement.
|
|
116
164
|
|
|
@@ -124,7 +172,7 @@ class UpdateFromClauseMixin:
|
|
|
124
172
|
Raises:
|
|
125
173
|
SQLBuilderError: If the current expression is not an UPDATE statement.
|
|
126
174
|
"""
|
|
127
|
-
if self._expression is None or not isinstance(self._expression, exp.Update):
|
|
175
|
+
if self._expression is None or not isinstance(self._expression, exp.Update):
|
|
128
176
|
msg = "Cannot add FROM clause to non-UPDATE expression. Set the main table first."
|
|
129
177
|
raise SQLBuilderError(msg)
|
|
130
178
|
table_expr: exp.Expression
|
|
@@ -142,9 +190,9 @@ class UpdateFromClauseMixin:
|
|
|
142
190
|
else:
|
|
143
191
|
msg = f"Unsupported table type for FROM clause: {type(table)}"
|
|
144
192
|
raise SQLBuilderError(msg)
|
|
145
|
-
if self._expression.args.get("from") is None:
|
|
146
|
-
self._expression.set("from", exp.From(expressions=[]))
|
|
147
|
-
from_clause = self._expression.args["from"]
|
|
193
|
+
if self._expression.args.get("from") is None:
|
|
194
|
+
self._expression.set("from", exp.From(expressions=[]))
|
|
195
|
+
from_clause = self._expression.args["from"]
|
|
148
196
|
if hasattr(from_clause, "append"):
|
|
149
197
|
from_clause.append("expressions", table_expr)
|
|
150
198
|
else:
|