sqlspec 0.26.0__py3-none-any.whl → 0.28.0__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 (212) hide show
  1. sqlspec/__init__.py +7 -15
  2. sqlspec/_serialization.py +55 -25
  3. sqlspec/_typing.py +155 -52
  4. sqlspec/adapters/adbc/_types.py +1 -1
  5. sqlspec/adapters/adbc/adk/__init__.py +5 -0
  6. sqlspec/adapters/adbc/adk/store.py +880 -0
  7. sqlspec/adapters/adbc/config.py +62 -12
  8. sqlspec/adapters/adbc/data_dictionary.py +74 -2
  9. sqlspec/adapters/adbc/driver.py +226 -58
  10. sqlspec/adapters/adbc/litestar/__init__.py +5 -0
  11. sqlspec/adapters/adbc/litestar/store.py +504 -0
  12. sqlspec/adapters/adbc/type_converter.py +44 -50
  13. sqlspec/adapters/aiosqlite/_types.py +1 -1
  14. sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
  15. sqlspec/adapters/aiosqlite/adk/store.py +536 -0
  16. sqlspec/adapters/aiosqlite/config.py +86 -16
  17. sqlspec/adapters/aiosqlite/data_dictionary.py +34 -2
  18. sqlspec/adapters/aiosqlite/driver.py +127 -38
  19. sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
  20. sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
  21. sqlspec/adapters/aiosqlite/pool.py +7 -7
  22. sqlspec/adapters/asyncmy/__init__.py +7 -1
  23. sqlspec/adapters/asyncmy/_types.py +1 -1
  24. sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
  25. sqlspec/adapters/asyncmy/adk/store.py +503 -0
  26. sqlspec/adapters/asyncmy/config.py +59 -17
  27. sqlspec/adapters/asyncmy/data_dictionary.py +41 -2
  28. sqlspec/adapters/asyncmy/driver.py +293 -62
  29. sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
  30. sqlspec/adapters/asyncmy/litestar/store.py +296 -0
  31. sqlspec/adapters/asyncpg/__init__.py +2 -1
  32. sqlspec/adapters/asyncpg/_type_handlers.py +71 -0
  33. sqlspec/adapters/asyncpg/_types.py +11 -7
  34. sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
  35. sqlspec/adapters/asyncpg/adk/store.py +460 -0
  36. sqlspec/adapters/asyncpg/config.py +57 -36
  37. sqlspec/adapters/asyncpg/data_dictionary.py +48 -2
  38. sqlspec/adapters/asyncpg/driver.py +153 -23
  39. sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
  40. sqlspec/adapters/asyncpg/litestar/store.py +253 -0
  41. sqlspec/adapters/bigquery/_types.py +1 -1
  42. sqlspec/adapters/bigquery/adk/__init__.py +5 -0
  43. sqlspec/adapters/bigquery/adk/store.py +585 -0
  44. sqlspec/adapters/bigquery/config.py +36 -11
  45. sqlspec/adapters/bigquery/data_dictionary.py +42 -2
  46. sqlspec/adapters/bigquery/driver.py +489 -144
  47. sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
  48. sqlspec/adapters/bigquery/litestar/store.py +327 -0
  49. sqlspec/adapters/bigquery/type_converter.py +55 -23
  50. sqlspec/adapters/duckdb/_types.py +2 -2
  51. sqlspec/adapters/duckdb/adk/__init__.py +14 -0
  52. sqlspec/adapters/duckdb/adk/store.py +563 -0
  53. sqlspec/adapters/duckdb/config.py +79 -21
  54. sqlspec/adapters/duckdb/data_dictionary.py +41 -2
  55. sqlspec/adapters/duckdb/driver.py +225 -44
  56. sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
  57. sqlspec/adapters/duckdb/litestar/store.py +332 -0
  58. sqlspec/adapters/duckdb/pool.py +5 -5
  59. sqlspec/adapters/duckdb/type_converter.py +51 -21
  60. sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
  61. sqlspec/adapters/oracledb/_types.py +20 -2
  62. sqlspec/adapters/oracledb/adk/__init__.py +5 -0
  63. sqlspec/adapters/oracledb/adk/store.py +1628 -0
  64. sqlspec/adapters/oracledb/config.py +120 -36
  65. sqlspec/adapters/oracledb/data_dictionary.py +87 -20
  66. sqlspec/adapters/oracledb/driver.py +475 -86
  67. sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
  68. sqlspec/adapters/oracledb/litestar/store.py +765 -0
  69. sqlspec/adapters/oracledb/migrations.py +316 -25
  70. sqlspec/adapters/oracledb/type_converter.py +91 -16
  71. sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
  72. sqlspec/adapters/psqlpy/_types.py +2 -1
  73. sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
  74. sqlspec/adapters/psqlpy/adk/store.py +483 -0
  75. sqlspec/adapters/psqlpy/config.py +45 -19
  76. sqlspec/adapters/psqlpy/data_dictionary.py +48 -2
  77. sqlspec/adapters/psqlpy/driver.py +108 -41
  78. sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
  79. sqlspec/adapters/psqlpy/litestar/store.py +272 -0
  80. sqlspec/adapters/psqlpy/type_converter.py +40 -11
  81. sqlspec/adapters/psycopg/_type_handlers.py +80 -0
  82. sqlspec/adapters/psycopg/_types.py +2 -1
  83. sqlspec/adapters/psycopg/adk/__init__.py +5 -0
  84. sqlspec/adapters/psycopg/adk/store.py +962 -0
  85. sqlspec/adapters/psycopg/config.py +65 -37
  86. sqlspec/adapters/psycopg/data_dictionary.py +91 -3
  87. sqlspec/adapters/psycopg/driver.py +200 -78
  88. sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
  89. sqlspec/adapters/psycopg/litestar/store.py +554 -0
  90. sqlspec/adapters/sqlite/__init__.py +2 -1
  91. sqlspec/adapters/sqlite/_type_handlers.py +86 -0
  92. sqlspec/adapters/sqlite/_types.py +1 -1
  93. sqlspec/adapters/sqlite/adk/__init__.py +5 -0
  94. sqlspec/adapters/sqlite/adk/store.py +582 -0
  95. sqlspec/adapters/sqlite/config.py +85 -16
  96. sqlspec/adapters/sqlite/data_dictionary.py +34 -2
  97. sqlspec/adapters/sqlite/driver.py +120 -52
  98. sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
  99. sqlspec/adapters/sqlite/litestar/store.py +318 -0
  100. sqlspec/adapters/sqlite/pool.py +5 -5
  101. sqlspec/base.py +45 -26
  102. sqlspec/builder/__init__.py +73 -4
  103. sqlspec/builder/_base.py +91 -58
  104. sqlspec/builder/_column.py +5 -5
  105. sqlspec/builder/_ddl.py +98 -89
  106. sqlspec/builder/_delete.py +5 -4
  107. sqlspec/builder/_dml.py +388 -0
  108. sqlspec/{_sql.py → builder/_factory.py} +41 -44
  109. sqlspec/builder/_insert.py +5 -82
  110. sqlspec/builder/{mixins/_join_operations.py → _join.py} +145 -143
  111. sqlspec/builder/_merge.py +446 -11
  112. sqlspec/builder/_parsing_utils.py +9 -11
  113. sqlspec/builder/_select.py +1313 -25
  114. sqlspec/builder/_update.py +11 -42
  115. sqlspec/cli.py +76 -69
  116. sqlspec/config.py +331 -62
  117. sqlspec/core/__init__.py +5 -4
  118. sqlspec/core/cache.py +18 -18
  119. sqlspec/core/compiler.py +6 -8
  120. sqlspec/core/filters.py +55 -47
  121. sqlspec/core/hashing.py +9 -9
  122. sqlspec/core/parameters.py +76 -45
  123. sqlspec/core/result.py +234 -47
  124. sqlspec/core/splitter.py +16 -17
  125. sqlspec/core/statement.py +32 -31
  126. sqlspec/core/type_conversion.py +3 -2
  127. sqlspec/driver/__init__.py +1 -3
  128. sqlspec/driver/_async.py +183 -160
  129. sqlspec/driver/_common.py +197 -109
  130. sqlspec/driver/_sync.py +189 -161
  131. sqlspec/driver/mixins/_result_tools.py +20 -236
  132. sqlspec/driver/mixins/_sql_translator.py +4 -4
  133. sqlspec/exceptions.py +70 -7
  134. sqlspec/extensions/adk/__init__.py +53 -0
  135. sqlspec/extensions/adk/_types.py +51 -0
  136. sqlspec/extensions/adk/converters.py +172 -0
  137. sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
  138. sqlspec/extensions/adk/migrations/__init__.py +0 -0
  139. sqlspec/extensions/adk/service.py +181 -0
  140. sqlspec/extensions/adk/store.py +536 -0
  141. sqlspec/extensions/aiosql/adapter.py +69 -61
  142. sqlspec/extensions/fastapi/__init__.py +21 -0
  143. sqlspec/extensions/fastapi/extension.py +331 -0
  144. sqlspec/extensions/fastapi/providers.py +543 -0
  145. sqlspec/extensions/flask/__init__.py +36 -0
  146. sqlspec/extensions/flask/_state.py +71 -0
  147. sqlspec/extensions/flask/_utils.py +40 -0
  148. sqlspec/extensions/flask/extension.py +389 -0
  149. sqlspec/extensions/litestar/__init__.py +21 -4
  150. sqlspec/extensions/litestar/cli.py +54 -10
  151. sqlspec/extensions/litestar/config.py +56 -266
  152. sqlspec/extensions/litestar/handlers.py +46 -17
  153. sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
  154. sqlspec/extensions/litestar/migrations/__init__.py +3 -0
  155. sqlspec/extensions/litestar/plugin.py +349 -224
  156. sqlspec/extensions/litestar/providers.py +25 -25
  157. sqlspec/extensions/litestar/store.py +265 -0
  158. sqlspec/extensions/starlette/__init__.py +10 -0
  159. sqlspec/extensions/starlette/_state.py +25 -0
  160. sqlspec/extensions/starlette/_utils.py +52 -0
  161. sqlspec/extensions/starlette/extension.py +254 -0
  162. sqlspec/extensions/starlette/middleware.py +154 -0
  163. sqlspec/loader.py +30 -49
  164. sqlspec/migrations/base.py +200 -76
  165. sqlspec/migrations/commands.py +591 -62
  166. sqlspec/migrations/context.py +6 -9
  167. sqlspec/migrations/fix.py +199 -0
  168. sqlspec/migrations/loaders.py +47 -19
  169. sqlspec/migrations/runner.py +241 -75
  170. sqlspec/migrations/tracker.py +237 -21
  171. sqlspec/migrations/utils.py +51 -3
  172. sqlspec/migrations/validation.py +177 -0
  173. sqlspec/protocols.py +106 -36
  174. sqlspec/storage/_utils.py +85 -0
  175. sqlspec/storage/backends/fsspec.py +133 -107
  176. sqlspec/storage/backends/local.py +78 -51
  177. sqlspec/storage/backends/obstore.py +276 -168
  178. sqlspec/storage/registry.py +75 -39
  179. sqlspec/typing.py +30 -84
  180. sqlspec/utils/__init__.py +25 -4
  181. sqlspec/utils/arrow_helpers.py +81 -0
  182. sqlspec/utils/config_resolver.py +6 -6
  183. sqlspec/utils/correlation.py +4 -5
  184. sqlspec/utils/data_transformation.py +3 -2
  185. sqlspec/utils/deprecation.py +9 -8
  186. sqlspec/utils/fixtures.py +4 -4
  187. sqlspec/utils/logging.py +46 -6
  188. sqlspec/utils/module_loader.py +205 -5
  189. sqlspec/utils/portal.py +311 -0
  190. sqlspec/utils/schema.py +288 -0
  191. sqlspec/utils/serializers.py +113 -4
  192. sqlspec/utils/sync_tools.py +36 -22
  193. sqlspec/utils/text.py +1 -2
  194. sqlspec/utils/type_guards.py +136 -20
  195. sqlspec/utils/version.py +433 -0
  196. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/METADATA +41 -22
  197. sqlspec-0.28.0.dist-info/RECORD +221 -0
  198. sqlspec/builder/mixins/__init__.py +0 -55
  199. sqlspec/builder/mixins/_cte_and_set_ops.py +0 -253
  200. sqlspec/builder/mixins/_delete_operations.py +0 -50
  201. sqlspec/builder/mixins/_insert_operations.py +0 -282
  202. sqlspec/builder/mixins/_merge_operations.py +0 -698
  203. sqlspec/builder/mixins/_order_limit_operations.py +0 -145
  204. sqlspec/builder/mixins/_pivot_operations.py +0 -157
  205. sqlspec/builder/mixins/_select_operations.py +0 -930
  206. sqlspec/builder/mixins/_update_operations.py +0 -199
  207. sqlspec/builder/mixins/_where_clause.py +0 -1298
  208. sqlspec-0.26.0.dist-info/RECORD +0 -157
  209. sqlspec-0.26.0.dist-info/licenses/NOTICE +0 -29
  210. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/WHEEL +0 -0
  211. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/entry_points.txt +0 -0
  212. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,930 +0,0 @@
1
- # pyright: reportPrivateUsage=false
2
- """SELECT clause mixins.
3
-
4
- Provides mixins for SELECT statement functionality including column selection,
5
- CASE expressions, subqueries, and window functions.
6
- """
7
-
8
- from typing import TYPE_CHECKING, Any, Optional, Union, cast
9
-
10
- from mypy_extensions import trait
11
- from sqlglot import exp
12
- from typing_extensions import Self
13
-
14
- from sqlspec.builder._parsing_utils import parse_column_expression, parse_table_expression, to_expression
15
- from sqlspec.exceptions import SQLBuilderError
16
- from sqlspec.utils.type_guards import has_query_builder_parameters, is_expression
17
-
18
- if TYPE_CHECKING:
19
- from sqlspec.builder._column import Column, ColumnExpression, FunctionColumn
20
- from sqlspec.core.statement import SQL
21
- from sqlspec.protocols import SelectBuilderProtocol, SQLBuilderProtocol
22
-
23
- __all__ = ("Case", "CaseBuilder", "SelectClauseMixin", "SubqueryBuilder", "WindowFunctionBuilder")
24
-
25
-
26
- @trait
27
- class SelectClauseMixin:
28
- """Consolidated mixin providing all SELECT-related clauses and functionality."""
29
-
30
- __slots__ = ()
31
-
32
- def get_expression(self) -> Optional[exp.Expression]: ...
33
- def set_expression(self, expression: exp.Expression) -> None: ...
34
-
35
- def select(self, *columns: Union[str, exp.Expression, "Column", "FunctionColumn", "SQL", "Case"]) -> Self:
36
- """Add columns to SELECT clause.
37
-
38
- Raises:
39
- SQLBuilderError: If the current expression is not a SELECT statement.
40
-
41
- Returns:
42
- The current builder instance for method chaining.
43
- """
44
- builder = cast("SQLBuilderProtocol", self)
45
- current_expr = self.get_expression()
46
- if current_expr is None:
47
- self.set_expression(exp.Select())
48
- current_expr = self.get_expression()
49
-
50
- if not isinstance(current_expr, exp.Select):
51
- msg = "Cannot add select columns to a non-SELECT expression."
52
- raise SQLBuilderError(msg)
53
- for column in columns:
54
- current_expr = current_expr.select(parse_column_expression(column, builder), copy=False)
55
- self.set_expression(current_expr)
56
- return cast("Self", builder)
57
-
58
- def distinct(self, *columns: Union[str, exp.Expression, "Column", "FunctionColumn", "SQL"]) -> Self:
59
- """Add DISTINCT clause to SELECT.
60
-
61
- Args:
62
- *columns: Optional columns to make distinct. If none provided, applies DISTINCT to all selected columns.
63
-
64
- Raises:
65
- SQLBuilderError: If the current expression is not a SELECT statement.
66
-
67
- Returns:
68
- The current builder instance for method chaining.
69
- """
70
- builder = cast("SQLBuilderProtocol", self)
71
- if builder._expression is None:
72
- builder._expression = exp.Select()
73
- if not isinstance(builder._expression, exp.Select):
74
- msg = "Cannot add DISTINCT to a non-SELECT expression."
75
- raise SQLBuilderError(msg)
76
- if not columns:
77
- builder._expression.set("distinct", exp.Distinct())
78
- else:
79
- distinct_columns = [parse_column_expression(column, builder) for column in columns]
80
- builder._expression.set("distinct", exp.Distinct(expressions=distinct_columns))
81
- return cast("Self", builder)
82
-
83
- def from_(self, table: Union[str, exp.Expression, Any], alias: Optional[str] = None) -> Self:
84
- """Add FROM clause.
85
-
86
- Args:
87
- table: The table name, expression, or subquery to select from.
88
- alias: Optional alias for the table.
89
-
90
- Raises:
91
- SQLBuilderError: If the current expression is not a SELECT statement or if the table type is unsupported.
92
-
93
- Returns:
94
- The current builder instance for method chaining.
95
- """
96
- builder = cast("SQLBuilderProtocol", self)
97
- if builder._expression is None:
98
- builder._expression = exp.Select()
99
- if not isinstance(builder._expression, exp.Select):
100
- msg = "FROM clause is only supported for SELECT statements."
101
- raise SQLBuilderError(msg)
102
- from_expr: exp.Expression
103
- if isinstance(table, str):
104
- from_expr = parse_table_expression(table, alias)
105
- elif is_expression(table):
106
- from_expr = exp.alias_(table, alias) if alias else table
107
- elif has_query_builder_parameters(table):
108
- subquery = table.build()
109
- sql_str = subquery.sql if hasattr(subquery, "sql") and not callable(subquery.sql) else str(subquery)
110
- subquery_exp = exp.paren(exp.maybe_parse(sql_str, dialect=getattr(builder, "dialect", None)))
111
- from_expr = exp.alias_(subquery_exp, alias) if alias else subquery_exp
112
- current_parameters = getattr(builder, "_parameters", None)
113
- merged_parameters = getattr(type(builder), "ParameterConverter", None)
114
- if merged_parameters and hasattr(subquery, "parameters"):
115
- subquery_parameters = getattr(subquery, "parameters", {})
116
- merged_parameters = merged_parameters.merge_parameters(
117
- parameters=subquery_parameters,
118
- args=current_parameters if isinstance(current_parameters, list) else None,
119
- kwargs=current_parameters if isinstance(current_parameters, dict) else {},
120
- )
121
- setattr(builder, "_parameters", merged_parameters)
122
- else:
123
- from_expr = table
124
- builder._expression = builder._expression.from_(from_expr, copy=False)
125
- return cast("Self", builder)
126
-
127
- def group_by(self, *columns: Union[str, exp.Expression]) -> Self:
128
- """Add GROUP BY clause.
129
-
130
- Args:
131
- *columns: Columns to group by. Can be column names, expressions,
132
- or special grouping expressions like ROLLUP, CUBE, etc.
133
-
134
- Returns:
135
- The current builder instance for method chaining.
136
- """
137
- current_expr = self.get_expression()
138
- if current_expr is None or not isinstance(current_expr, exp.Select):
139
- return self
140
-
141
- for column in columns:
142
- current_expr = current_expr.group_by(exp.column(column) if isinstance(column, str) else column, copy=False)
143
- self.set_expression(current_expr)
144
- return self
145
-
146
- def group_by_rollup(self, *columns: Union[str, exp.Expression]) -> Self:
147
- """Add GROUP BY ROLLUP clause.
148
-
149
- ROLLUP generates subtotals and grand totals for a hierarchical set of columns.
150
-
151
- Args:
152
- *columns: Columns to include in the rollup hierarchy.
153
-
154
- Returns:
155
- The current builder instance for method chaining.
156
-
157
- Example:
158
- ```python
159
- query = (
160
- sql.select("product", "region", sql.sum("sales"))
161
- .from_("sales_data")
162
- .group_by_rollup("product", "region")
163
- )
164
- ```
165
- """
166
- column_exprs = [exp.column(col) if isinstance(col, str) else col for col in columns]
167
- rollup_expr = exp.Rollup(expressions=column_exprs)
168
- return self.group_by(rollup_expr)
169
-
170
- def group_by_cube(self, *columns: Union[str, exp.Expression]) -> Self:
171
- """Add GROUP BY CUBE clause.
172
-
173
- CUBE generates subtotals for all possible combinations of the specified columns.
174
-
175
- Args:
176
- *columns: Columns to include in the cube.
177
-
178
- Returns:
179
- The current builder instance for method chaining.
180
-
181
- Example:
182
- ```python
183
- query = (
184
- sql.select("product", "region", sql.sum("sales"))
185
- .from_("sales_data")
186
- .group_by_cube("product", "region")
187
- )
188
- ```
189
- """
190
- column_exprs = [exp.column(col) if isinstance(col, str) else col for col in columns]
191
- cube_expr = exp.Cube(expressions=column_exprs)
192
- return self.group_by(cube_expr)
193
-
194
- def group_by_grouping_sets(self, *column_sets: Union[tuple[str, ...], list[str]]) -> Self:
195
- """Add GROUP BY GROUPING SETS clause.
196
-
197
- GROUPING SETS allows you to specify multiple grouping sets in a single query.
198
-
199
- Args:
200
- *column_sets: Sets of columns to group by. Each set can be a tuple or list.
201
- Empty tuple/list creates a grand total grouping.
202
-
203
- Returns:
204
- The current builder instance for method chaining.
205
-
206
- Example:
207
- ```python
208
- query = (
209
- sql.select("product", "region", sql.sum("sales"))
210
- .from_("sales_data")
211
- .group_by_grouping_sets(("product",), ("region",), ())
212
- )
213
- ```
214
- """
215
- set_expressions = []
216
- for column_set in column_sets:
217
- if isinstance(column_set, (tuple, list)):
218
- if len(column_set) == 0:
219
- set_expressions.append(exp.Tuple(expressions=[]))
220
- else:
221
- columns = [exp.column(col) for col in column_set]
222
- set_expressions.append(exp.Tuple(expressions=columns))
223
- else:
224
- set_expressions.append(exp.column(column_set))
225
-
226
- grouping_sets_expr = exp.GroupingSets(expressions=set_expressions)
227
- return self.group_by(grouping_sets_expr)
228
-
229
- def count_(self, column: "Union[str, exp.Expression]" = "*", alias: Optional[str] = None) -> Self:
230
- """Add COUNT function to SELECT clause.
231
-
232
- Args:
233
- column: The column to count (default is "*").
234
- alias: Optional alias for the count.
235
-
236
- Returns:
237
- The current builder instance for method chaining.
238
- """
239
- builder = cast("SelectBuilderProtocol", self)
240
- if column == "*":
241
- count_expr = exp.Count(this=exp.Star())
242
- else:
243
- col_expr = exp.column(column) if isinstance(column, str) else column
244
- count_expr = exp.Count(this=col_expr)
245
-
246
- select_expr = exp.alias_(count_expr, alias) if alias else count_expr
247
- return cast("Self", builder.select(select_expr))
248
-
249
- def sum_(self, column: Union[str, exp.Expression], alias: Optional[str] = None) -> Self:
250
- """Add SUM function to SELECT clause.
251
-
252
- Args:
253
- column: The column to sum.
254
- alias: Optional alias for the sum.
255
-
256
- Returns:
257
- The current builder instance for method chaining.
258
- """
259
- builder = cast("SelectBuilderProtocol", self)
260
- col_expr = exp.column(column) if isinstance(column, str) else column
261
- sum_expr = exp.Sum(this=col_expr)
262
- select_expr = exp.alias_(sum_expr, alias) if alias else sum_expr
263
- return cast("Self", builder.select(select_expr))
264
-
265
- def avg_(self, column: Union[str, exp.Expression], alias: Optional[str] = None) -> Self:
266
- """Add AVG function to SELECT clause.
267
-
268
- Args:
269
- column: The column to average.
270
- alias: Optional alias for the average.
271
-
272
- Returns:
273
- The current builder instance for method chaining.
274
- """
275
- builder = cast("SelectBuilderProtocol", self)
276
- col_expr = exp.column(column) if isinstance(column, str) else column
277
- avg_expr = exp.Avg(this=col_expr)
278
- select_expr = exp.alias_(avg_expr, alias) if alias else avg_expr
279
- return cast("Self", builder.select(select_expr))
280
-
281
- def max_(self, column: Union[str, exp.Expression], alias: Optional[str] = None) -> Self:
282
- """Add MAX function to SELECT clause.
283
-
284
- Args:
285
- column: The column to find the maximum of.
286
- alias: Optional alias for the maximum.
287
-
288
- Returns:
289
- The current builder instance for method chaining.
290
- """
291
- builder = cast("SelectBuilderProtocol", self)
292
- col_expr = exp.column(column) if isinstance(column, str) else column
293
- max_expr = exp.Max(this=col_expr)
294
- select_expr = exp.alias_(max_expr, alias) if alias else max_expr
295
- return cast("Self", builder.select(select_expr))
296
-
297
- def min_(self, column: Union[str, exp.Expression], alias: Optional[str] = None) -> Self:
298
- """Add MIN function to SELECT clause.
299
-
300
- Args:
301
- column: The column to find the minimum of.
302
- alias: Optional alias for the minimum.
303
-
304
- Returns:
305
- The current builder instance for method chaining.
306
- """
307
- builder = cast("SelectBuilderProtocol", self)
308
- col_expr = exp.column(column) if isinstance(column, str) else column
309
- min_expr = exp.Min(this=col_expr)
310
- select_expr = exp.alias_(min_expr, alias) if alias else min_expr
311
- return cast("Self", builder.select(select_expr))
312
-
313
- def array_agg(self, column: Union[str, exp.Expression], alias: Optional[str] = None) -> Self:
314
- """Add ARRAY_AGG aggregate function to SELECT clause.
315
-
316
- Args:
317
- column: The column to aggregate into an array.
318
- alias: Optional alias for the result.
319
-
320
- Returns:
321
- The current builder instance for method chaining.
322
- """
323
- builder = cast("SelectBuilderProtocol", self)
324
- col_expr = exp.column(column) if isinstance(column, str) else column
325
- array_agg_expr = exp.ArrayAgg(this=col_expr)
326
- select_expr = exp.alias_(array_agg_expr, alias) if alias else array_agg_expr
327
- return cast("Self", builder.select(select_expr))
328
-
329
- def count_distinct(self, column: Union[str, exp.Expression], alias: Optional[str] = None) -> Self:
330
- """Add COUNT(DISTINCT column) to SELECT clause.
331
-
332
- Args:
333
- column: The column to count distinct values of.
334
- alias: Optional alias for the count.
335
-
336
- Returns:
337
- The current builder instance for method chaining.
338
- """
339
- builder = cast("SelectBuilderProtocol", self)
340
- col_expr = exp.column(column) if isinstance(column, str) else column
341
- count_expr = exp.Count(this=exp.Distinct(expressions=[col_expr]))
342
- select_expr = exp.alias_(count_expr, alias) if alias else count_expr
343
- return cast("Self", builder.select(select_expr))
344
-
345
- def stddev(self, column: Union[str, exp.Expression], alias: Optional[str] = None) -> Self:
346
- """Add STDDEV aggregate function to SELECT clause.
347
-
348
- Args:
349
- column: The column to calculate standard deviation of.
350
- alias: Optional alias for the result.
351
-
352
- Returns:
353
- The current builder instance for method chaining.
354
- """
355
- builder = cast("SelectBuilderProtocol", self)
356
- col_expr = exp.column(column) if isinstance(column, str) else column
357
- stddev_expr = exp.Stddev(this=col_expr)
358
- select_expr = exp.alias_(stddev_expr, alias) if alias else stddev_expr
359
- return cast("Self", builder.select(select_expr))
360
-
361
- def stddev_pop(self, column: Union[str, exp.Expression], alias: Optional[str] = None) -> Self:
362
- """Add STDDEV_POP aggregate function to SELECT clause.
363
-
364
- Args:
365
- column: The column to calculate population standard deviation of.
366
- alias: Optional alias for the result.
367
-
368
- Returns:
369
- The current builder instance for method chaining.
370
- """
371
- builder = cast("SelectBuilderProtocol", self)
372
- col_expr = exp.column(column) if isinstance(column, str) else column
373
- stddev_pop_expr = exp.StddevPop(this=col_expr)
374
- select_expr = exp.alias_(stddev_pop_expr, alias) if alias else stddev_pop_expr
375
- return cast("Self", builder.select(select_expr))
376
-
377
- def stddev_samp(self, column: Union[str, exp.Expression], alias: Optional[str] = None) -> Self:
378
- """Add STDDEV_SAMP aggregate function to SELECT clause.
379
-
380
- Args:
381
- column: The column to calculate sample standard deviation of.
382
- alias: Optional alias for the result.
383
-
384
- Returns:
385
- The current builder instance for method chaining.
386
- """
387
- builder = cast("SelectBuilderProtocol", self)
388
- col_expr = exp.column(column) if isinstance(column, str) else column
389
- stddev_samp_expr = exp.StddevSamp(this=col_expr)
390
- select_expr = exp.alias_(stddev_samp_expr, alias) if alias else stddev_samp_expr
391
- return cast("Self", builder.select(select_expr))
392
-
393
- def variance(self, column: Union[str, exp.Expression], alias: Optional[str] = None) -> Self:
394
- """Add VARIANCE aggregate function to SELECT clause.
395
-
396
- Args:
397
- column: The column to calculate variance of.
398
- alias: Optional alias for the result.
399
-
400
- Returns:
401
- The current builder instance for method chaining.
402
- """
403
- builder = cast("SelectBuilderProtocol", self)
404
- col_expr = exp.column(column) if isinstance(column, str) else column
405
- variance_expr = exp.Variance(this=col_expr)
406
- select_expr = exp.alias_(variance_expr, alias) if alias else variance_expr
407
- return cast("Self", builder.select(select_expr))
408
-
409
- def var_pop(self, column: Union[str, exp.Expression], alias: Optional[str] = None) -> Self:
410
- """Add VAR_POP aggregate function to SELECT clause.
411
-
412
- Args:
413
- column: The column to calculate population variance of.
414
- alias: Optional alias for the result.
415
-
416
- Returns:
417
- The current builder instance for method chaining.
418
- """
419
- builder = cast("SelectBuilderProtocol", self)
420
- col_expr = exp.column(column) if isinstance(column, str) else column
421
- var_pop_expr = exp.VariancePop(this=col_expr)
422
- select_expr = exp.alias_(var_pop_expr, alias) if alias else var_pop_expr
423
- return cast("Self", builder.select(select_expr))
424
-
425
- def string_agg(self, column: Union[str, exp.Expression], separator: str = ",", alias: Optional[str] = None) -> Self:
426
- """Add STRING_AGG aggregate function to SELECT clause.
427
-
428
- Args:
429
- column: The column to aggregate into a string.
430
- separator: The separator between values (default is comma).
431
- alias: Optional alias for the result.
432
-
433
- Returns:
434
- The current builder instance for method chaining.
435
-
436
- Note:
437
- Different databases have different names for this function:
438
- - PostgreSQL: STRING_AGG
439
- - MySQL: GROUP_CONCAT
440
- - SQLite: GROUP_CONCAT
441
- SQLGlot will handle the translation.
442
- """
443
- builder = cast("SelectBuilderProtocol", self)
444
- col_expr = exp.column(column) if isinstance(column, str) else column
445
- string_agg_expr = exp.GroupConcat(this=col_expr, separator=exp.convert(separator))
446
- select_expr = exp.alias_(string_agg_expr, alias) if alias else string_agg_expr
447
- return cast("Self", builder.select(select_expr))
448
-
449
- def json_agg(self, column: Union[str, exp.Expression], alias: Optional[str] = None) -> Self:
450
- """Add JSON_AGG aggregate function to SELECT clause.
451
-
452
- Args:
453
- column: The column to aggregate into a JSON array.
454
- alias: Optional alias for the result.
455
-
456
- Returns:
457
- The current builder instance for method chaining.
458
- """
459
- builder = cast("SelectBuilderProtocol", self)
460
- col_expr = exp.column(column) if isinstance(column, str) else column
461
- json_agg_expr = exp.JSONArrayAgg(this=col_expr)
462
- select_expr = exp.alias_(json_agg_expr, alias) if alias else json_agg_expr
463
- return cast("Self", builder.select(select_expr))
464
-
465
- def window(
466
- self,
467
- function_expr: Union[str, exp.Expression],
468
- partition_by: Optional[Union[str, list[str], exp.Expression, list[exp.Expression]]] = None,
469
- order_by: Optional[Union[str, list[str], exp.Expression, list[exp.Expression]]] = None,
470
- frame: Optional[str] = None,
471
- alias: Optional[str] = None,
472
- ) -> Self:
473
- """Add a window function to the SELECT clause.
474
-
475
- Args:
476
- function_expr: The window function expression (e.g., "COUNT(*)", "ROW_NUMBER()").
477
- partition_by: Column(s) to partition by.
478
- order_by: Column(s) to order by within the window.
479
- frame: Window frame specification (e.g., "ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW").
480
- alias: Optional alias for the window function.
481
-
482
- Raises:
483
- SQLBuilderError: If the current expression is not a SELECT statement or function parsing fails.
484
-
485
- Returns:
486
- The current builder instance for method chaining.
487
- """
488
- current_expr = self.get_expression()
489
- if current_expr is None:
490
- self.set_expression(exp.Select())
491
- current_expr = self.get_expression()
492
-
493
- if not isinstance(current_expr, exp.Select):
494
- msg = "Cannot add window function to a non-SELECT expression."
495
- raise SQLBuilderError(msg)
496
-
497
- func_expr_parsed: exp.Expression
498
- if isinstance(function_expr, str):
499
- parsed: Optional[exp.Expression] = exp.maybe_parse(function_expr, dialect=getattr(self, "dialect", None))
500
- if not parsed:
501
- msg = f"Could not parse function expression: {function_expr}"
502
- raise SQLBuilderError(msg)
503
- func_expr_parsed = parsed
504
- else:
505
- func_expr_parsed = function_expr
506
-
507
- over_args: dict[str, Any] = {}
508
- if partition_by:
509
- if isinstance(partition_by, str):
510
- over_args["partition_by"] = [exp.column(partition_by)]
511
- elif isinstance(partition_by, list):
512
- over_args["partition_by"] = [exp.column(col) if isinstance(col, str) else col for col in partition_by]
513
- elif isinstance(partition_by, exp.Expression):
514
- over_args["partition_by"] = [partition_by]
515
-
516
- if order_by:
517
- if isinstance(order_by, str):
518
- over_args["order"] = exp.column(order_by).asc()
519
- elif isinstance(order_by, list):
520
- order_expressions: list[Union[exp.Expression, exp.Column]] = []
521
- for col in order_by:
522
- if isinstance(col, str):
523
- order_expressions.append(exp.column(col).asc())
524
- else:
525
- order_expressions.append(col)
526
- over_args["order"] = exp.Order(expressions=order_expressions)
527
- elif isinstance(order_by, exp.Expression):
528
- over_args["order"] = order_by
529
-
530
- if frame:
531
- frame_expr: Optional[exp.Expression] = exp.maybe_parse(frame, dialect=getattr(self, "dialect", None))
532
- if frame_expr:
533
- over_args["frame"] = frame_expr
534
-
535
- window_expr = exp.Window(this=func_expr_parsed, **over_args)
536
- current_expr = current_expr.select(exp.alias_(window_expr, alias) if alias else window_expr, copy=False)
537
- self.set_expression(current_expr)
538
- return self
539
-
540
- def case_(self, alias: "Optional[str]" = None) -> "CaseBuilder":
541
- """Create a CASE expression for the SELECT clause.
542
-
543
- Args:
544
- alias: Optional alias for the CASE expression.
545
-
546
- Returns:
547
- CaseBuilder: A CaseBuilder instance for building the CASE expression.
548
- """
549
- builder = cast("SelectBuilderProtocol", self)
550
- return CaseBuilder(builder, alias)
551
-
552
-
553
- class CaseBuilder:
554
- """Builder for CASE expressions."""
555
-
556
- __slots__ = ("_alias", "_case_expr", "_parent")
557
-
558
- def __init__(self, parent: "SelectBuilderProtocol", alias: "Optional[str]" = None) -> None:
559
- """Initialize CaseBuilder.
560
-
561
- Args:
562
- parent: The parent builder with select capabilities.
563
- alias: Optional alias for the CASE expression.
564
- """
565
- self._parent = parent
566
- self._alias = alias
567
- self._case_expr = exp.Case()
568
-
569
- def when(self, condition: "Union[str, exp.Expression]", value: "Any") -> "CaseBuilder":
570
- """Add WHEN clause to CASE expression.
571
-
572
- Args:
573
- condition: The condition to test.
574
- value: The value to return if condition is true.
575
-
576
- Returns:
577
- CaseBuilder: The current builder instance for method chaining.
578
- """
579
- cond_expr = exp.condition(condition) if isinstance(condition, str) else condition
580
- param_name = self._parent._generate_unique_parameter_name("case_when_value")
581
- param_name = self._parent.add_parameter(value, name=param_name)[1]
582
- value_expr = exp.Placeholder(this=param_name)
583
-
584
- when_clause = exp.When(this=cond_expr, then=value_expr)
585
-
586
- if not self._case_expr.args.get("ifs"):
587
- self._case_expr.set("ifs", [])
588
- self._case_expr.args["ifs"].append(when_clause)
589
- return self
590
-
591
- def else_(self, value: "Any") -> "CaseBuilder":
592
- """Add ELSE clause to CASE expression.
593
-
594
- Args:
595
- value: The value to return if no conditions match.
596
-
597
- Returns:
598
- CaseBuilder: The current builder instance for method chaining.
599
- """
600
- param_name = self._parent._generate_unique_parameter_name("case_else_value")
601
- param_name = self._parent.add_parameter(value, name=param_name)[1]
602
- value_expr = exp.Placeholder(this=param_name)
603
- self._case_expr.set("default", value_expr)
604
- return self
605
-
606
- def end(self) -> "SelectBuilderProtocol":
607
- """Finalize the CASE expression and add it to the SELECT clause.
608
-
609
- Returns:
610
- The parent builder instance.
611
- """
612
- select_expr = exp.alias_(self._case_expr, self._alias) if self._alias else self._case_expr
613
- return self._parent.select(select_expr)
614
-
615
-
616
- @trait
617
- class WindowFunctionBuilder:
618
- """Builder for window functions with fluent syntax.
619
-
620
- Example:
621
- ```python
622
- from sqlspec import sql
623
-
624
- # sql.row_number_.partition_by("department").order_by("salary")
625
- window_func = (
626
- sql.row_number_.partition_by("department")
627
- .order_by("salary")
628
- .as_("row_num")
629
- )
630
- ```
631
- """
632
-
633
- def __init__(self, function_name: str) -> None:
634
- """Initialize the window function builder.
635
-
636
- Args:
637
- function_name: Name of the window function (row_number, rank, etc.)
638
- """
639
- self._function_name = function_name
640
- self._partition_by_cols: list[exp.Expression] = []
641
- self._order_by_cols: list[exp.Expression] = []
642
- self._alias: Optional[str] = None
643
-
644
- def __eq__(self, other: object) -> "ColumnExpression": # type: ignore[override]
645
- """Equal to (==) - convert to expression then compare."""
646
- from sqlspec.builder._column import ColumnExpression
647
-
648
- window_expr = self._build_expression()
649
- if other is None:
650
- return ColumnExpression(exp.Is(this=window_expr, expression=exp.Null()))
651
- return ColumnExpression(exp.EQ(this=window_expr, expression=exp.convert(other)))
652
-
653
- def __hash__(self) -> int:
654
- """Make WindowFunctionBuilder hashable."""
655
- return hash(id(self))
656
-
657
- def partition_by(self, *columns: Union[str, exp.Expression]) -> "WindowFunctionBuilder":
658
- """Add PARTITION BY clause.
659
-
660
- Args:
661
- *columns: Columns to partition by.
662
-
663
- Returns:
664
- Self for method chaining.
665
- """
666
- for col in columns:
667
- col_expr = exp.column(col) if isinstance(col, str) else col
668
- self._partition_by_cols.append(col_expr)
669
- return self
670
-
671
- def order_by(self, *columns: Union[str, exp.Expression]) -> "WindowFunctionBuilder":
672
- """Add ORDER BY clause.
673
-
674
- Args:
675
- *columns: Columns to order by.
676
-
677
- Returns:
678
- Self for method chaining.
679
- """
680
- for col in columns:
681
- if isinstance(col, str):
682
- col_expr = exp.column(col).asc()
683
- self._order_by_cols.append(col_expr)
684
- else:
685
- # Convert to ordered expression
686
- self._order_by_cols.append(exp.Ordered(this=col, desc=False))
687
- return self
688
-
689
- def as_(self, alias: str) -> exp.Alias:
690
- """Complete the window function with an alias.
691
-
692
- Args:
693
- alias: Alias name for the window function.
694
-
695
- Returns:
696
- Aliased window function expression.
697
- """
698
- window_expr = self._build_expression()
699
- return cast("exp.Alias", exp.alias_(window_expr, alias))
700
-
701
- def build(self) -> exp.Expression:
702
- """Complete the window function without an alias.
703
-
704
- Returns:
705
- Window function expression.
706
- """
707
- return self._build_expression()
708
-
709
- def _build_expression(self) -> exp.Expression:
710
- """Build the complete window function expression."""
711
- # Create the function expression
712
- func_expr = exp.Anonymous(this=self._function_name.upper(), expressions=[])
713
-
714
- # Build the OVER clause arguments
715
- over_args: dict[str, Any] = {}
716
-
717
- if self._partition_by_cols:
718
- over_args["partition_by"] = self._partition_by_cols
719
-
720
- if self._order_by_cols:
721
- over_args["order"] = exp.Order(expressions=self._order_by_cols)
722
-
723
- return exp.Window(this=func_expr, **over_args)
724
-
725
-
726
- @trait
727
- class SubqueryBuilder:
728
- """Builder for subquery operations with fluent syntax.
729
-
730
- Example:
731
- ```python
732
- from sqlspec import sql
733
-
734
- # sql.exists_(subquery)
735
- exists_check = sql.exists_(
736
- sql.select("1")
737
- .from_("orders")
738
- .where_eq("user_id", sql.users.id)
739
- )
740
-
741
- # sql.in_(subquery)
742
- in_check = sql.in_(
743
- sql.select("category_id")
744
- .from_("categories")
745
- .where_eq("active", True)
746
- )
747
- ```
748
- """
749
-
750
- def __init__(self, operation: str) -> None:
751
- """Initialize the subquery builder.
752
-
753
- Args:
754
- operation: Type of subquery operation (exists, in, any, all)
755
- """
756
- self._operation = operation
757
-
758
- def __eq__(self, other: object) -> "ColumnExpression": # type: ignore[override]
759
- """Equal to (==) - not typically used but needed for type consistency."""
760
- from sqlspec.builder._column import ColumnExpression
761
-
762
- # SubqueryBuilder doesn't have a direct expression, so this is a placeholder
763
- # In practice, this shouldn't be called as subqueries are used differently
764
- placeholder_expr = exp.Literal.string(f"subquery_{self._operation}")
765
- if other is None:
766
- return ColumnExpression(exp.Is(this=placeholder_expr, expression=exp.Null()))
767
- return ColumnExpression(exp.EQ(this=placeholder_expr, expression=exp.convert(other)))
768
-
769
- def __hash__(self) -> int:
770
- """Make SubqueryBuilder hashable."""
771
- return hash(id(self))
772
-
773
- def __call__(self, subquery: Union[str, exp.Expression, Any]) -> exp.Expression:
774
- """Build the subquery expression.
775
-
776
- Args:
777
- subquery: The subquery - can be a SQL string, SelectBuilder, or expression
778
-
779
- Returns:
780
- The subquery expression (EXISTS, IN, ANY, ALL, etc.)
781
- """
782
- subquery_expr: exp.Expression
783
- if isinstance(subquery, str):
784
- # Parse as SQL
785
- parsed: Optional[exp.Expression] = exp.maybe_parse(subquery)
786
- if not parsed:
787
- msg = f"Could not parse subquery SQL: {subquery}"
788
- raise SQLBuilderError(msg)
789
- subquery_expr = parsed
790
- elif hasattr(subquery, "build") and callable(getattr(subquery, "build", None)):
791
- # It's a query builder - build it to get the SQL and parse
792
- built_query = subquery.build() # pyright: ignore[reportAttributeAccessIssue]
793
- subquery_expr = exp.maybe_parse(built_query.sql)
794
- if not subquery_expr:
795
- msg = f"Could not parse built query: {built_query.sql}"
796
- raise SQLBuilderError(msg)
797
- elif isinstance(subquery, exp.Expression):
798
- subquery_expr = subquery
799
- else:
800
- # Try to convert to expression
801
- parsed = exp.maybe_parse(str(subquery))
802
- if not parsed:
803
- msg = f"Could not convert subquery to expression: {subquery}"
804
- raise SQLBuilderError(msg)
805
- subquery_expr = parsed
806
-
807
- # Build the appropriate expression based on operation
808
- if self._operation == "exists":
809
- return exp.Exists(this=subquery_expr)
810
- if self._operation == "in":
811
- # For IN, we create a subquery that can be used with WHERE column IN (subquery)
812
- return exp.In(expressions=[subquery_expr])
813
- if self._operation == "any":
814
- return exp.Any(this=subquery_expr)
815
- if self._operation == "all":
816
- return exp.All(this=subquery_expr)
817
- msg = f"Unknown subquery operation: {self._operation}"
818
- raise SQLBuilderError(msg)
819
-
820
-
821
- @trait
822
- class Case:
823
- """Builder for CASE expressions using the SQL factory.
824
-
825
- Example:
826
- ```python
827
- from sqlspec import sql
828
-
829
- case_expr = (
830
- sql.case()
831
- .when(sql.age < 18, "Minor")
832
- .when(sql.age < 65, "Adult")
833
- .else_("Senior")
834
- .end()
835
- )
836
- ```
837
- """
838
-
839
- def __init__(self) -> None:
840
- """Initialize the CASE expression builder."""
841
- self._conditions: list[exp.If] = []
842
- self._default: Optional[exp.Expression] = None
843
-
844
- def __eq__(self, other: object) -> "ColumnExpression": # type: ignore[override]
845
- """Equal to (==) - convert to expression then compare."""
846
- from sqlspec.builder._column import ColumnExpression
847
-
848
- case_expr = exp.Case(ifs=self._conditions, default=self._default)
849
- if other is None:
850
- return ColumnExpression(exp.Is(this=case_expr, expression=exp.Null()))
851
- return ColumnExpression(exp.EQ(this=case_expr, expression=exp.convert(other)))
852
-
853
- def __hash__(self) -> int:
854
- """Make Case hashable."""
855
- return hash(id(self))
856
-
857
- def when(self, condition: Union[str, exp.Expression], value: Union[str, exp.Expression, Any]) -> Self:
858
- """Add a WHEN clause.
859
-
860
- Args:
861
- condition: Condition to test.
862
- value: Value to return if condition is true.
863
-
864
- Returns:
865
- Self for method chaining.
866
- """
867
- cond_expr = exp.maybe_parse(condition) or exp.column(condition) if isinstance(condition, str) else condition
868
- val_expr = to_expression(value)
869
-
870
- when_clause = exp.If(this=cond_expr, true=val_expr)
871
- self._conditions.append(when_clause)
872
- return self
873
-
874
- def else_(self, value: Union[str, exp.Expression, Any]) -> Self:
875
- """Add an ELSE clause.
876
-
877
- Args:
878
- value: Default value to return.
879
-
880
- Returns:
881
- Self for method chaining.
882
- """
883
- self._default = to_expression(value)
884
- return self
885
-
886
- def end(self) -> Self:
887
- """Complete the CASE expression.
888
-
889
- Returns:
890
- Complete CASE expression.
891
- """
892
- return self
893
-
894
- @property
895
- def _expression(self) -> exp.Case:
896
- """Get the sqlglot expression for this case builder.
897
-
898
- This allows the CaseBuilder to be used wherever expressions are expected.
899
- """
900
- return exp.Case(ifs=self._conditions, default=self._default)
901
-
902
- def as_(self, alias: str) -> exp.Alias:
903
- """Complete the CASE expression with an alias.
904
-
905
- Args:
906
- alias: Alias name for the CASE expression.
907
-
908
- Returns:
909
- Aliased CASE expression.
910
- """
911
- case_expr = exp.Case(ifs=self._conditions, default=self._default)
912
- return cast("exp.Alias", exp.alias_(case_expr, alias))
913
-
914
- @property
915
- def conditions(self) -> "list[exp.If]":
916
- """Get CASE conditions (public API).
917
-
918
- Returns:
919
- List of If expressions representing WHEN clauses
920
- """
921
- return self._conditions
922
-
923
- @property
924
- def default(self) -> Optional[exp.Expression]:
925
- """Get CASE default value (public API).
926
-
927
- Returns:
928
- Default expression for the ELSE clause, or None
929
- """
930
- return self._default