sqlspec 0.16.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 (39) hide show
  1. sqlspec/_sql.py +448 -15
  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 +59 -44
  8. sqlspec/builder/_merge.py +17 -2
  9. sqlspec/builder/_parsing_utils.py +11 -11
  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 -34
  15. sqlspec/builder/mixins/_join_operations.py +11 -4
  16. sqlspec/builder/mixins/_merge_operations.py +81 -21
  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 +12 -8
  20. sqlspec/builder/mixins/_update_operations.py +37 -14
  21. sqlspec/builder/mixins/_where_clause.py +55 -43
  22. sqlspec/core/cache.py +26 -28
  23. sqlspec/core/compiler.py +58 -37
  24. sqlspec/core/parameters.py +80 -52
  25. sqlspec/core/result.py +30 -17
  26. sqlspec/core/statement.py +31 -21
  27. sqlspec/driver/_async.py +76 -46
  28. sqlspec/driver/_common.py +25 -6
  29. sqlspec/driver/_sync.py +73 -43
  30. sqlspec/driver/mixins/_result_tools.py +51 -22
  31. sqlspec/driver/mixins/_sql_translator.py +61 -11
  32. sqlspec/protocols.py +7 -0
  33. sqlspec/utils/type_guards.py +7 -3
  34. {sqlspec-0.16.0.dist-info → sqlspec-0.16.1.dist-info}/METADATA +1 -1
  35. {sqlspec-0.16.0.dist-info → sqlspec-0.16.1.dist-info}/RECORD +39 -39
  36. {sqlspec-0.16.0.dist-info → sqlspec-0.16.1.dist-info}/WHEEL +0 -0
  37. {sqlspec-0.16.0.dist-info → sqlspec-0.16.1.dist-info}/entry_points.txt +0 -0
  38. {sqlspec-0.16.0.dist-info → sqlspec-0.16.1.dist-info}/licenses/LICENSE +0 -0
  39. {sqlspec-0.16.0.dist-info → sqlspec-0.16.1.dist-info}/licenses/NOTICE +0 -0
@@ -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,14 +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
103
  column_name = col if isinstance(col, str) else str(col)
85
- # Extract just the column part if table.column format
86
104
  if "." in column_name:
87
105
  column_name = column_name.split(".")[-1]
88
- param_name = self._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
89
- param_name = self.add_parameter(val, name=param_name)[1] # type: ignore[attr-defined]
106
+ param_name = self._generate_unique_parameter_name(column_name)
107
+ param_name = self.add_parameter(val, name=param_name)[1]
90
108
  value_expr = exp.Placeholder(this=param_name)
91
109
  assignments.append(exp.EQ(this=col_expr, expression=value_expr))
92
110
  elif (len(args) == 1 and isinstance(args[0], Mapping)) or kwargs:
@@ -100,14 +118,13 @@ class UpdateSetClauseMixin:
100
118
  value_expr = exp.paren(exp.maybe_parse(sql_str, dialect=getattr(self, "dialect", None)))
101
119
  if has_query_builder_parameters(val):
102
120
  for p_name, p_value in val.parameters.items():
103
- self.add_parameter(p_value, name=p_name) # type: ignore[attr-defined]
121
+ self.add_parameter(p_value, name=p_name)
104
122
  else:
105
- # Extract column name for parameter naming
106
123
  column_name = col if isinstance(col, str) else str(col)
107
124
  if "." in column_name:
108
125
  column_name = column_name.split(".")[-1]
109
- param_name = self._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
110
- param_name = self.add_parameter(val, name=param_name)[1] # type: ignore[attr-defined]
126
+ param_name = self._generate_unique_parameter_name(column_name)
127
+ param_name = self.add_parameter(val, name=param_name)[1]
111
128
  value_expr = exp.Placeholder(this=param_name)
112
129
  assignments.append(exp.EQ(this=exp.column(col), expression=value_expr))
113
130
  else:
@@ -118,9 +135,15 @@ class UpdateSetClauseMixin:
118
135
  return self
119
136
 
120
137
 
138
+ @trait
121
139
  class UpdateFromClauseMixin:
122
140
  """Mixin providing FROM clause for UPDATE builders (e.g., PostgreSQL style)."""
123
141
 
142
+ __slots__ = ()
143
+
144
+ # Type annotation for PyRight - this will be provided by the base class
145
+ _expression: Optional[exp.Expression]
146
+
124
147
  def from_(self, table: Union[str, exp.Expression, Any], alias: Optional[str] = None) -> Self:
125
148
  """Add a FROM clause to the UPDATE statement.
126
149
 
@@ -134,7 +157,7 @@ class UpdateFromClauseMixin:
134
157
  Raises:
135
158
  SQLBuilderError: If the current expression is not an UPDATE statement.
136
159
  """
137
- 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):
138
161
  msg = "Cannot add FROM clause to non-UPDATE expression. Set the main table first."
139
162
  raise SQLBuilderError(msg)
140
163
  table_expr: exp.Expression
@@ -152,9 +175,9 @@ class UpdateFromClauseMixin:
152
175
  else:
153
176
  msg = f"Unsupported table type for FROM clause: {type(table)}"
154
177
  raise SQLBuilderError(msg)
155
- if self._expression.args.get("from") is None: # type: ignore[attr-defined]
156
- self._expression.set("from", exp.From(expressions=[])) # type: ignore[attr-defined]
157
- 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"]
158
181
  if hasattr(from_clause, "append"):
159
182
  from_clause.append("expressions", table_expr)
160
183
  else:
@@ -3,6 +3,7 @@
3
3
 
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
 
@@ -27,9 +28,10 @@ def _extract_column_name(column: Union[str, exp.Column]) -> str:
27
28
  return column
28
29
  if isinstance(column, exp.Column):
29
30
  # Extract the column name from SQLGlot Column expression
30
- if column.this and hasattr(column.this, "this"):
31
+ try:
31
32
  return str(column.this.this)
32
- return str(column.this) if column.this else "column"
33
+ except AttributeError:
34
+ return str(column.this) if column.this else "column"
33
35
  return "column"
34
36
 
35
37
 
@@ -40,9 +42,15 @@ if TYPE_CHECKING:
40
42
  __all__ = ("HavingClauseMixin", "WhereClauseMixin")
41
43
 
42
44
 
45
+ @trait
43
46
  class WhereClauseMixin:
44
47
  """Mixin providing WHERE clause methods for SELECT, UPDATE, and DELETE builders."""
45
48
 
49
+ __slots__ = ()
50
+
51
+ # Type annotation for PyRight - this will be provided by the base class
52
+ _expression: Optional[exp.Expression]
53
+
46
54
  def _handle_in_operator(
47
55
  self, column_exp: exp.Expression, value: Any, column_name: str = "column"
48
56
  ) -> exp.Expression:
@@ -52,13 +60,13 @@ class WhereClauseMixin:
52
60
  placeholders = []
53
61
  for i, v in enumerate(value):
54
62
  if len(value) == 1:
55
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
63
+ param_name = builder._generate_unique_parameter_name(column_name)
56
64
  else:
57
- param_name = builder._generate_unique_parameter_name(f"{column_name}_{i + 1}") # type: ignore[attr-defined]
65
+ param_name = builder._generate_unique_parameter_name(f"{column_name}_{i + 1}")
58
66
  _, param_name = builder.add_parameter(v, name=param_name)
59
67
  placeholders.append(exp.Placeholder(this=param_name))
60
68
  return exp.In(this=column_exp, expressions=placeholders)
61
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
69
+ param_name = builder._generate_unique_parameter_name(column_name)
62
70
  _, param_name = builder.add_parameter(value, name=param_name)
63
71
  return exp.In(this=column_exp, expressions=[exp.Placeholder(this=param_name)])
64
72
 
@@ -71,13 +79,13 @@ class WhereClauseMixin:
71
79
  placeholders = []
72
80
  for i, v in enumerate(value):
73
81
  if len(value) == 1:
74
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
82
+ param_name = builder._generate_unique_parameter_name(column_name)
75
83
  else:
76
- param_name = builder._generate_unique_parameter_name(f"{column_name}_{i + 1}") # type: ignore[attr-defined]
84
+ param_name = builder._generate_unique_parameter_name(f"{column_name}_{i + 1}")
77
85
  _, param_name = builder.add_parameter(v, name=param_name)
78
86
  placeholders.append(exp.Placeholder(this=param_name))
79
87
  return exp.Not(this=exp.In(this=column_exp, expressions=placeholders))
80
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
88
+ param_name = builder._generate_unique_parameter_name(column_name)
81
89
  _, param_name = builder.add_parameter(value, name=param_name)
82
90
  return exp.Not(this=exp.In(this=column_exp, expressions=[exp.Placeholder(this=param_name)]))
83
91
 
@@ -98,8 +106,8 @@ class WhereClauseMixin:
98
106
  if is_iterable_parameters(value) and len(value) == 2:
99
107
  builder = cast("SQLBuilderProtocol", self)
100
108
  low, high = value
101
- low_param = builder._generate_unique_parameter_name(f"{column_name}_low") # type: ignore[attr-defined]
102
- high_param = builder._generate_unique_parameter_name(f"{column_name}_high") # type: ignore[attr-defined]
109
+ low_param = builder._generate_unique_parameter_name(f"{column_name}_low")
110
+ high_param = builder._generate_unique_parameter_name(f"{column_name}_high")
103
111
  _, low_param = builder.add_parameter(low, name=low_param)
104
112
  _, high_param = builder.add_parameter(high, name=high_param)
105
113
  return exp.Between(
@@ -115,8 +123,8 @@ class WhereClauseMixin:
115
123
  if is_iterable_parameters(value) and len(value) == 2:
116
124
  builder = cast("SQLBuilderProtocol", self)
117
125
  low, high = value
118
- low_param = builder._generate_unique_parameter_name(f"{column_name}_low") # type: ignore[attr-defined]
119
- high_param = builder._generate_unique_parameter_name(f"{column_name}_high") # type: ignore[attr-defined]
126
+ low_param = builder._generate_unique_parameter_name(f"{column_name}_low")
127
+ high_param = builder._generate_unique_parameter_name(f"{column_name}_high")
120
128
  _, low_param = builder.add_parameter(low, name=low_param)
121
129
  _, high_param = builder.add_parameter(high, name=high_param)
122
130
  return exp.Not(
@@ -137,7 +145,7 @@ class WhereClauseMixin:
137
145
  if len(condition) == 2:
138
146
  # (column, value) tuple for equality
139
147
  value = condition[1]
140
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
148
+ param_name = builder._generate_unique_parameter_name(column_name)
141
149
  _, param_name = builder.add_parameter(value, name=param_name)
142
150
  return exp.EQ(this=column_exp, expression=exp.Placeholder(this=param_name))
143
151
 
@@ -147,35 +155,35 @@ class WhereClauseMixin:
147
155
  value = condition[2]
148
156
 
149
157
  if operator == "=":
150
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
158
+ param_name = builder._generate_unique_parameter_name(column_name)
151
159
  _, param_name = builder.add_parameter(value, name=param_name)
152
160
  return exp.EQ(this=column_exp, expression=exp.Placeholder(this=param_name))
153
161
  if operator in {"!=", "<>"}:
154
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
162
+ param_name = builder._generate_unique_parameter_name(column_name)
155
163
  _, param_name = builder.add_parameter(value, name=param_name)
156
164
  return exp.NEQ(this=column_exp, expression=exp.Placeholder(this=param_name))
157
165
  if operator == ">":
158
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
166
+ param_name = builder._generate_unique_parameter_name(column_name)
159
167
  _, param_name = builder.add_parameter(value, name=param_name)
160
168
  return exp.GT(this=column_exp, expression=exp.Placeholder(this=param_name))
161
169
  if operator == ">=":
162
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
170
+ param_name = builder._generate_unique_parameter_name(column_name)
163
171
  _, param_name = builder.add_parameter(value, name=param_name)
164
172
  return exp.GTE(this=column_exp, expression=exp.Placeholder(this=param_name))
165
173
  if operator == "<":
166
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
174
+ param_name = builder._generate_unique_parameter_name(column_name)
167
175
  _, param_name = builder.add_parameter(value, name=param_name)
168
176
  return exp.LT(this=column_exp, expression=exp.Placeholder(this=param_name))
169
177
  if operator == "<=":
170
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
178
+ param_name = builder._generate_unique_parameter_name(column_name)
171
179
  _, param_name = builder.add_parameter(value, name=param_name)
172
180
  return exp.LTE(this=column_exp, expression=exp.Placeholder(this=param_name))
173
181
  if operator == "LIKE":
174
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
182
+ param_name = builder._generate_unique_parameter_name(column_name)
175
183
  _, param_name = builder.add_parameter(value, name=param_name)
176
184
  return exp.Like(this=column_exp, expression=exp.Placeholder(this=param_name))
177
185
  if operator == "NOT LIKE":
178
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
186
+ param_name = builder._generate_unique_parameter_name(column_name)
179
187
  _, param_name = builder.add_parameter(value, name=param_name)
180
188
  return exp.Not(this=exp.Like(this=column_exp, expression=exp.Placeholder(this=param_name)))
181
189
 
@@ -222,7 +230,7 @@ class WhereClauseMixin:
222
230
  Returns:
223
231
  The current builder instance for method chaining.
224
232
  """
225
- if self.__class__.__name__ == "Update" and not isinstance(self._expression, exp.Update): # type: ignore[attr-defined]
233
+ if self.__class__.__name__ == "Update" and not isinstance(self._expression, exp.Update):
226
234
  msg = "Cannot add WHERE clause to non-UPDATE expression"
227
235
  raise SQLBuilderError(msg)
228
236
 
@@ -274,7 +282,7 @@ class WhereClauseMixin:
274
282
  """Add WHERE column = value clause."""
275
283
  builder = cast("SQLBuilderProtocol", self)
276
284
  column_name = _extract_column_name(column)
277
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
285
+ param_name = builder._generate_unique_parameter_name(column_name)
278
286
  _, param_name = builder.add_parameter(value, name=param_name)
279
287
  col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
280
288
  condition: exp.Expression = col_expr.eq(exp.Placeholder(this=param_name))
@@ -284,7 +292,7 @@ class WhereClauseMixin:
284
292
  """Add WHERE column != value clause."""
285
293
  builder = cast("SQLBuilderProtocol", self)
286
294
  column_name = _extract_column_name(column)
287
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
295
+ param_name = builder._generate_unique_parameter_name(column_name)
288
296
  _, param_name = builder.add_parameter(value, name=param_name)
289
297
  col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
290
298
  condition: exp.Expression = col_expr.neq(exp.Placeholder(this=param_name))
@@ -294,7 +302,7 @@ class WhereClauseMixin:
294
302
  """Add WHERE column < value clause."""
295
303
  builder = cast("SQLBuilderProtocol", self)
296
304
  column_name = _extract_column_name(column)
297
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
305
+ param_name = builder._generate_unique_parameter_name(column_name)
298
306
  _, param_name = builder.add_parameter(value, name=param_name)
299
307
  col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
300
308
  condition: exp.Expression = exp.LT(this=col_expr, expression=exp.Placeholder(this=param_name))
@@ -304,7 +312,7 @@ class WhereClauseMixin:
304
312
  """Add WHERE column <= value clause."""
305
313
  builder = cast("SQLBuilderProtocol", self)
306
314
  column_name = _extract_column_name(column)
307
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
315
+ param_name = builder._generate_unique_parameter_name(column_name)
308
316
  _, param_name = builder.add_parameter(value, name=param_name)
309
317
  col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
310
318
  condition: exp.Expression = exp.LTE(this=col_expr, expression=exp.Placeholder(this=param_name))
@@ -314,7 +322,7 @@ class WhereClauseMixin:
314
322
  """Add WHERE column > value clause."""
315
323
  builder = cast("SQLBuilderProtocol", self)
316
324
  column_name = _extract_column_name(column)
317
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
325
+ param_name = builder._generate_unique_parameter_name(column_name)
318
326
  _, param_name = builder.add_parameter(value, name=param_name)
319
327
  col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
320
328
  condition: exp.Expression = exp.GT(this=col_expr, expression=exp.Placeholder(this=param_name))
@@ -324,7 +332,7 @@ class WhereClauseMixin:
324
332
  """Add WHERE column >= value clause."""
325
333
  builder = cast("SQLBuilderProtocol", self)
326
334
  column_name = _extract_column_name(column)
327
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
335
+ param_name = builder._generate_unique_parameter_name(column_name)
328
336
  _, param_name = builder.add_parameter(value, name=param_name)
329
337
  col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
330
338
  condition: exp.Expression = exp.GTE(this=col_expr, expression=exp.Placeholder(this=param_name))
@@ -334,8 +342,8 @@ class WhereClauseMixin:
334
342
  """Add WHERE column BETWEEN low AND high clause."""
335
343
  builder = cast("SQLBuilderProtocol", self)
336
344
  column_name = _extract_column_name(column)
337
- low_param = builder._generate_unique_parameter_name(f"{column_name}_low") # type: ignore[attr-defined]
338
- high_param = builder._generate_unique_parameter_name(f"{column_name}_high") # type: ignore[attr-defined]
345
+ low_param = builder._generate_unique_parameter_name(f"{column_name}_low")
346
+ high_param = builder._generate_unique_parameter_name(f"{column_name}_high")
339
347
  _, low_param = builder.add_parameter(low, name=low_param)
340
348
  _, high_param = builder.add_parameter(high, name=high_param)
341
349
  col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
@@ -346,7 +354,7 @@ class WhereClauseMixin:
346
354
  """Add WHERE column LIKE pattern clause."""
347
355
  builder = cast("SQLBuilderProtocol", self)
348
356
  column_name = _extract_column_name(column)
349
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
357
+ param_name = builder._generate_unique_parameter_name(column_name)
350
358
  _, param_name = builder.add_parameter(pattern, name=param_name)
351
359
  col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
352
360
  if escape is not None:
@@ -360,7 +368,7 @@ class WhereClauseMixin:
360
368
  """Add WHERE column NOT LIKE pattern clause."""
361
369
  builder = cast("SQLBuilderProtocol", self)
362
370
  column_name = _extract_column_name(column)
363
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
371
+ param_name = builder._generate_unique_parameter_name(column_name)
364
372
  _, param_name = builder.add_parameter(pattern, name=param_name)
365
373
  col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
366
374
  condition: exp.Expression = col_expr.like(exp.Placeholder(this=param_name)).not_()
@@ -370,7 +378,7 @@ class WhereClauseMixin:
370
378
  """Add WHERE column ILIKE pattern clause."""
371
379
  builder = cast("SQLBuilderProtocol", self)
372
380
  column_name = _extract_column_name(column)
373
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
381
+ param_name = builder._generate_unique_parameter_name(column_name)
374
382
  _, param_name = builder.add_parameter(pattern, name=param_name)
375
383
  col_expr = parse_column_expression(column) if not isinstance(column, exp.Column) else column
376
384
  condition: exp.Expression = col_expr.ilike(exp.Placeholder(this=param_name))
@@ -399,7 +407,7 @@ class WhereClauseMixin:
399
407
  sql_str = subquery.sql
400
408
  subquery_exp = exp.paren(exp.maybe_parse(sql_str, dialect=builder.dialect_name)) # pyright: ignore
401
409
  # Merge subquery parameters into parent builder
402
- if hasattr(subquery, "parameters"):
410
+ if hasattr(subquery, "parameters") and isinstance(subquery.parameters, dict): # pyright: ignore[reportAttributeAccessIssue]
403
411
  for param_name, param_value in subquery.parameters.items(): # pyright: ignore[reportAttributeAccessIssue]
404
412
  builder.add_parameter(param_value, name=param_name)
405
413
  else:
@@ -413,9 +421,9 @@ class WhereClauseMixin:
413
421
  parameters = []
414
422
  for i, v in enumerate(values):
415
423
  if len(values) == 1:
416
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
424
+ param_name = builder._generate_unique_parameter_name(column_name)
417
425
  else:
418
- param_name = builder._generate_unique_parameter_name(f"{column_name}_{i + 1}") # type: ignore[attr-defined]
426
+ param_name = builder._generate_unique_parameter_name(f"{column_name}_{i + 1}")
419
427
  _, param_name = builder.add_parameter(v, name=param_name)
420
428
  parameters.append(exp.Placeholder(this=param_name))
421
429
  condition = col_expr.isin(*parameters)
@@ -442,9 +450,9 @@ class WhereClauseMixin:
442
450
  parameters = []
443
451
  for i, v in enumerate(values):
444
452
  if len(values) == 1:
445
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
453
+ param_name = builder._generate_unique_parameter_name(column_name)
446
454
  else:
447
- param_name = builder._generate_unique_parameter_name(f"{column_name}_{i + 1}") # type: ignore[attr-defined]
455
+ param_name = builder._generate_unique_parameter_name(f"{column_name}_{i + 1}")
448
456
  _, param_name = builder.add_parameter(v, name=param_name)
449
457
  parameters.append(exp.Placeholder(this=param_name))
450
458
  condition = exp.Not(this=col_expr.isin(*parameters))
@@ -532,9 +540,9 @@ class WhereClauseMixin:
532
540
  parameters = []
533
541
  for i, v in enumerate(values):
534
542
  if len(values) == 1:
535
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
543
+ param_name = builder._generate_unique_parameter_name(column_name)
536
544
  else:
537
- param_name = builder._generate_unique_parameter_name(f"{column_name}_any_{i + 1}") # type: ignore[attr-defined]
545
+ param_name = builder._generate_unique_parameter_name(f"{column_name}_any_{i + 1}")
538
546
  _, param_name = builder.add_parameter(v, name=param_name)
539
547
  parameters.append(exp.Placeholder(this=param_name))
540
548
  tuple_expr = exp.Tuple(expressions=parameters)
@@ -572,9 +580,9 @@ class WhereClauseMixin:
572
580
  parameters = []
573
581
  for i, v in enumerate(values):
574
582
  if len(values) == 1:
575
- param_name = builder._generate_unique_parameter_name(column_name) # type: ignore[attr-defined]
583
+ param_name = builder._generate_unique_parameter_name(column_name)
576
584
  else:
577
- param_name = builder._generate_unique_parameter_name(f"{column_name}_not_any_{i + 1}") # type: ignore[attr-defined]
585
+ param_name = builder._generate_unique_parameter_name(f"{column_name}_not_any_{i + 1}")
578
586
  _, param_name = builder.add_parameter(v, name=param_name)
579
587
  parameters.append(exp.Placeholder(this=param_name))
580
588
  tuple_expr = exp.Tuple(expressions=parameters)
@@ -582,10 +590,14 @@ class WhereClauseMixin:
582
590
  return self.where(condition)
583
591
 
584
592
 
593
+ @trait
585
594
  class HavingClauseMixin:
586
595
  """Mixin providing HAVING clause for SELECT builders."""
587
596
 
588
- _expression: Optional[exp.Expression] = None
597
+ __slots__ = ()
598
+
599
+ # Type annotation for PyRight - this will be provided by the base class
600
+ _expression: Optional[exp.Expression]
589
601
 
590
602
  def having(self, condition: Union[str, exp.Expression]) -> Self:
591
603
  """Add HAVING clause.
sqlspec/core/cache.py CHANGED
@@ -245,19 +245,21 @@ class UnifiedCache(Generic[CacheValueT]):
245
245
  Cached value or None if not found or expired
246
246
  """
247
247
  with self._lock:
248
- node: Optional[CacheNode] = self._cache.get(key)
248
+ node = self._cache.get(key)
249
249
  if node is None:
250
250
  self._stats.record_miss()
251
251
  return None
252
252
 
253
- current_time: float = time.time()
254
- ttl: Optional[int] = self._ttl
255
- if ttl is not None and (current_time - node.timestamp) > ttl:
256
- self._remove_node(node)
257
- del self._cache[key]
258
- self._stats.record_miss()
259
- self._stats.record_eviction()
260
- return None
253
+ # Optimize TTL check with early variable assignment
254
+ ttl = self._ttl
255
+ if ttl is not None:
256
+ current_time = time.time()
257
+ if (current_time - node.timestamp) > ttl:
258
+ self._remove_node(node)
259
+ del self._cache[key]
260
+ self._stats.record_miss()
261
+ self._stats.record_eviction()
262
+ return None
261
263
 
262
264
  self._move_to_head(node)
263
265
  node.access_count += 1
@@ -272,7 +274,7 @@ class UnifiedCache(Generic[CacheValueT]):
272
274
  value: Value to cache
273
275
  """
274
276
  with self._lock:
275
- existing_node: Optional[CacheNode] = self._cache.get(key)
277
+ existing_node = self._cache.get(key)
276
278
  if existing_node is not None:
277
279
  existing_node.value = value
278
280
  existing_node.timestamp = time.time()
@@ -280,14 +282,13 @@ class UnifiedCache(Generic[CacheValueT]):
280
282
  self._move_to_head(existing_node)
281
283
  return
282
284
 
283
- new_node: CacheNode = CacheNode(key, value)
285
+ new_node = CacheNode(key, value)
284
286
  self._cache[key] = new_node
285
287
  self._add_to_head(new_node)
286
288
 
287
- cache_size: int = len(self._cache)
288
- max_size: int = self._max_size
289
- if cache_size > max_size:
290
- tail_node: Optional[CacheNode] = self._tail.prev
289
+ # Optimize size check with cached length
290
+ if len(self._cache) > self._max_size:
291
+ tail_node = self._tail.prev
291
292
  if tail_node is not None and tail_node is not self._head:
292
293
  self._remove_node(tail_node)
293
294
  del self._cache[tail_node.key]
@@ -361,17 +362,13 @@ class UnifiedCache(Generic[CacheValueT]):
361
362
  def __contains__(self, key: CacheKey) -> bool:
362
363
  """Check if key exists in cache."""
363
364
  with self._lock:
364
- node: Optional[CacheNode] = self._cache.get(key)
365
+ node = self._cache.get(key)
365
366
  if node is None:
366
367
  return False
367
368
 
368
- ttl: Optional[int] = self._ttl
369
- if ttl is not None:
370
- current_time: float = time.time()
371
- if (current_time - node.timestamp) > ttl:
372
- return False
373
-
374
- return True
369
+ # Optimize TTL check
370
+ ttl = self._ttl
371
+ return not (ttl is not None and time.time() - node.timestamp > ttl)
375
372
 
376
373
 
377
374
  @mypyc_attr(allow_interpreted_subclasses=False)
@@ -553,6 +550,8 @@ class ParameterCache:
553
550
  """
554
551
  # Create stable key from parameters and configuration
555
552
  try:
553
+ # Optimize type checking order
554
+ param_key: tuple[Any, ...]
556
555
  if isinstance(params, dict):
557
556
  param_key = tuple(sorted(params.items()))
558
557
  elif isinstance(params, (list, tuple)):
@@ -560,12 +559,11 @@ class ParameterCache:
560
559
  else:
561
560
  param_key = (params,)
562
561
 
563
- key_data = ("parameters", param_key, config_hash)
564
- return CacheKey(key_data)
562
+ return CacheKey(("parameters", param_key, config_hash))
565
563
  except (TypeError, ValueError):
566
- param_key = (str(params), type(params).__name__) # type: ignore[assignment]
567
- key_data = ("parameters", param_key, config_hash)
568
- return CacheKey(key_data)
564
+ # Fallback for unhashable types
565
+ param_key_fallback = (str(params), type(params).__name__)
566
+ return CacheKey(("parameters", param_key_fallback, config_hash))
569
567
 
570
568
  def clear(self) -> None:
571
569
  """Clear parameter cache."""