sqlspec 0.25.0__py3-none-any.whl → 0.27.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 (199) hide show
  1. sqlspec/__init__.py +7 -15
  2. sqlspec/_serialization.py +256 -24
  3. sqlspec/_typing.py +71 -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 +870 -0
  7. sqlspec/adapters/adbc/config.py +69 -12
  8. sqlspec/adapters/adbc/data_dictionary.py +340 -0
  9. sqlspec/adapters/adbc/driver.py +266 -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 +153 -0
  13. sqlspec/adapters/aiosqlite/_types.py +1 -1
  14. sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
  15. sqlspec/adapters/aiosqlite/adk/store.py +527 -0
  16. sqlspec/adapters/aiosqlite/config.py +88 -15
  17. sqlspec/adapters/aiosqlite/data_dictionary.py +149 -0
  18. sqlspec/adapters/aiosqlite/driver.py +143 -40
  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 +2 -2
  24. sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
  25. sqlspec/adapters/asyncmy/adk/store.py +493 -0
  26. sqlspec/adapters/asyncmy/config.py +68 -23
  27. sqlspec/adapters/asyncmy/data_dictionary.py +161 -0
  28. sqlspec/adapters/asyncmy/driver.py +313 -58
  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 +450 -0
  36. sqlspec/adapters/asyncpg/config.py +59 -35
  37. sqlspec/adapters/asyncpg/data_dictionary.py +173 -0
  38. sqlspec/adapters/asyncpg/driver.py +170 -25
  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 +576 -0
  44. sqlspec/adapters/bigquery/config.py +27 -10
  45. sqlspec/adapters/bigquery/data_dictionary.py +149 -0
  46. sqlspec/adapters/bigquery/driver.py +368 -142
  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 +125 -0
  50. sqlspec/adapters/duckdb/_types.py +1 -1
  51. sqlspec/adapters/duckdb/adk/__init__.py +14 -0
  52. sqlspec/adapters/duckdb/adk/store.py +553 -0
  53. sqlspec/adapters/duckdb/config.py +80 -20
  54. sqlspec/adapters/duckdb/data_dictionary.py +163 -0
  55. sqlspec/adapters/duckdb/driver.py +167 -45
  56. sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
  57. sqlspec/adapters/duckdb/litestar/store.py +332 -0
  58. sqlspec/adapters/duckdb/pool.py +4 -4
  59. sqlspec/adapters/duckdb/type_converter.py +133 -0
  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 +1745 -0
  64. sqlspec/adapters/oracledb/config.py +122 -32
  65. sqlspec/adapters/oracledb/data_dictionary.py +509 -0
  66. sqlspec/adapters/oracledb/driver.py +353 -91
  67. sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
  68. sqlspec/adapters/oracledb/litestar/store.py +767 -0
  69. sqlspec/adapters/oracledb/migrations.py +348 -73
  70. sqlspec/adapters/oracledb/type_converter.py +207 -0
  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 +482 -0
  75. sqlspec/adapters/psqlpy/config.py +46 -17
  76. sqlspec/adapters/psqlpy/data_dictionary.py +172 -0
  77. sqlspec/adapters/psqlpy/driver.py +123 -209
  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 +102 -0
  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 +944 -0
  85. sqlspec/adapters/psycopg/config.py +69 -35
  86. sqlspec/adapters/psycopg/data_dictionary.py +331 -0
  87. sqlspec/adapters/psycopg/driver.py +238 -81
  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 +572 -0
  95. sqlspec/adapters/sqlite/config.py +87 -15
  96. sqlspec/adapters/sqlite/data_dictionary.py +149 -0
  97. sqlspec/adapters/sqlite/driver.py +137 -54
  98. sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
  99. sqlspec/adapters/sqlite/litestar/store.py +318 -0
  100. sqlspec/adapters/sqlite/pool.py +18 -9
  101. sqlspec/base.py +45 -26
  102. sqlspec/builder/__init__.py +73 -4
  103. sqlspec/builder/_base.py +162 -89
  104. sqlspec/builder/_column.py +62 -29
  105. sqlspec/builder/_ddl.py +180 -121
  106. sqlspec/builder/_delete.py +5 -4
  107. sqlspec/builder/_dml.py +388 -0
  108. sqlspec/{_sql.py → builder/_factory.py} +53 -94
  109. sqlspec/builder/_insert.py +32 -131
  110. sqlspec/builder/_join.py +375 -0
  111. sqlspec/builder/_merge.py +446 -11
  112. sqlspec/builder/_parsing_utils.py +111 -17
  113. sqlspec/builder/_select.py +1457 -24
  114. sqlspec/builder/_update.py +11 -42
  115. sqlspec/cli.py +307 -194
  116. sqlspec/config.py +252 -67
  117. sqlspec/core/__init__.py +5 -4
  118. sqlspec/core/cache.py +17 -17
  119. sqlspec/core/compiler.py +62 -9
  120. sqlspec/core/filters.py +37 -37
  121. sqlspec/core/hashing.py +9 -9
  122. sqlspec/core/parameters.py +83 -48
  123. sqlspec/core/result.py +102 -46
  124. sqlspec/core/splitter.py +16 -17
  125. sqlspec/core/statement.py +36 -30
  126. sqlspec/core/type_conversion.py +235 -0
  127. sqlspec/driver/__init__.py +7 -6
  128. sqlspec/driver/_async.py +188 -151
  129. sqlspec/driver/_common.py +285 -80
  130. sqlspec/driver/_sync.py +188 -152
  131. sqlspec/driver/mixins/_result_tools.py +20 -236
  132. sqlspec/driver/mixins/_sql_translator.py +4 -4
  133. sqlspec/exceptions.py +75 -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 +73 -53
  142. sqlspec/extensions/litestar/__init__.py +21 -4
  143. sqlspec/extensions/litestar/cli.py +54 -10
  144. sqlspec/extensions/litestar/config.py +59 -266
  145. sqlspec/extensions/litestar/handlers.py +46 -17
  146. sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
  147. sqlspec/extensions/litestar/migrations/__init__.py +3 -0
  148. sqlspec/extensions/litestar/plugin.py +324 -223
  149. sqlspec/extensions/litestar/providers.py +25 -25
  150. sqlspec/extensions/litestar/store.py +265 -0
  151. sqlspec/loader.py +30 -49
  152. sqlspec/migrations/__init__.py +4 -3
  153. sqlspec/migrations/base.py +302 -39
  154. sqlspec/migrations/commands.py +611 -144
  155. sqlspec/migrations/context.py +142 -0
  156. sqlspec/migrations/fix.py +199 -0
  157. sqlspec/migrations/loaders.py +68 -23
  158. sqlspec/migrations/runner.py +543 -107
  159. sqlspec/migrations/tracker.py +237 -21
  160. sqlspec/migrations/utils.py +51 -3
  161. sqlspec/migrations/validation.py +177 -0
  162. sqlspec/protocols.py +66 -36
  163. sqlspec/storage/_utils.py +98 -0
  164. sqlspec/storage/backends/fsspec.py +134 -106
  165. sqlspec/storage/backends/local.py +78 -51
  166. sqlspec/storage/backends/obstore.py +278 -162
  167. sqlspec/storage/registry.py +75 -39
  168. sqlspec/typing.py +16 -84
  169. sqlspec/utils/config_resolver.py +153 -0
  170. sqlspec/utils/correlation.py +4 -5
  171. sqlspec/utils/data_transformation.py +3 -2
  172. sqlspec/utils/deprecation.py +9 -8
  173. sqlspec/utils/fixtures.py +4 -4
  174. sqlspec/utils/logging.py +46 -6
  175. sqlspec/utils/module_loader.py +2 -2
  176. sqlspec/utils/schema.py +288 -0
  177. sqlspec/utils/serializers.py +50 -2
  178. sqlspec/utils/sync_tools.py +21 -17
  179. sqlspec/utils/text.py +1 -2
  180. sqlspec/utils/type_guards.py +111 -20
  181. sqlspec/utils/version.py +433 -0
  182. {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/METADATA +40 -21
  183. sqlspec-0.27.0.dist-info/RECORD +207 -0
  184. sqlspec/builder/mixins/__init__.py +0 -55
  185. sqlspec/builder/mixins/_cte_and_set_ops.py +0 -254
  186. sqlspec/builder/mixins/_delete_operations.py +0 -50
  187. sqlspec/builder/mixins/_insert_operations.py +0 -282
  188. sqlspec/builder/mixins/_join_operations.py +0 -389
  189. sqlspec/builder/mixins/_merge_operations.py +0 -592
  190. sqlspec/builder/mixins/_order_limit_operations.py +0 -152
  191. sqlspec/builder/mixins/_pivot_operations.py +0 -157
  192. sqlspec/builder/mixins/_select_operations.py +0 -936
  193. sqlspec/builder/mixins/_update_operations.py +0 -218
  194. sqlspec/builder/mixins/_where_clause.py +0 -1304
  195. sqlspec-0.25.0.dist-info/RECORD +0 -139
  196. sqlspec-0.25.0.dist-info/licenses/NOTICE +0 -29
  197. {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/WHEEL +0 -0
  198. {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/entry_points.txt +0 -0
  199. {sqlspec-0.25.0.dist-info → sqlspec-0.27.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,152 +0,0 @@
1
- # pyright: reportPrivateUsage=false
2
- """ORDER BY, LIMIT, OFFSET, and RETURNING clause mixins.
3
-
4
- Provides mixins for query result ordering, limiting, and result
5
- returning functionality.
6
- """
7
-
8
- from typing import TYPE_CHECKING, 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_order_expression
15
- from sqlspec.exceptions import SQLBuilderError
16
-
17
- if TYPE_CHECKING:
18
- from sqlspec.builder._column import Column
19
- from sqlspec.builder._expression_wrappers import ExpressionWrapper
20
- from sqlspec.builder.mixins._select_operations import Case
21
- from sqlspec.protocols import SQLBuilderProtocol
22
-
23
- __all__ = ("LimitOffsetClauseMixin", "OrderByClauseMixin", "ReturningClauseMixin")
24
-
25
-
26
- @trait
27
- class OrderByClauseMixin:
28
- """Mixin providing ORDER BY clause."""
29
-
30
- __slots__ = ()
31
-
32
- # Type annotation for PyRight - this will be provided by the base class
33
- _expression: Optional[exp.Expression]
34
-
35
- def order_by(self, *items: Union[str, exp.Ordered, "Column"], desc: bool = False) -> Self:
36
- """Add ORDER BY clause.
37
-
38
- Args:
39
- *items: Columns to order by. Can be strings (column names) or sqlglot.exp.Ordered instances for specific directions (e.g., exp.column("name").desc()).
40
- desc: Whether to order in descending order (applies to all items if they are strings).
41
-
42
- Raises:
43
- SQLBuilderError: If the current expression is not a SELECT statement or if the item type is unsupported.
44
-
45
- Returns:
46
- The current builder instance for method chaining.
47
- """
48
- builder = cast("SQLBuilderProtocol", self)
49
- if not isinstance(builder._expression, exp.Select):
50
- msg = "ORDER BY is only supported for SELECT statements."
51
- raise SQLBuilderError(msg)
52
-
53
- current_expr = builder._expression
54
- for item in items:
55
- if isinstance(item, str):
56
- order_item = parse_order_expression(item)
57
- if desc:
58
- order_item = order_item.desc()
59
- else:
60
- # Extract expression from Column objects or use as-is for sqlglot expressions
61
- from sqlspec._sql import SQLFactory
62
-
63
- extracted_item = SQLFactory._extract_expression(item)
64
- order_item = extracted_item
65
- if desc and not isinstance(item, exp.Ordered):
66
- order_item = order_item.desc()
67
- current_expr = current_expr.order_by(order_item, copy=False)
68
- builder._expression = current_expr
69
- return cast("Self", builder)
70
-
71
-
72
- @trait
73
- class LimitOffsetClauseMixin:
74
- """Mixin providing LIMIT and OFFSET clauses."""
75
-
76
- __slots__ = ()
77
-
78
- # Type annotation for PyRight - this will be provided by the base class
79
- _expression: Optional[exp.Expression]
80
-
81
- def limit(self, value: int) -> Self:
82
- """Add LIMIT clause.
83
-
84
- Args:
85
- value: The maximum number of rows to return.
86
-
87
- Raises:
88
- SQLBuilderError: If the current expression is not a SELECT statement.
89
-
90
- Returns:
91
- The current builder instance for method chaining.
92
- """
93
- builder = cast("SQLBuilderProtocol", self)
94
- if not isinstance(builder._expression, exp.Select):
95
- msg = "LIMIT is only supported for SELECT statements."
96
- raise SQLBuilderError(msg)
97
- builder._expression = builder._expression.limit(exp.convert(value), copy=False)
98
- return cast("Self", builder)
99
-
100
- def offset(self, value: int) -> Self:
101
- """Add OFFSET clause.
102
-
103
- Args:
104
- value: The number of rows to skip before starting to return rows.
105
-
106
- Raises:
107
- SQLBuilderError: If the current expression is not a SELECT statement.
108
-
109
- Returns:
110
- The current builder instance for method chaining.
111
- """
112
- builder = cast("SQLBuilderProtocol", self)
113
- if not isinstance(builder._expression, exp.Select):
114
- msg = "OFFSET is only supported for SELECT statements."
115
- raise SQLBuilderError(msg)
116
- builder._expression = builder._expression.offset(exp.convert(value), copy=False)
117
- return cast("Self", builder)
118
-
119
-
120
- @trait
121
- class ReturningClauseMixin:
122
- """Mixin providing RETURNING clause."""
123
-
124
- __slots__ = ()
125
- # Type annotation for PyRight - this will be provided by the base class
126
- _expression: Optional[exp.Expression]
127
-
128
- def returning(self, *columns: Union[str, exp.Expression, "Column", "ExpressionWrapper", "Case"]) -> Self:
129
- """Add RETURNING clause to the statement.
130
-
131
- Args:
132
- *columns: Columns to return. Can be strings or sqlglot expressions.
133
-
134
- Raises:
135
- SQLBuilderError: If the current expression is not INSERT, UPDATE, or DELETE.
136
-
137
- Returns:
138
- The current builder instance for method chaining.
139
- """
140
- if self._expression is None:
141
- msg = "Cannot add RETURNING: expression is not initialized."
142
- raise SQLBuilderError(msg)
143
- valid_types = (exp.Insert, exp.Update, exp.Delete)
144
- if not isinstance(self._expression, valid_types):
145
- msg = "RETURNING is only supported for INSERT, UPDATE, and DELETE statements."
146
- raise SQLBuilderError(msg)
147
- # Extract expressions from various wrapper types
148
- from sqlspec._sql import SQLFactory
149
-
150
- returning_exprs = [SQLFactory._extract_expression(c) for c in columns]
151
- self._expression.set("returning", exp.Returning(expressions=returning_exprs))
152
- return self
@@ -1,157 +0,0 @@
1
- # pyright: reportPrivateUsage=false
2
- """PIVOT and UNPIVOT operation mixins.
3
-
4
- Provides mixins for PIVOT and UNPIVOT operations in SELECT statements.
5
- """
6
-
7
- from typing import TYPE_CHECKING, Optional, Union, cast
8
-
9
- from mypy_extensions import trait
10
- from sqlglot import exp
11
-
12
- if TYPE_CHECKING:
13
- from sqlglot.dialects.dialect import DialectType
14
-
15
- from sqlspec.builder._select import Select
16
-
17
- __all__ = ("PivotClauseMixin", "UnpivotClauseMixin")
18
-
19
-
20
- @trait
21
- class PivotClauseMixin:
22
- """Mixin class to add PIVOT functionality to a Select."""
23
-
24
- __slots__ = ()
25
- # Type annotation for PyRight - this will be provided by the base class
26
- _expression: Optional[exp.Expression]
27
-
28
- dialect: "DialectType" = None
29
-
30
- def pivot(
31
- self: "PivotClauseMixin",
32
- aggregate_function: Union[str, exp.Expression],
33
- aggregate_column: Union[str, exp.Expression],
34
- pivot_column: Union[str, exp.Expression],
35
- pivot_values: list[Union[str, int, float, exp.Expression]],
36
- alias: Optional[str] = None,
37
- ) -> "Select":
38
- """Adds a PIVOT clause to the SELECT statement.
39
-
40
- Example:
41
- `query.pivot(aggregate_function="SUM", aggregate_column="Sales", pivot_column="Quarter", pivot_values=["Q1", "Q2", "Q3", "Q4"], alias="PivotTable")`
42
-
43
- Args:
44
- aggregate_function: The aggregate function to use (e.g., "SUM", "AVG").
45
- aggregate_column: The column to be aggregated.
46
- pivot_column: The column whose unique values will become new column headers.
47
- pivot_values: A list of specific values from the pivot_column to be turned into columns.
48
- alias: Optional alias for the pivoted table/subquery.
49
-
50
- Returns:
51
- The SelectBuilder instance for chaining.
52
- """
53
- current_expr = self._expression
54
- if not isinstance(current_expr, exp.Select):
55
- msg = "Pivot can only be applied to a Select expression managed by SelectBuilder."
56
- raise TypeError(msg)
57
-
58
- agg_func_name = aggregate_function if isinstance(aggregate_function, str) else aggregate_function.name
59
- agg_col_expr = exp.column(aggregate_column) if isinstance(aggregate_column, str) else aggregate_column
60
- pivot_col_expr = exp.column(pivot_column) if isinstance(pivot_column, str) else pivot_column
61
-
62
- pivot_agg_expr = exp.func(agg_func_name, agg_col_expr)
63
-
64
- pivot_value_exprs: list[exp.Expression] = []
65
- for val in pivot_values:
66
- if isinstance(val, exp.Expression):
67
- pivot_value_exprs.append(val)
68
- elif isinstance(val, (str, int, float)):
69
- pivot_value_exprs.append(exp.convert(val))
70
- else:
71
- pivot_value_exprs.append(exp.convert(str(val)))
72
-
73
- in_expr = exp.In(this=pivot_col_expr, expressions=pivot_value_exprs)
74
-
75
- pivot_node = exp.Pivot(expressions=[pivot_agg_expr], fields=[in_expr], unpivot=False)
76
-
77
- if alias:
78
- pivot_node.set("alias", exp.TableAlias(this=exp.to_identifier(alias)))
79
-
80
- from_clause = current_expr.args.get("from")
81
- if from_clause and isinstance(from_clause, exp.From):
82
- table = from_clause.this
83
- if isinstance(table, exp.Table):
84
- existing_pivots = table.args.get("pivots", [])
85
- existing_pivots.append(pivot_node)
86
- table.set("pivots", existing_pivots)
87
-
88
- return cast("Select", self)
89
-
90
-
91
- @trait
92
- class UnpivotClauseMixin:
93
- """Mixin class to add UNPIVOT functionality to a Select."""
94
-
95
- __slots__ = ()
96
- # Type annotation for PyRight - this will be provided by the base class
97
- _expression: Optional[exp.Expression]
98
-
99
- dialect: "DialectType" = None
100
-
101
- def unpivot(
102
- self: "UnpivotClauseMixin",
103
- value_column_name: str,
104
- name_column_name: str,
105
- columns_to_unpivot: list[Union[str, exp.Expression]],
106
- alias: Optional[str] = None,
107
- ) -> "Select":
108
- """Adds an UNPIVOT clause to the SELECT statement.
109
-
110
- Example:
111
- `query.unpivot(value_column_name="Sales", name_column_name="Quarter", columns_to_unpivot=["Q1Sales", "Q2Sales"], alias="UnpivotTable")`
112
-
113
- Args:
114
- value_column_name: The name for the new column that will hold the values from the unpivoted columns.
115
- name_column_name: The name for the new column that will hold the names of the original unpivoted columns.
116
- columns_to_unpivot: A list of columns to be unpivoted into rows.
117
- alias: Optional alias for the unpivoted table/subquery.
118
-
119
- Raises:
120
- TypeError: If the current expression is not a Select expression.
121
-
122
- Returns:
123
- The Select instance for chaining.
124
- """
125
- current_expr = self._expression
126
- if not isinstance(current_expr, exp.Select):
127
- msg = "Unpivot can only be applied to a Select expression managed by Select."
128
- raise TypeError(msg)
129
-
130
- value_col_ident = exp.to_identifier(value_column_name)
131
- name_col_ident = exp.to_identifier(name_column_name)
132
-
133
- unpivot_cols_exprs: list[exp.Expression] = []
134
- for col_name_or_expr in columns_to_unpivot:
135
- if isinstance(col_name_or_expr, exp.Expression):
136
- unpivot_cols_exprs.append(col_name_or_expr)
137
- elif isinstance(col_name_or_expr, str):
138
- unpivot_cols_exprs.append(exp.column(col_name_or_expr))
139
- else:
140
- unpivot_cols_exprs.append(exp.column(str(col_name_or_expr)))
141
-
142
- in_expr = exp.In(this=name_col_ident, expressions=unpivot_cols_exprs)
143
-
144
- unpivot_node = exp.Pivot(expressions=[value_col_ident], fields=[in_expr], unpivot=True)
145
-
146
- if alias:
147
- unpivot_node.set("alias", exp.TableAlias(this=exp.to_identifier(alias)))
148
-
149
- from_clause = current_expr.args.get("from")
150
- if from_clause and isinstance(from_clause, exp.From):
151
- table = from_clause.this
152
- if isinstance(table, exp.Table):
153
- existing_pivots = table.args.get("pivots", [])
154
- existing_pivots.append(unpivot_node)
155
- table.set("pivots", existing_pivots)
156
-
157
- return cast("Select", self)