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.

Files changed (43) hide show
  1. sqlspec/_sql.py +699 -43
  2. sqlspec/builder/_base.py +77 -44
  3. sqlspec/builder/_column.py +0 -4
  4. sqlspec/builder/_ddl.py +15 -52
  5. sqlspec/builder/_ddl_utils.py +0 -1
  6. sqlspec/builder/_delete.py +4 -5
  7. sqlspec/builder/_insert.py +61 -35
  8. sqlspec/builder/_merge.py +17 -2
  9. sqlspec/builder/_parsing_utils.py +16 -12
  10. sqlspec/builder/_select.py +29 -33
  11. sqlspec/builder/_update.py +4 -2
  12. sqlspec/builder/mixins/_cte_and_set_ops.py +47 -20
  13. sqlspec/builder/mixins/_delete_operations.py +6 -1
  14. sqlspec/builder/mixins/_insert_operations.py +126 -24
  15. sqlspec/builder/mixins/_join_operations.py +11 -4
  16. sqlspec/builder/mixins/_merge_operations.py +91 -19
  17. sqlspec/builder/mixins/_order_limit_operations.py +15 -3
  18. sqlspec/builder/mixins/_pivot_operations.py +11 -2
  19. sqlspec/builder/mixins/_select_operations.py +16 -10
  20. sqlspec/builder/mixins/_update_operations.py +43 -10
  21. sqlspec/builder/mixins/_where_clause.py +177 -65
  22. sqlspec/core/cache.py +26 -28
  23. sqlspec/core/compiler.py +58 -37
  24. sqlspec/core/filters.py +12 -10
  25. sqlspec/core/parameters.py +80 -52
  26. sqlspec/core/result.py +30 -17
  27. sqlspec/core/statement.py +47 -22
  28. sqlspec/driver/_async.py +76 -46
  29. sqlspec/driver/_common.py +25 -6
  30. sqlspec/driver/_sync.py +73 -43
  31. sqlspec/driver/mixins/_result_tools.py +62 -37
  32. sqlspec/driver/mixins/_sql_translator.py +61 -11
  33. sqlspec/extensions/litestar/cli.py +1 -1
  34. sqlspec/extensions/litestar/plugin.py +2 -2
  35. sqlspec/protocols.py +7 -0
  36. sqlspec/utils/sync_tools.py +1 -1
  37. sqlspec/utils/type_guards.py +7 -3
  38. {sqlspec-0.15.0.dist-info → sqlspec-0.16.1.dist-info}/METADATA +1 -1
  39. {sqlspec-0.15.0.dist-info → sqlspec-0.16.1.dist-info}/RECORD +43 -43
  40. {sqlspec-0.15.0.dist-info → sqlspec-0.16.1.dist-info}/WHEEL +0 -0
  41. {sqlspec-0.15.0.dist-info → sqlspec-0.16.1.dist-info}/entry_points.txt +0 -0
  42. {sqlspec-0.15.0.dist-info → sqlspec-0.16.1.dist-info}/licenses/LICENSE +0 -0
  43. {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
- _expression: Optional[exp.Expression] = None
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("QueryBuilder", self) # pyright: ignore
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: "QueryBuilder" # pyright: ignore
544
+ _parent: "SelectBuilderProtocol"
541
545
  _alias: Optional[str]
542
546
  _case_expr: exp.Case
543
547
 
544
- def __init__(self, parent: "QueryBuilder", alias: "Optional[str]" = None) -> None:
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.add_parameter(value)[1]
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.add_parameter(value)[1]
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) -> "QueryBuilder":
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 cast("QueryBuilder", self._parent.select(select_expr)) # type: ignore[attr-defined]
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
- _expression: Optional[exp.Expression] = None
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
- _expression: Optional[exp.Expression] = None
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) # type: ignore[attr-defined]
101
+ self.add_parameter(p_value, name=p_name)
83
102
  else:
84
- param_name = self.add_parameter(val)[1] # type: ignore[attr-defined]
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) # type: ignore[attr-defined]
121
+ self.add_parameter(p_value, name=p_name)
99
122
  else:
100
- param_name = self.add_parameter(val)[1] # type: ignore[attr-defined]
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): # type: ignore[attr-defined]
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: # type: ignore[attr-defined]
146
- self._expression.set("from", exp.From(expressions=[])) # type: ignore[attr-defined]
147
- from_clause = self._expression.args["from"] # type: ignore[attr-defined]
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: