sqlspec 0.15.0__py3-none-any.whl → 0.16.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/_sql.py +699 -43
- 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 +61 -35
- sqlspec/builder/_merge.py +17 -2
- sqlspec/builder/_parsing_utils.py +16 -12
- 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 +11 -4
- sqlspec/builder/mixins/_merge_operations.py +91 -19
- sqlspec/builder/mixins/_order_limit_operations.py +15 -3
- sqlspec/builder/mixins/_pivot_operations.py +11 -2
- sqlspec/builder/mixins/_select_operations.py +16 -10
- sqlspec/builder/mixins/_update_operations.py +43 -10
- sqlspec/builder/mixins/_where_clause.py +177 -65
- 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.1.dist-info}/METADATA +1 -1
- {sqlspec-0.15.0.dist-info → sqlspec-0.16.1.dist-info}/RECORD +43 -43
- {sqlspec-0.15.0.dist-info → sqlspec-0.16.1.dist-info}/WHEEL +0 -0
- {sqlspec-0.15.0.dist-info → sqlspec-0.16.1.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.15.0.dist-info → sqlspec-0.16.1.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.15.0.dist-info → sqlspec-0.16.1.dist-info}/licenses/NOTICE +0 -0
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from dataclasses import dataclass
|
|
4
4
|
from typing import TYPE_CHECKING, 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
|
|
|
@@ -11,17 +12,20 @@ from sqlspec.exceptions import SQLBuilderError
|
|
|
11
12
|
from sqlspec.utils.type_guards import has_query_builder_parameters, is_expression
|
|
12
13
|
|
|
13
14
|
if TYPE_CHECKING:
|
|
14
|
-
from sqlspec.builder._base import QueryBuilder
|
|
15
15
|
from sqlspec.builder._column import Column, FunctionColumn
|
|
16
16
|
from sqlspec.protocols import SelectBuilderProtocol, SQLBuilderProtocol
|
|
17
17
|
|
|
18
18
|
__all__ = ("CaseBuilder", "SelectClauseMixin")
|
|
19
19
|
|
|
20
20
|
|
|
21
|
+
@trait
|
|
21
22
|
class SelectClauseMixin:
|
|
22
23
|
"""Consolidated mixin providing all SELECT-related clauses and functionality."""
|
|
23
24
|
|
|
24
|
-
|
|
25
|
+
__slots__ = ()
|
|
26
|
+
|
|
27
|
+
# Type annotation for PyRight - this will be provided by the base class
|
|
28
|
+
_expression: Optional[exp.Expression]
|
|
25
29
|
|
|
26
30
|
def select(self, *columns: Union[str, exp.Expression, "Column", "FunctionColumn"]) -> Self:
|
|
27
31
|
"""Add columns to SELECT clause.
|
|
@@ -529,7 +533,7 @@ class SelectClauseMixin:
|
|
|
529
533
|
Returns:
|
|
530
534
|
CaseBuilder: A CaseBuilder instance for building the CASE expression.
|
|
531
535
|
"""
|
|
532
|
-
builder = cast("
|
|
536
|
+
builder = cast("SelectBuilderProtocol", self)
|
|
533
537
|
return CaseBuilder(builder, alias)
|
|
534
538
|
|
|
535
539
|
|
|
@@ -537,15 +541,15 @@ class SelectClauseMixin:
|
|
|
537
541
|
class CaseBuilder:
|
|
538
542
|
"""Builder for CASE expressions."""
|
|
539
543
|
|
|
540
|
-
_parent: "
|
|
544
|
+
_parent: "SelectBuilderProtocol"
|
|
541
545
|
_alias: Optional[str]
|
|
542
546
|
_case_expr: exp.Case
|
|
543
547
|
|
|
544
|
-
def __init__(self, parent: "
|
|
548
|
+
def __init__(self, parent: "SelectBuilderProtocol", alias: "Optional[str]" = None) -> None:
|
|
545
549
|
"""Initialize CaseBuilder.
|
|
546
550
|
|
|
547
551
|
Args:
|
|
548
|
-
parent: The parent builder.
|
|
552
|
+
parent: The parent builder with select capabilities.
|
|
549
553
|
alias: Optional alias for the CASE expression.
|
|
550
554
|
"""
|
|
551
555
|
self._parent = parent
|
|
@@ -563,7 +567,8 @@ class CaseBuilder:
|
|
|
563
567
|
CaseBuilder: The current builder instance for method chaining.
|
|
564
568
|
"""
|
|
565
569
|
cond_expr = exp.condition(condition) if isinstance(condition, str) else condition
|
|
566
|
-
param_name = self._parent.
|
|
570
|
+
param_name = self._parent._generate_unique_parameter_name("case_when_value")
|
|
571
|
+
param_name = self._parent.add_parameter(value, name=param_name)[1]
|
|
567
572
|
value_expr = exp.Placeholder(this=param_name)
|
|
568
573
|
|
|
569
574
|
when_clause = exp.When(this=cond_expr, then=value_expr)
|
|
@@ -582,16 +587,17 @@ class CaseBuilder:
|
|
|
582
587
|
Returns:
|
|
583
588
|
CaseBuilder: The current builder instance for method chaining.
|
|
584
589
|
"""
|
|
585
|
-
param_name = self._parent.
|
|
590
|
+
param_name = self._parent._generate_unique_parameter_name("case_else_value")
|
|
591
|
+
param_name = self._parent.add_parameter(value, name=param_name)[1]
|
|
586
592
|
value_expr = exp.Placeholder(this=param_name)
|
|
587
593
|
self._case_expr.set("default", value_expr)
|
|
588
594
|
return self
|
|
589
595
|
|
|
590
|
-
def end(self) -> "
|
|
596
|
+
def end(self) -> "SelectBuilderProtocol":
|
|
591
597
|
"""Finalize the CASE expression and add it to the SELECT clause.
|
|
592
598
|
|
|
593
599
|
Returns:
|
|
594
600
|
The parent builder instance.
|
|
595
601
|
"""
|
|
596
602
|
select_expr = exp.alias_(self._case_expr, self._alias) if self._alias else self._case_expr
|
|
597
|
-
return
|
|
603
|
+
return self._parent.select(select_expr)
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from collections.abc import Mapping
|
|
4
4
|
from typing import Any, Optional, Union
|
|
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,24 @@ 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)
|
|
44
63
|
|
|
45
64
|
def set(self, *args: Any, **kwargs: Any) -> Self:
|
|
46
65
|
"""Set columns and values for the UPDATE statement.
|
|
@@ -79,9 +98,13 @@ class UpdateSetClauseMixin:
|
|
|
79
98
|
value_expr = exp.paren(exp.maybe_parse(sql_str, dialect=getattr(self, "dialect", None)))
|
|
80
99
|
if has_query_builder_parameters(val):
|
|
81
100
|
for p_name, p_value in val.parameters.items():
|
|
82
|
-
self.add_parameter(p_value, name=p_name)
|
|
101
|
+
self.add_parameter(p_value, name=p_name)
|
|
83
102
|
else:
|
|
84
|
-
|
|
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]
|
|
85
108
|
value_expr = exp.Placeholder(this=param_name)
|
|
86
109
|
assignments.append(exp.EQ(this=col_expr, expression=value_expr))
|
|
87
110
|
elif (len(args) == 1 and isinstance(args[0], Mapping)) or kwargs:
|
|
@@ -95,9 +118,13 @@ class UpdateSetClauseMixin:
|
|
|
95
118
|
value_expr = exp.paren(exp.maybe_parse(sql_str, dialect=getattr(self, "dialect", None)))
|
|
96
119
|
if has_query_builder_parameters(val):
|
|
97
120
|
for p_name, p_value in val.parameters.items():
|
|
98
|
-
self.add_parameter(p_value, name=p_name)
|
|
121
|
+
self.add_parameter(p_value, name=p_name)
|
|
99
122
|
else:
|
|
100
|
-
|
|
123
|
+
column_name = col if isinstance(col, str) else str(col)
|
|
124
|
+
if "." in column_name:
|
|
125
|
+
column_name = column_name.split(".")[-1]
|
|
126
|
+
param_name = self._generate_unique_parameter_name(column_name)
|
|
127
|
+
param_name = self.add_parameter(val, name=param_name)[1]
|
|
101
128
|
value_expr = exp.Placeholder(this=param_name)
|
|
102
129
|
assignments.append(exp.EQ(this=exp.column(col), expression=value_expr))
|
|
103
130
|
else:
|
|
@@ -108,9 +135,15 @@ class UpdateSetClauseMixin:
|
|
|
108
135
|
return self
|
|
109
136
|
|
|
110
137
|
|
|
138
|
+
@trait
|
|
111
139
|
class UpdateFromClauseMixin:
|
|
112
140
|
"""Mixin providing FROM clause for UPDATE builders (e.g., PostgreSQL style)."""
|
|
113
141
|
|
|
142
|
+
__slots__ = ()
|
|
143
|
+
|
|
144
|
+
# Type annotation for PyRight - this will be provided by the base class
|
|
145
|
+
_expression: Optional[exp.Expression]
|
|
146
|
+
|
|
114
147
|
def from_(self, table: Union[str, exp.Expression, Any], alias: Optional[str] = None) -> Self:
|
|
115
148
|
"""Add a FROM clause to the UPDATE statement.
|
|
116
149
|
|
|
@@ -124,7 +157,7 @@ class UpdateFromClauseMixin:
|
|
|
124
157
|
Raises:
|
|
125
158
|
SQLBuilderError: If the current expression is not an UPDATE statement.
|
|
126
159
|
"""
|
|
127
|
-
if self._expression is None or not isinstance(self._expression, exp.Update):
|
|
160
|
+
if self._expression is None or not isinstance(self._expression, exp.Update):
|
|
128
161
|
msg = "Cannot add FROM clause to non-UPDATE expression. Set the main table first."
|
|
129
162
|
raise SQLBuilderError(msg)
|
|
130
163
|
table_expr: exp.Expression
|
|
@@ -142,9 +175,9 @@ class UpdateFromClauseMixin:
|
|
|
142
175
|
else:
|
|
143
176
|
msg = f"Unsupported table type for FROM clause: {type(table)}"
|
|
144
177
|
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"]
|
|
178
|
+
if self._expression.args.get("from") is None:
|
|
179
|
+
self._expression.set("from", exp.From(expressions=[]))
|
|
180
|
+
from_clause = self._expression.args["from"]
|
|
148
181
|
if hasattr(from_clause, "append"):
|
|
149
182
|
from_clause.append("expressions", table_expr)
|
|
150
183
|
else:
|