sqlspec 0.13.1__py3-none-any.whl → 0.14.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.
- sqlspec/__init__.py +39 -1
- sqlspec/adapters/adbc/config.py +4 -40
- sqlspec/adapters/adbc/driver.py +29 -16
- sqlspec/adapters/aiosqlite/config.py +2 -20
- sqlspec/adapters/aiosqlite/driver.py +36 -18
- sqlspec/adapters/asyncmy/config.py +2 -33
- sqlspec/adapters/asyncmy/driver.py +23 -16
- sqlspec/adapters/asyncpg/config.py +5 -39
- sqlspec/adapters/asyncpg/driver.py +41 -18
- sqlspec/adapters/bigquery/config.py +2 -43
- sqlspec/adapters/bigquery/driver.py +26 -14
- sqlspec/adapters/duckdb/config.py +2 -49
- sqlspec/adapters/duckdb/driver.py +35 -16
- sqlspec/adapters/oracledb/config.py +4 -83
- sqlspec/adapters/oracledb/driver.py +54 -27
- sqlspec/adapters/psqlpy/config.py +2 -55
- sqlspec/adapters/psqlpy/driver.py +28 -8
- sqlspec/adapters/psycopg/config.py +4 -73
- sqlspec/adapters/psycopg/driver.py +69 -24
- sqlspec/adapters/sqlite/config.py +3 -21
- sqlspec/adapters/sqlite/driver.py +50 -26
- sqlspec/cli.py +248 -0
- sqlspec/config.py +18 -20
- sqlspec/driver/_async.py +28 -10
- sqlspec/driver/_common.py +5 -4
- sqlspec/driver/_sync.py +28 -10
- sqlspec/driver/mixins/__init__.py +6 -0
- sqlspec/driver/mixins/_cache.py +114 -0
- sqlspec/driver/mixins/_pipeline.py +0 -4
- sqlspec/{service/base.py → driver/mixins/_query_tools.py} +86 -421
- sqlspec/driver/mixins/_result_utils.py +0 -2
- sqlspec/driver/mixins/_sql_translator.py +0 -2
- sqlspec/driver/mixins/_storage.py +4 -18
- sqlspec/driver/mixins/_type_coercion.py +0 -2
- sqlspec/driver/parameters.py +4 -4
- sqlspec/extensions/aiosql/adapter.py +4 -4
- sqlspec/extensions/litestar/__init__.py +2 -1
- sqlspec/extensions/litestar/cli.py +48 -0
- sqlspec/extensions/litestar/plugin.py +3 -0
- sqlspec/loader.py +1 -1
- sqlspec/migrations/__init__.py +23 -0
- sqlspec/migrations/base.py +390 -0
- sqlspec/migrations/commands.py +525 -0
- sqlspec/migrations/runner.py +215 -0
- sqlspec/migrations/tracker.py +153 -0
- sqlspec/migrations/utils.py +89 -0
- sqlspec/protocols.py +37 -3
- sqlspec/statement/builder/__init__.py +8 -8
- sqlspec/statement/builder/{column.py → _column.py} +82 -52
- sqlspec/statement/builder/{ddl.py → _ddl.py} +5 -5
- sqlspec/statement/builder/_ddl_utils.py +1 -1
- sqlspec/statement/builder/{delete.py → _delete.py} +1 -1
- sqlspec/statement/builder/{insert.py → _insert.py} +1 -1
- sqlspec/statement/builder/{merge.py → _merge.py} +1 -1
- sqlspec/statement/builder/_parsing_utils.py +5 -3
- sqlspec/statement/builder/{select.py → _select.py} +59 -61
- sqlspec/statement/builder/{update.py → _update.py} +2 -2
- sqlspec/statement/builder/mixins/__init__.py +24 -30
- sqlspec/statement/builder/mixins/{_set_ops.py → _cte_and_set_ops.py} +86 -2
- sqlspec/statement/builder/mixins/{_delete_from.py → _delete_operations.py} +2 -0
- sqlspec/statement/builder/mixins/{_insert_values.py → _insert_operations.py} +70 -1
- sqlspec/statement/builder/mixins/{_merge_clauses.py → _merge_operations.py} +2 -0
- sqlspec/statement/builder/mixins/_order_limit_operations.py +123 -0
- sqlspec/statement/builder/mixins/{_pivot.py → _pivot_operations.py} +71 -2
- sqlspec/statement/builder/mixins/_select_operations.py +612 -0
- sqlspec/statement/builder/mixins/{_update_set.py → _update_operations.py} +73 -2
- sqlspec/statement/builder/mixins/_where_clause.py +536 -0
- sqlspec/statement/cache.py +50 -0
- sqlspec/statement/filters.py +37 -8
- sqlspec/statement/parameters.py +154 -25
- sqlspec/statement/pipelines/__init__.py +1 -1
- sqlspec/statement/pipelines/context.py +4 -4
- sqlspec/statement/pipelines/transformers/_expression_simplifier.py +3 -3
- sqlspec/statement/pipelines/validators/_parameter_style.py +22 -22
- sqlspec/statement/pipelines/validators/_performance.py +1 -5
- sqlspec/statement/sql.py +246 -176
- sqlspec/utils/__init__.py +2 -1
- sqlspec/utils/statement_hashing.py +203 -0
- sqlspec/utils/type_guards.py +32 -0
- {sqlspec-0.13.1.dist-info → sqlspec-0.14.0.dist-info}/METADATA +1 -1
- sqlspec-0.14.0.dist-info/RECORD +143 -0
- sqlspec-0.14.0.dist-info/entry_points.txt +2 -0
- sqlspec/service/__init__.py +0 -4
- sqlspec/service/_util.py +0 -147
- sqlspec/service/pagination.py +0 -26
- sqlspec/statement/builder/mixins/_aggregate_functions.py +0 -250
- sqlspec/statement/builder/mixins/_case_builder.py +0 -91
- sqlspec/statement/builder/mixins/_common_table_expr.py +0 -90
- sqlspec/statement/builder/mixins/_from.py +0 -63
- sqlspec/statement/builder/mixins/_group_by.py +0 -118
- sqlspec/statement/builder/mixins/_having.py +0 -35
- sqlspec/statement/builder/mixins/_insert_from_select.py +0 -47
- sqlspec/statement/builder/mixins/_insert_into.py +0 -36
- sqlspec/statement/builder/mixins/_limit_offset.py +0 -53
- sqlspec/statement/builder/mixins/_order_by.py +0 -46
- sqlspec/statement/builder/mixins/_returning.py +0 -37
- sqlspec/statement/builder/mixins/_select_columns.py +0 -61
- sqlspec/statement/builder/mixins/_unpivot.py +0 -77
- sqlspec/statement/builder/mixins/_update_from.py +0 -55
- sqlspec/statement/builder/mixins/_update_table.py +0 -29
- sqlspec/statement/builder/mixins/_where.py +0 -401
- sqlspec/statement/builder/mixins/_window_functions.py +0 -86
- sqlspec/statement/parameter_manager.py +0 -220
- sqlspec/statement/sql_compiler.py +0 -140
- sqlspec-0.13.1.dist-info/RECORD +0 -150
- /sqlspec/statement/builder/{base.py → _base.py} +0 -0
- /sqlspec/statement/builder/mixins/{_join.py → _join_operations.py} +0 -0
- {sqlspec-0.13.1.dist-info → sqlspec-0.14.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.13.1.dist-info → sqlspec-0.14.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.13.1.dist-info → sqlspec-0.14.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
from typing import Any, Optional
|
|
2
|
-
|
|
3
|
-
from sqlglot import exp
|
|
4
|
-
from typing_extensions import Self
|
|
5
|
-
|
|
6
|
-
from sqlspec.exceptions import SQLBuilderError
|
|
7
|
-
|
|
8
|
-
__all__ = ("InsertFromSelectMixin",)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class InsertFromSelectMixin:
|
|
12
|
-
"""Mixin providing INSERT ... SELECT support for INSERT builders."""
|
|
13
|
-
|
|
14
|
-
_expression: Optional[exp.Expression] = None
|
|
15
|
-
|
|
16
|
-
def from_select(self, select_builder: Any) -> Self:
|
|
17
|
-
"""Sets the INSERT source to a SELECT statement.
|
|
18
|
-
|
|
19
|
-
Args:
|
|
20
|
-
select_builder: A SelectBuilder instance representing the SELECT query.
|
|
21
|
-
|
|
22
|
-
Returns:
|
|
23
|
-
The current builder instance for method chaining.
|
|
24
|
-
|
|
25
|
-
Raises:
|
|
26
|
-
SQLBuilderError: If the table is not set or the select_builder is invalid.
|
|
27
|
-
"""
|
|
28
|
-
if not getattr(self, "_table", None):
|
|
29
|
-
msg = "The target table must be set using .into() before adding values."
|
|
30
|
-
raise SQLBuilderError(msg)
|
|
31
|
-
if self._expression is None:
|
|
32
|
-
self._expression = exp.Insert()
|
|
33
|
-
if not isinstance(self._expression, exp.Insert):
|
|
34
|
-
msg = "Cannot set INSERT source on a non-INSERT expression."
|
|
35
|
-
raise SQLBuilderError(msg)
|
|
36
|
-
# Merge parameters from the SELECT builder
|
|
37
|
-
subquery_params = getattr(select_builder, "_parameters", None)
|
|
38
|
-
if subquery_params:
|
|
39
|
-
for p_name, p_value in subquery_params.items():
|
|
40
|
-
self.add_parameter(p_value, name=p_name) # type: ignore[attr-defined]
|
|
41
|
-
select_expr = getattr(select_builder, "_expression", None)
|
|
42
|
-
if select_expr and isinstance(select_expr, exp.Select):
|
|
43
|
-
self._expression.set("expression", select_expr.copy())
|
|
44
|
-
else:
|
|
45
|
-
msg = "SelectBuilder must have a valid SELECT expression."
|
|
46
|
-
raise SQLBuilderError(msg)
|
|
47
|
-
return self
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
|
-
from sqlglot import exp
|
|
4
|
-
from typing_extensions import Self
|
|
5
|
-
|
|
6
|
-
from sqlspec.exceptions import SQLBuilderError
|
|
7
|
-
|
|
8
|
-
__all__ = ("InsertIntoClauseMixin",)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class InsertIntoClauseMixin:
|
|
12
|
-
"""Mixin providing INTO clause for INSERT builders."""
|
|
13
|
-
|
|
14
|
-
_expression: Optional[exp.Expression] = None
|
|
15
|
-
|
|
16
|
-
def into(self, table: str) -> Self:
|
|
17
|
-
"""Set the target table for the INSERT statement.
|
|
18
|
-
|
|
19
|
-
Args:
|
|
20
|
-
table: The name of the table to insert data into.
|
|
21
|
-
|
|
22
|
-
Raises:
|
|
23
|
-
SQLBuilderError: If the current expression is not an INSERT statement.
|
|
24
|
-
|
|
25
|
-
Returns:
|
|
26
|
-
The current builder instance for method chaining.
|
|
27
|
-
"""
|
|
28
|
-
if self._expression is None:
|
|
29
|
-
self._expression = exp.Insert()
|
|
30
|
-
if not isinstance(self._expression, exp.Insert):
|
|
31
|
-
msg = "Cannot set target table on a non-INSERT expression."
|
|
32
|
-
raise SQLBuilderError(msg)
|
|
33
|
-
|
|
34
|
-
setattr(self, "_table", table)
|
|
35
|
-
self._expression.set("this", exp.to_table(table))
|
|
36
|
-
return self
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, cast
|
|
2
|
-
|
|
3
|
-
from sqlglot import exp
|
|
4
|
-
from typing_extensions import Self
|
|
5
|
-
|
|
6
|
-
if TYPE_CHECKING:
|
|
7
|
-
from sqlspec.protocols import SQLBuilderProtocol
|
|
8
|
-
|
|
9
|
-
from sqlspec.exceptions import SQLBuilderError
|
|
10
|
-
|
|
11
|
-
__all__ = ("LimitOffsetClauseMixin",)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class LimitOffsetClauseMixin:
|
|
15
|
-
"""Mixin providing LIMIT and OFFSET clauses for SELECT builders."""
|
|
16
|
-
|
|
17
|
-
def limit(self, value: int) -> Self:
|
|
18
|
-
"""Add LIMIT clause.
|
|
19
|
-
|
|
20
|
-
Args:
|
|
21
|
-
value: The maximum number of rows to return.
|
|
22
|
-
|
|
23
|
-
Raises:
|
|
24
|
-
SQLBuilderError: If the current expression is not a SELECT statement.
|
|
25
|
-
|
|
26
|
-
Returns:
|
|
27
|
-
The current builder instance for method chaining.
|
|
28
|
-
"""
|
|
29
|
-
builder = cast("SQLBuilderProtocol", self)
|
|
30
|
-
if not isinstance(builder._expression, exp.Select):
|
|
31
|
-
msg = "LIMIT is only supported for SELECT statements."
|
|
32
|
-
raise SQLBuilderError(msg)
|
|
33
|
-
builder._expression = builder._expression.limit(exp.Literal.number(value), copy=False)
|
|
34
|
-
return cast("Self", builder)
|
|
35
|
-
|
|
36
|
-
def offset(self, value: int) -> Self:
|
|
37
|
-
"""Add OFFSET clause.
|
|
38
|
-
|
|
39
|
-
Args:
|
|
40
|
-
value: The number of rows to skip before starting to return rows.
|
|
41
|
-
|
|
42
|
-
Raises:
|
|
43
|
-
SQLBuilderError: If the current expression is not a SELECT statement.
|
|
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 = "OFFSET is only supported for SELECT statements."
|
|
51
|
-
raise SQLBuilderError(msg)
|
|
52
|
-
builder._expression = builder._expression.offset(exp.Literal.number(value), copy=False)
|
|
53
|
-
return cast("Self", builder)
|
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, Union, cast
|
|
2
|
-
|
|
3
|
-
from sqlglot import exp
|
|
4
|
-
from typing_extensions import Self
|
|
5
|
-
|
|
6
|
-
from sqlspec.exceptions import SQLBuilderError
|
|
7
|
-
from sqlspec.statement.builder._parsing_utils import parse_order_expression
|
|
8
|
-
|
|
9
|
-
if TYPE_CHECKING:
|
|
10
|
-
from sqlspec.protocols import SQLBuilderProtocol
|
|
11
|
-
|
|
12
|
-
__all__ = ("OrderByClauseMixin",)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
class OrderByClauseMixin:
|
|
16
|
-
"""Mixin providing ORDER BY clause for SELECT builders."""
|
|
17
|
-
|
|
18
|
-
def order_by(self, *items: Union[str, exp.Ordered], desc: bool = False) -> Self:
|
|
19
|
-
"""Add ORDER BY clause.
|
|
20
|
-
|
|
21
|
-
Args:
|
|
22
|
-
*items: Columns to order by. Can be strings (column names) or sqlglot.exp.Ordered instances for specific directions (e.g., exp.column("name").desc()).
|
|
23
|
-
desc: Whether to order in descending order (applies to all items if they are strings).
|
|
24
|
-
|
|
25
|
-
Raises:
|
|
26
|
-
SQLBuilderError: If the current expression is not a SELECT statement or if the item type is unsupported.
|
|
27
|
-
|
|
28
|
-
Returns:
|
|
29
|
-
The current builder instance for method chaining.
|
|
30
|
-
"""
|
|
31
|
-
builder = cast("SQLBuilderProtocol", self)
|
|
32
|
-
if not isinstance(builder._expression, exp.Select):
|
|
33
|
-
msg = "ORDER BY is only supported for SELECT statements."
|
|
34
|
-
raise SQLBuilderError(msg)
|
|
35
|
-
|
|
36
|
-
current_expr = builder._expression
|
|
37
|
-
for item in items:
|
|
38
|
-
if isinstance(item, str):
|
|
39
|
-
order_item = parse_order_expression(item)
|
|
40
|
-
if desc:
|
|
41
|
-
order_item = order_item.desc()
|
|
42
|
-
else:
|
|
43
|
-
order_item = item
|
|
44
|
-
current_expr = current_expr.order_by(order_item, copy=False)
|
|
45
|
-
builder._expression = current_expr
|
|
46
|
-
return cast("Self", builder)
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
from typing import Optional, Union
|
|
2
|
-
|
|
3
|
-
from sqlglot import exp
|
|
4
|
-
from typing_extensions import Self
|
|
5
|
-
|
|
6
|
-
from sqlspec.exceptions import SQLBuilderError
|
|
7
|
-
|
|
8
|
-
__all__ = ("ReturningClauseMixin",)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class ReturningClauseMixin:
|
|
12
|
-
"""Mixin providing RETURNING clause for INSERT, UPDATE, and DELETE builders."""
|
|
13
|
-
|
|
14
|
-
_expression: Optional[exp.Expression] = None
|
|
15
|
-
|
|
16
|
-
def returning(self, *columns: Union[str, exp.Expression]) -> Self:
|
|
17
|
-
"""Add RETURNING clause to the statement.
|
|
18
|
-
|
|
19
|
-
Args:
|
|
20
|
-
*columns: Columns to return. Can be strings or sqlglot expressions.
|
|
21
|
-
|
|
22
|
-
Raises:
|
|
23
|
-
SQLBuilderError: If the current expression is not INSERT, UPDATE, or DELETE.
|
|
24
|
-
|
|
25
|
-
Returns:
|
|
26
|
-
The current builder instance for method chaining.
|
|
27
|
-
"""
|
|
28
|
-
if self._expression is None:
|
|
29
|
-
msg = "Cannot add RETURNING: expression is not initialized."
|
|
30
|
-
raise SQLBuilderError(msg)
|
|
31
|
-
valid_types = (exp.Insert, exp.Update, exp.Delete)
|
|
32
|
-
if not isinstance(self._expression, valid_types):
|
|
33
|
-
msg = "RETURNING is only supported for INSERT, UPDATE, and DELETE statements."
|
|
34
|
-
raise SQLBuilderError(msg)
|
|
35
|
-
returning_exprs = [exp.column(c) if isinstance(c, str) else c for c in columns]
|
|
36
|
-
self._expression.set("returning", exp.Returning(expressions=returning_exprs))
|
|
37
|
-
return self
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, Union, cast
|
|
2
|
-
|
|
3
|
-
from sqlglot import exp
|
|
4
|
-
from typing_extensions import Self
|
|
5
|
-
|
|
6
|
-
from sqlspec.exceptions import SQLBuilderError
|
|
7
|
-
from sqlspec.statement.builder._parsing_utils import parse_column_expression
|
|
8
|
-
|
|
9
|
-
if TYPE_CHECKING:
|
|
10
|
-
from sqlspec.protocols import SQLBuilderProtocol
|
|
11
|
-
from sqlspec.statement.builder.column import Column, FunctionColumn
|
|
12
|
-
|
|
13
|
-
__all__ = ("SelectColumnsMixin",)
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class SelectColumnsMixin:
|
|
17
|
-
"""Mixin providing SELECT column and DISTINCT clauses for SELECT builders."""
|
|
18
|
-
|
|
19
|
-
def select(self, *columns: Union[str, exp.Expression, "Column", "FunctionColumn"]) -> Self:
|
|
20
|
-
"""Add columns to SELECT clause.
|
|
21
|
-
|
|
22
|
-
Raises:
|
|
23
|
-
SQLBuilderError: If the current expression is not a SELECT statement.
|
|
24
|
-
|
|
25
|
-
Returns:
|
|
26
|
-
The current builder instance for method chaining.
|
|
27
|
-
"""
|
|
28
|
-
builder = cast("SQLBuilderProtocol", self)
|
|
29
|
-
if builder._expression is None:
|
|
30
|
-
builder._expression = exp.Select()
|
|
31
|
-
if not isinstance(builder._expression, exp.Select):
|
|
32
|
-
msg = "Cannot add select columns to a non-SELECT expression."
|
|
33
|
-
raise SQLBuilderError(msg)
|
|
34
|
-
for column in columns:
|
|
35
|
-
builder._expression = builder._expression.select(parse_column_expression(column), copy=False)
|
|
36
|
-
return cast("Self", builder)
|
|
37
|
-
|
|
38
|
-
def distinct(self, *columns: Union[str, exp.Expression, "Column", "FunctionColumn"]) -> Self:
|
|
39
|
-
"""Add DISTINCT clause to SELECT.
|
|
40
|
-
|
|
41
|
-
Args:
|
|
42
|
-
*columns: Optional columns to make distinct. If none provided, applies DISTINCT to all selected columns.
|
|
43
|
-
|
|
44
|
-
Raises:
|
|
45
|
-
SQLBuilderError: If the current expression is not a SELECT statement.
|
|
46
|
-
|
|
47
|
-
Returns:
|
|
48
|
-
The current builder instance for method chaining.
|
|
49
|
-
"""
|
|
50
|
-
builder = cast("SQLBuilderProtocol", self)
|
|
51
|
-
if builder._expression is None:
|
|
52
|
-
builder._expression = exp.Select()
|
|
53
|
-
if not isinstance(builder._expression, exp.Select):
|
|
54
|
-
msg = "Cannot add DISTINCT to a non-SELECT expression."
|
|
55
|
-
raise SQLBuilderError(msg)
|
|
56
|
-
if not columns:
|
|
57
|
-
builder._expression.set("distinct", exp.Distinct())
|
|
58
|
-
else:
|
|
59
|
-
distinct_columns = [parse_column_expression(column) for column in columns]
|
|
60
|
-
builder._expression.set("distinct", exp.Distinct(expressions=distinct_columns))
|
|
61
|
-
return cast("Self", builder)
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, Optional, Union, cast
|
|
2
|
-
|
|
3
|
-
from sqlglot import exp
|
|
4
|
-
|
|
5
|
-
if TYPE_CHECKING:
|
|
6
|
-
from sqlglot.dialects.dialect import DialectType
|
|
7
|
-
|
|
8
|
-
from sqlspec.statement.builder.select import Select
|
|
9
|
-
|
|
10
|
-
__all__ = ("UnpivotClauseMixin",)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
class UnpivotClauseMixin:
|
|
14
|
-
"""Mixin class to add UNPIVOT functionality to a Select."""
|
|
15
|
-
|
|
16
|
-
_expression: "Optional[exp.Expression]" = None
|
|
17
|
-
dialect: "DialectType" = None
|
|
18
|
-
|
|
19
|
-
def unpivot(
|
|
20
|
-
self: "UnpivotClauseMixin",
|
|
21
|
-
value_column_name: str,
|
|
22
|
-
name_column_name: str,
|
|
23
|
-
columns_to_unpivot: list[Union[str, exp.Expression]],
|
|
24
|
-
alias: Optional[str] = None,
|
|
25
|
-
) -> "Select":
|
|
26
|
-
"""Adds an UNPIVOT clause to the SELECT statement.
|
|
27
|
-
|
|
28
|
-
Example:
|
|
29
|
-
`query.unpivot(value_column_name="Sales", name_column_name="Quarter", columns_to_unpivot=["Q1Sales", "Q2Sales"], alias="UnpivotTable")`
|
|
30
|
-
|
|
31
|
-
Args:
|
|
32
|
-
value_column_name: The name for the new column that will hold the values from the unpivoted columns.
|
|
33
|
-
name_column_name: The name for the new column that will hold the names of the original unpivoted columns.
|
|
34
|
-
columns_to_unpivot: A list of columns to be unpivoted into rows.
|
|
35
|
-
alias: Optional alias for the unpivoted table/subquery.
|
|
36
|
-
|
|
37
|
-
Raises:
|
|
38
|
-
TypeError: If the current expression is not a Select expression.
|
|
39
|
-
|
|
40
|
-
Returns:
|
|
41
|
-
The Select instance for chaining.
|
|
42
|
-
"""
|
|
43
|
-
current_expr = self._expression
|
|
44
|
-
if not isinstance(current_expr, exp.Select):
|
|
45
|
-
# SelectBuilder's __init__ ensures _expression is exp.Select.
|
|
46
|
-
msg = "Unpivot can only be applied to a Select expression managed by Select."
|
|
47
|
-
raise TypeError(msg)
|
|
48
|
-
|
|
49
|
-
value_col_ident = exp.to_identifier(value_column_name)
|
|
50
|
-
name_col_ident = exp.to_identifier(name_column_name)
|
|
51
|
-
|
|
52
|
-
unpivot_cols_exprs: list[exp.Expression] = []
|
|
53
|
-
for col_name_or_expr in columns_to_unpivot:
|
|
54
|
-
if isinstance(col_name_or_expr, exp.Expression):
|
|
55
|
-
unpivot_cols_exprs.append(col_name_or_expr)
|
|
56
|
-
elif isinstance(col_name_or_expr, str):
|
|
57
|
-
unpivot_cols_exprs.append(exp.column(col_name_or_expr))
|
|
58
|
-
else:
|
|
59
|
-
# Fallback for other types, should ideally be an error or more specific handling
|
|
60
|
-
unpivot_cols_exprs.append(exp.column(str(col_name_or_expr)))
|
|
61
|
-
|
|
62
|
-
in_expr = exp.In(this=name_col_ident, expressions=unpivot_cols_exprs)
|
|
63
|
-
|
|
64
|
-
unpivot_node = exp.Pivot(expressions=[value_col_ident], fields=[in_expr], unpivot=True)
|
|
65
|
-
|
|
66
|
-
if alias:
|
|
67
|
-
unpivot_node.set("alias", exp.TableAlias(this=exp.to_identifier(alias)))
|
|
68
|
-
|
|
69
|
-
from_clause = current_expr.args.get("from")
|
|
70
|
-
if from_clause and isinstance(from_clause, exp.From):
|
|
71
|
-
table = from_clause.this
|
|
72
|
-
if isinstance(table, exp.Table):
|
|
73
|
-
existing_pivots = table.args.get("pivots", [])
|
|
74
|
-
existing_pivots.append(unpivot_node)
|
|
75
|
-
table.set("pivots", existing_pivots)
|
|
76
|
-
|
|
77
|
-
return cast("Select", self)
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
from typing import Any, Optional, Union
|
|
2
|
-
|
|
3
|
-
from sqlglot import exp
|
|
4
|
-
from typing_extensions import Self
|
|
5
|
-
|
|
6
|
-
from sqlspec.exceptions import SQLBuilderError
|
|
7
|
-
from sqlspec.utils.type_guards import has_query_builder_parameters
|
|
8
|
-
|
|
9
|
-
__all__ = ("UpdateFromClauseMixin",)
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class UpdateFromClauseMixin:
|
|
13
|
-
"""Mixin providing FROM clause for UPDATE builders (e.g., PostgreSQL style)."""
|
|
14
|
-
|
|
15
|
-
def from_(self, table: Union[str, exp.Expression, Any], alias: Optional[str] = None) -> Self:
|
|
16
|
-
"""Add a FROM clause to the UPDATE statement.
|
|
17
|
-
|
|
18
|
-
Args:
|
|
19
|
-
table: The table name, expression, or subquery to add to the FROM clause.
|
|
20
|
-
alias: Optional alias for the table in the FROM clause.
|
|
21
|
-
|
|
22
|
-
Returns:
|
|
23
|
-
The current builder instance for method chaining.
|
|
24
|
-
|
|
25
|
-
Raises:
|
|
26
|
-
SQLBuilderError: If the current expression is not an UPDATE statement.
|
|
27
|
-
"""
|
|
28
|
-
if self._expression is None or not isinstance(self._expression, exp.Update): # type: ignore[attr-defined]
|
|
29
|
-
msg = "Cannot add FROM clause to non-UPDATE expression. Set the main table first."
|
|
30
|
-
raise SQLBuilderError(msg)
|
|
31
|
-
table_expr: exp.Expression
|
|
32
|
-
if isinstance(table, str):
|
|
33
|
-
table_expr = exp.to_table(table, alias=alias)
|
|
34
|
-
elif has_query_builder_parameters(table):
|
|
35
|
-
subquery_builder_params = getattr(table, "_parameters", None)
|
|
36
|
-
if subquery_builder_params:
|
|
37
|
-
for p_name, p_value in subquery_builder_params.items():
|
|
38
|
-
self.add_parameter(p_value, name=p_name) # type: ignore[attr-defined]
|
|
39
|
-
subquery_exp = exp.paren(getattr(table, "_expression", exp.select()))
|
|
40
|
-
table_expr = exp.alias_(subquery_exp, alias) if alias else subquery_exp
|
|
41
|
-
elif isinstance(table, exp.Expression):
|
|
42
|
-
table_expr = exp.alias_(table, alias) if alias else table
|
|
43
|
-
else:
|
|
44
|
-
msg = f"Unsupported table type for FROM clause: {type(table)}"
|
|
45
|
-
raise SQLBuilderError(msg)
|
|
46
|
-
if self._expression.args.get("from") is None: # type: ignore[attr-defined]
|
|
47
|
-
self._expression.set("from", exp.From(expressions=[])) # type: ignore[attr-defined]
|
|
48
|
-
from_clause = self._expression.args["from"] # type: ignore[attr-defined]
|
|
49
|
-
if hasattr(from_clause, "append"):
|
|
50
|
-
from_clause.append("expressions", table_expr)
|
|
51
|
-
else:
|
|
52
|
-
if not from_clause.expressions:
|
|
53
|
-
from_clause.expressions = []
|
|
54
|
-
from_clause.expressions.append(table_expr)
|
|
55
|
-
return self
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
from typing import Optional
|
|
2
|
-
|
|
3
|
-
from sqlglot import exp
|
|
4
|
-
from typing_extensions import Self
|
|
5
|
-
|
|
6
|
-
__all__ = ("UpdateTableClauseMixin",)
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
class UpdateTableClauseMixin:
|
|
10
|
-
"""Mixin providing TABLE clause for UPDATE builders."""
|
|
11
|
-
|
|
12
|
-
_expression: Optional[exp.Expression] = None
|
|
13
|
-
|
|
14
|
-
def table(self, table_name: str, alias: Optional[str] = None) -> Self:
|
|
15
|
-
"""Set the table to update.
|
|
16
|
-
|
|
17
|
-
Args:
|
|
18
|
-
table_name: The name of the table.
|
|
19
|
-
alias: Optional alias for the table.
|
|
20
|
-
|
|
21
|
-
Returns:
|
|
22
|
-
The current builder instance for method chaining.
|
|
23
|
-
"""
|
|
24
|
-
if self._expression is None or not isinstance(self._expression, exp.Update):
|
|
25
|
-
self._expression = exp.Update(this=None, expressions=[], joins=[])
|
|
26
|
-
table_expr: exp.Expression = exp.to_table(table_name, alias=alias)
|
|
27
|
-
self._expression.set("this", table_expr)
|
|
28
|
-
setattr(self, "_table", table_name)
|
|
29
|
-
return self
|