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,145 +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 extract_expression, 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
- _expression: Optional[exp.Expression]
33
-
34
- def order_by(self, *items: Union[str, exp.Ordered, "Column"], desc: bool = False) -> Self:
35
- """Add ORDER BY clause.
36
-
37
- Args:
38
- *items: Columns to order by. Can be strings (column names) or sqlglot.exp.Ordered instances for specific directions (e.g., exp.column("name").desc()).
39
- desc: Whether to order in descending order (applies to all items if they are strings).
40
-
41
- Raises:
42
- SQLBuilderError: If the current expression is not a SELECT statement or if the item type is unsupported.
43
-
44
- Returns:
45
- The current builder instance for method chaining.
46
- """
47
- builder = cast("SQLBuilderProtocol", self)
48
- if not isinstance(builder._expression, exp.Select):
49
- msg = "ORDER BY is only supported for SELECT statements."
50
- raise SQLBuilderError(msg)
51
-
52
- current_expr = builder._expression
53
- for item in items:
54
- if isinstance(item, str):
55
- order_item = parse_order_expression(item)
56
- if desc:
57
- order_item = order_item.desc()
58
- else:
59
- # Extract expression from Column objects or use as-is for sqlglot expressions
60
- extracted_item = extract_expression(item)
61
- order_item = extracted_item
62
- if desc and not isinstance(item, exp.Ordered):
63
- order_item = order_item.desc()
64
- current_expr = current_expr.order_by(order_item, copy=False)
65
- builder._expression = current_expr
66
- return cast("Self", builder)
67
-
68
-
69
- @trait
70
- class LimitOffsetClauseMixin:
71
- """Mixin providing LIMIT and OFFSET clauses."""
72
-
73
- __slots__ = ()
74
-
75
- _expression: Optional[exp.Expression]
76
-
77
- def limit(self, value: int) -> Self:
78
- """Add LIMIT clause.
79
-
80
- Args:
81
- value: The maximum number of rows to return.
82
-
83
- Raises:
84
- SQLBuilderError: If the current expression is not a SELECT statement.
85
-
86
- Returns:
87
- The current builder instance for method chaining.
88
- """
89
- builder = cast("SQLBuilderProtocol", self)
90
- if not isinstance(builder._expression, exp.Select):
91
- msg = "LIMIT is only supported for SELECT statements."
92
- raise SQLBuilderError(msg)
93
- builder._expression = builder._expression.limit(exp.convert(value), copy=False)
94
- return cast("Self", builder)
95
-
96
- def offset(self, value: int) -> Self:
97
- """Add OFFSET clause.
98
-
99
- Args:
100
- value: The number of rows to skip before starting to return rows.
101
-
102
- Raises:
103
- SQLBuilderError: If the current expression is not a SELECT statement.
104
-
105
- Returns:
106
- The current builder instance for method chaining.
107
- """
108
- builder = cast("SQLBuilderProtocol", self)
109
- if not isinstance(builder._expression, exp.Select):
110
- msg = "OFFSET is only supported for SELECT statements."
111
- raise SQLBuilderError(msg)
112
- builder._expression = builder._expression.offset(exp.convert(value), copy=False)
113
- return cast("Self", builder)
114
-
115
-
116
- @trait
117
- class ReturningClauseMixin:
118
- """Mixin providing RETURNING clause."""
119
-
120
- __slots__ = ()
121
- _expression: Optional[exp.Expression]
122
-
123
- def returning(self, *columns: Union[str, exp.Expression, "Column", "ExpressionWrapper", "Case"]) -> Self:
124
- """Add RETURNING clause to the statement.
125
-
126
- Args:
127
- *columns: Columns to return. Can be strings or sqlglot expressions.
128
-
129
- Raises:
130
- SQLBuilderError: If the current expression is not INSERT, UPDATE, or DELETE.
131
-
132
- Returns:
133
- The current builder instance for method chaining.
134
- """
135
- if self._expression is None:
136
- msg = "Cannot add RETURNING: expression is not initialized."
137
- raise SQLBuilderError(msg)
138
- valid_types = (exp.Insert, exp.Update, exp.Delete)
139
- if not isinstance(self._expression, valid_types):
140
- msg = "RETURNING is only supported for INSERT, UPDATE, and DELETE statements."
141
- raise SQLBuilderError(msg)
142
- # Extract expressions from various wrapper types
143
- returning_exprs = [extract_expression(c) for c in columns]
144
- self._expression.set("returning", exp.Returning(expressions=returning_exprs))
145
- 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)