sqlspec 0.18.0__py3-none-any.whl → 0.20.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/adapters/adbc/driver.py +192 -28
- sqlspec/adapters/asyncmy/driver.py +72 -15
- sqlspec/adapters/asyncpg/config.py +23 -3
- sqlspec/adapters/asyncpg/driver.py +30 -14
- sqlspec/adapters/bigquery/driver.py +79 -9
- sqlspec/adapters/duckdb/driver.py +39 -56
- sqlspec/adapters/oracledb/driver.py +99 -52
- sqlspec/adapters/psqlpy/driver.py +89 -31
- sqlspec/adapters/psycopg/driver.py +11 -23
- sqlspec/adapters/sqlite/driver.py +77 -8
- sqlspec/base.py +29 -25
- sqlspec/builder/__init__.py +1 -1
- sqlspec/builder/_base.py +4 -5
- sqlspec/builder/_column.py +3 -3
- sqlspec/builder/_ddl.py +5 -1
- sqlspec/builder/_delete.py +5 -6
- sqlspec/builder/_insert.py +6 -7
- sqlspec/builder/_merge.py +5 -5
- sqlspec/builder/_parsing_utils.py +3 -3
- sqlspec/builder/_select.py +6 -5
- sqlspec/builder/_update.py +4 -5
- sqlspec/builder/mixins/_cte_and_set_ops.py +5 -1
- sqlspec/builder/mixins/_delete_operations.py +5 -1
- sqlspec/builder/mixins/_insert_operations.py +5 -1
- sqlspec/builder/mixins/_join_operations.py +5 -0
- sqlspec/builder/mixins/_merge_operations.py +5 -1
- sqlspec/builder/mixins/_order_limit_operations.py +5 -1
- sqlspec/builder/mixins/_pivot_operations.py +4 -1
- sqlspec/builder/mixins/_select_operations.py +5 -1
- sqlspec/builder/mixins/_update_operations.py +5 -1
- sqlspec/builder/mixins/_where_clause.py +5 -1
- sqlspec/cli.py +281 -33
- sqlspec/config.py +160 -10
- sqlspec/core/compiler.py +11 -3
- sqlspec/core/filters.py +30 -9
- sqlspec/core/parameters.py +67 -67
- sqlspec/core/result.py +62 -31
- sqlspec/core/splitter.py +160 -34
- sqlspec/core/statement.py +95 -14
- sqlspec/driver/_common.py +12 -3
- sqlspec/driver/mixins/_result_tools.py +21 -4
- sqlspec/driver/mixins/_sql_translator.py +45 -7
- sqlspec/extensions/aiosql/adapter.py +1 -1
- sqlspec/extensions/litestar/_utils.py +1 -1
- sqlspec/extensions/litestar/handlers.py +21 -0
- sqlspec/extensions/litestar/plugin.py +15 -8
- sqlspec/loader.py +12 -12
- sqlspec/migrations/loaders.py +5 -2
- sqlspec/migrations/utils.py +2 -2
- sqlspec/storage/backends/obstore.py +1 -3
- sqlspec/storage/registry.py +1 -1
- sqlspec/utils/__init__.py +7 -0
- sqlspec/utils/deprecation.py +6 -0
- sqlspec/utils/fixtures.py +239 -30
- sqlspec/utils/module_loader.py +5 -1
- sqlspec/utils/serializers.py +6 -0
- sqlspec/utils/singleton.py +6 -0
- sqlspec/utils/sync_tools.py +10 -1
- {sqlspec-0.18.0.dist-info → sqlspec-0.20.0.dist-info}/METADATA +1 -1
- {sqlspec-0.18.0.dist-info → sqlspec-0.20.0.dist-info}/RECORD +64 -64
- {sqlspec-0.18.0.dist-info → sqlspec-0.20.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.18.0.dist-info → sqlspec-0.20.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.18.0.dist-info → sqlspec-0.20.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.18.0.dist-info → sqlspec-0.20.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/builder/_insert.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""INSERT statement builder.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Provides a fluent interface for building SQL INSERT queries with
|
|
4
|
+
parameter binding and validation.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from typing import TYPE_CHECKING, Any, Final, Optional
|
|
@@ -31,8 +31,7 @@ ERR_MSG_EXPRESSION_NOT_INITIALIZED: Final[str] = "Internal error: base expressio
|
|
|
31
31
|
class Insert(QueryBuilder, ReturningClauseMixin, InsertValuesMixin, InsertFromSelectMixin, InsertIntoClauseMixin):
|
|
32
32
|
"""Builder for INSERT statements.
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
in a safe and dialect-agnostic manner with automatic parameter binding.
|
|
34
|
+
Constructs SQL INSERT queries with parameter binding and validation.
|
|
36
35
|
"""
|
|
37
36
|
|
|
38
37
|
__slots__ = ("_columns", "_table", "_values_added_count")
|
|
@@ -316,8 +315,8 @@ class Insert(QueryBuilder, ReturningClauseMixin, InsertValuesMixin, InsertFromSe
|
|
|
316
315
|
class ConflictBuilder:
|
|
317
316
|
"""Builder for ON CONFLICT clauses in INSERT statements.
|
|
318
317
|
|
|
319
|
-
|
|
320
|
-
|
|
318
|
+
Constructs conflict resolution clauses using PostgreSQL-style syntax,
|
|
319
|
+
which SQLGlot can transpile to other dialects.
|
|
321
320
|
"""
|
|
322
321
|
|
|
323
322
|
__slots__ = ("_columns", "_insert_builder")
|
sqlspec/builder/_merge.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""MERGE statement builder.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Provides a fluent interface for building SQL MERGE queries with
|
|
4
|
+
parameter binding and validation.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from typing import Any, Optional
|
|
@@ -33,8 +33,8 @@ class Merge(
|
|
|
33
33
|
):
|
|
34
34
|
"""Builder for MERGE statements.
|
|
35
35
|
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
Constructs SQL MERGE statements (also known as UPSERT in some databases)
|
|
37
|
+
with parameter binding and validation.
|
|
38
38
|
"""
|
|
39
39
|
|
|
40
40
|
__slots__ = ()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Parsing utilities for SQL builders.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Provides common parsing functions to handle SQL expressions
|
|
4
|
+
passed as strings to builder methods.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import contextlib
|
sqlspec/builder/_select.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""SELECT statement builder.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Provides a fluent interface for building SQL SELECT queries with
|
|
4
|
+
parameter binding and validation.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
import re
|
|
@@ -44,9 +44,10 @@ class Select(
|
|
|
44
44
|
PivotClauseMixin,
|
|
45
45
|
UnpivotClauseMixin,
|
|
46
46
|
):
|
|
47
|
-
"""
|
|
47
|
+
"""Builder for SELECT queries.
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
Provides a fluent interface for constructing SQL SELECT statements
|
|
50
|
+
with parameter binding and validation.
|
|
50
51
|
|
|
51
52
|
Example:
|
|
52
53
|
>>> class User(BaseModel):
|
sqlspec/builder/_update.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""UPDATE statement builder.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Provides a fluent interface for building SQL UPDATE queries with
|
|
4
|
+
parameter binding and validation.
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
7
|
from typing import TYPE_CHECKING, Any, Optional, Union
|
|
@@ -36,8 +36,7 @@ class Update(
|
|
|
36
36
|
):
|
|
37
37
|
"""Builder for UPDATE statements.
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
with automatic parameter binding and validation.
|
|
39
|
+
Constructs SQL UPDATE statements with parameter binding and validation.
|
|
41
40
|
|
|
42
41
|
Example:
|
|
43
42
|
```python
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"""CTE
|
|
1
|
+
"""CTE and set operation mixins.
|
|
2
|
+
|
|
3
|
+
Provides mixins for Common Table Expressions (WITH clause) and
|
|
4
|
+
set operations (UNION, INTERSECT, EXCEPT).
|
|
5
|
+
"""
|
|
2
6
|
|
|
3
7
|
from typing import Any, Optional, Union
|
|
4
8
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""INSERT operation mixins.
|
|
2
|
+
|
|
3
|
+
Provides mixins for INSERT statement functionality including
|
|
4
|
+
INTO clauses, VALUES clauses, and INSERT FROM SELECT operations.
|
|
5
|
+
"""
|
|
2
6
|
|
|
3
7
|
from collections.abc import Sequence
|
|
4
8
|
from typing import Any, Optional, TypeVar, Union
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""ORDER BY, LIMIT, OFFSET, and RETURNING clause mixins.
|
|
2
|
+
|
|
3
|
+
Provides mixins for query result ordering, limiting, and result
|
|
4
|
+
returning functionality.
|
|
5
|
+
"""
|
|
2
6
|
|
|
3
7
|
from typing import TYPE_CHECKING, Optional, Union, cast
|
|
4
8
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"""SELECT clause mixins
|
|
1
|
+
"""SELECT clause mixins.
|
|
2
|
+
|
|
3
|
+
Provides mixins for SELECT statement functionality including column selection,
|
|
4
|
+
CASE expressions, subqueries, and window functions.
|
|
5
|
+
"""
|
|
2
6
|
|
|
3
7
|
from typing import TYPE_CHECKING, Any, Optional, Union, cast
|
|
4
8
|
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""UPDATE operation mixins.
|
|
2
|
+
|
|
3
|
+
Provides mixins for UPDATE statement functionality including
|
|
4
|
+
table specification, SET clauses, and FROM clauses.
|
|
5
|
+
"""
|
|
2
6
|
|
|
3
7
|
from collections.abc import Mapping
|
|
4
8
|
from typing import Any, Optional, Union, cast
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
# ruff: noqa: PLR2004
|
|
2
|
-
"""
|
|
2
|
+
"""WHERE and HAVING clause mixins.
|
|
3
|
+
|
|
4
|
+
Provides mixins for WHERE and HAVING clause functionality with
|
|
5
|
+
parameter binding and various condition operators.
|
|
6
|
+
"""
|
|
3
7
|
|
|
4
8
|
from typing import TYPE_CHECKING, Any, Optional, Union, cast
|
|
5
9
|
|
sqlspec/cli.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# ruff: noqa: C901
|
|
1
2
|
import sys
|
|
2
3
|
from collections.abc import Sequence
|
|
3
4
|
from typing import TYPE_CHECKING, Any, Optional, Union, cast
|
|
@@ -99,6 +100,15 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
|
|
|
99
100
|
show_default=True,
|
|
100
101
|
is_flag=True,
|
|
101
102
|
)
|
|
103
|
+
include_option = click.option(
|
|
104
|
+
"--include", multiple=True, help="Include only specific configurations (can be used multiple times)"
|
|
105
|
+
)
|
|
106
|
+
exclude_option = click.option(
|
|
107
|
+
"--exclude", multiple=True, help="Exclude specific configurations (can be used multiple times)"
|
|
108
|
+
)
|
|
109
|
+
dry_run_option = click.option(
|
|
110
|
+
"--dry-run", is_flag=True, default=False, help="Show what would be executed without making changes"
|
|
111
|
+
)
|
|
102
112
|
|
|
103
113
|
def get_config_by_bind_key(
|
|
104
114
|
ctx: "click.Context", bind_key: Optional[str]
|
|
@@ -114,35 +124,180 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
|
|
|
114
124
|
"""
|
|
115
125
|
configs = ctx.obj["configs"]
|
|
116
126
|
if bind_key is None:
|
|
117
|
-
|
|
127
|
+
config = configs[0]
|
|
128
|
+
else:
|
|
129
|
+
config = None
|
|
130
|
+
for cfg in configs:
|
|
131
|
+
config_name = getattr(cfg, "name", None) or getattr(cfg, "bind_key", None)
|
|
132
|
+
if config_name == bind_key:
|
|
133
|
+
config = cfg
|
|
134
|
+
break
|
|
135
|
+
|
|
136
|
+
if config is None:
|
|
137
|
+
console.print(f"[red]No config found for bind key: {bind_key}[/]")
|
|
138
|
+
sys.exit(1)
|
|
139
|
+
|
|
140
|
+
# Extract the actual config from DatabaseConfig wrapper if needed
|
|
141
|
+
from sqlspec.extensions.litestar.config import DatabaseConfig
|
|
142
|
+
|
|
143
|
+
if isinstance(config, DatabaseConfig):
|
|
144
|
+
config = config.config
|
|
145
|
+
|
|
146
|
+
return cast("Union[AsyncDatabaseConfig[Any, Any, Any], SyncDatabaseConfig[Any, Any, Any]]", config)
|
|
147
|
+
|
|
148
|
+
def get_configs_with_migrations(ctx: "click.Context", enabled_only: bool = False) -> "list[tuple[str, Any]]":
|
|
149
|
+
"""Get all configurations that have migrations enabled.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
ctx: The click context.
|
|
153
|
+
enabled_only: If True, only return configs with enabled=True.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
List of tuples (config_name, config) for configs with migrations enabled.
|
|
157
|
+
"""
|
|
158
|
+
configs = ctx.obj["configs"]
|
|
159
|
+
migration_configs = []
|
|
160
|
+
|
|
161
|
+
from sqlspec.extensions.litestar.config import DatabaseConfig
|
|
118
162
|
|
|
119
163
|
for config in configs:
|
|
120
|
-
|
|
121
|
-
if
|
|
122
|
-
|
|
164
|
+
# Extract the actual config from DatabaseConfig wrapper if needed
|
|
165
|
+
actual_config = config.config if isinstance(config, DatabaseConfig) else config
|
|
166
|
+
|
|
167
|
+
migration_config = getattr(actual_config, "migration_config", None)
|
|
168
|
+
if migration_config:
|
|
169
|
+
enabled = migration_config.get("enabled", True)
|
|
170
|
+
if not enabled_only or enabled:
|
|
171
|
+
config_name = (
|
|
172
|
+
getattr(actual_config, "name", None)
|
|
173
|
+
or getattr(actual_config, "bind_key", None)
|
|
174
|
+
or str(type(actual_config).__name__)
|
|
175
|
+
)
|
|
176
|
+
migration_configs.append((config_name, actual_config))
|
|
177
|
+
|
|
178
|
+
return migration_configs
|
|
179
|
+
|
|
180
|
+
def filter_configs(
|
|
181
|
+
configs: "list[tuple[str, Any]]", include: "tuple[str, ...]", exclude: "tuple[str, ...]"
|
|
182
|
+
) -> "list[tuple[str, Any]]":
|
|
183
|
+
"""Filter configuration list based on include/exclude criteria.
|
|
184
|
+
|
|
185
|
+
Args:
|
|
186
|
+
configs: List of (config_name, config) tuples.
|
|
187
|
+
include: Config names to include (empty means include all).
|
|
188
|
+
exclude: Config names to exclude.
|
|
189
|
+
|
|
190
|
+
Returns:
|
|
191
|
+
Filtered list of configurations.
|
|
192
|
+
"""
|
|
193
|
+
filtered = configs
|
|
194
|
+
if include:
|
|
195
|
+
filtered = [(name, config) for name, config in filtered if name in include]
|
|
196
|
+
if exclude:
|
|
197
|
+
filtered = [(name, config) for name, config in filtered if name not in exclude]
|
|
198
|
+
return filtered
|
|
199
|
+
|
|
200
|
+
def process_multiple_configs(
|
|
201
|
+
ctx: "click.Context",
|
|
202
|
+
bind_key: Optional[str],
|
|
203
|
+
include: "tuple[str, ...]",
|
|
204
|
+
exclude: "tuple[str, ...]",
|
|
205
|
+
dry_run: bool,
|
|
206
|
+
operation_name: str,
|
|
207
|
+
) -> "Optional[list[tuple[str, Any]]]":
|
|
208
|
+
"""Process configuration selection for multi-config operations.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
ctx: Click context.
|
|
212
|
+
bind_key: Specific bind key to target.
|
|
213
|
+
include: Config names to include.
|
|
214
|
+
exclude: Config names to exclude.
|
|
215
|
+
dry_run: Whether this is a dry run.
|
|
216
|
+
operation_name: Name of the operation for display.
|
|
217
|
+
|
|
218
|
+
Returns:
|
|
219
|
+
List of (config_name, config) tuples to process, or None for single config mode.
|
|
220
|
+
"""
|
|
221
|
+
# If specific bind_key requested, use single config mode
|
|
222
|
+
if bind_key and not include and not exclude:
|
|
223
|
+
return None
|
|
224
|
+
|
|
225
|
+
# Get enabled configs by default, all configs if include/exclude specified
|
|
226
|
+
enabled_only = not include and not exclude
|
|
227
|
+
migration_configs = get_configs_with_migrations(ctx, enabled_only=enabled_only)
|
|
228
|
+
|
|
229
|
+
# If only one config and no filtering, use single config mode
|
|
230
|
+
if len(migration_configs) <= 1 and not include and not exclude:
|
|
231
|
+
return None
|
|
232
|
+
|
|
233
|
+
# Apply filtering
|
|
234
|
+
configs_to_process = filter_configs(migration_configs, include, exclude)
|
|
235
|
+
|
|
236
|
+
if not configs_to_process:
|
|
237
|
+
console.print("[yellow]No configurations match the specified criteria.[/]")
|
|
238
|
+
return []
|
|
123
239
|
|
|
124
|
-
|
|
125
|
-
|
|
240
|
+
# Show what will be processed
|
|
241
|
+
if dry_run:
|
|
242
|
+
console.print(f"[blue]Dry run: Would {operation_name} {len(configs_to_process)} configuration(s)[/]")
|
|
243
|
+
for config_name, _ in configs_to_process:
|
|
244
|
+
console.print(f" • {config_name}")
|
|
245
|
+
return []
|
|
246
|
+
|
|
247
|
+
return configs_to_process
|
|
126
248
|
|
|
127
249
|
@database_group.command(name="show-current-revision", help="Shows the current revision for the database.")
|
|
128
250
|
@bind_key_option
|
|
129
251
|
@verbose_option
|
|
130
|
-
|
|
252
|
+
@include_option
|
|
253
|
+
@exclude_option
|
|
254
|
+
def show_database_revision( # pyright: ignore[reportUnusedFunction]
|
|
255
|
+
bind_key: Optional[str], verbose: bool, include: "tuple[str, ...]", exclude: "tuple[str, ...]"
|
|
256
|
+
) -> None:
|
|
131
257
|
"""Show current database revision."""
|
|
132
258
|
from sqlspec.migrations.commands import MigrationCommands
|
|
133
259
|
|
|
134
260
|
ctx = click.get_current_context()
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
261
|
+
|
|
262
|
+
# Check if this is a multi-config operation
|
|
263
|
+
configs_to_process = process_multiple_configs(
|
|
264
|
+
ctx, bind_key, include, exclude, dry_run=False, operation_name="show current revision"
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
if configs_to_process is not None:
|
|
268
|
+
if not configs_to_process:
|
|
269
|
+
return
|
|
270
|
+
|
|
271
|
+
console.rule("[yellow]Listing current revisions for all configurations[/]", align="left")
|
|
272
|
+
|
|
273
|
+
for config_name, config in configs_to_process:
|
|
274
|
+
console.print(f"\n[blue]Configuration: {config_name}[/]")
|
|
275
|
+
try:
|
|
276
|
+
migration_commands = MigrationCommands(config=config)
|
|
277
|
+
migration_commands.current(verbose=verbose)
|
|
278
|
+
except Exception as e:
|
|
279
|
+
console.print(f"[red]✗ Failed to get current revision for {config_name}: {e}[/]")
|
|
280
|
+
else:
|
|
281
|
+
# Single config operation
|
|
282
|
+
console.rule("[yellow]Listing current revision[/]", align="left")
|
|
283
|
+
sqlspec_config = get_config_by_bind_key(ctx, bind_key)
|
|
284
|
+
migration_commands = MigrationCommands(config=sqlspec_config)
|
|
285
|
+
migration_commands.current(verbose=verbose)
|
|
139
286
|
|
|
140
287
|
@database_group.command(name="downgrade", help="Downgrade database to a specific revision.")
|
|
141
288
|
@bind_key_option
|
|
142
289
|
@no_prompt_option
|
|
290
|
+
@include_option
|
|
291
|
+
@exclude_option
|
|
292
|
+
@dry_run_option
|
|
143
293
|
@click.argument("revision", type=str, default="-1")
|
|
144
294
|
def downgrade_database( # pyright: ignore[reportUnusedFunction]
|
|
145
|
-
bind_key: Optional[str],
|
|
295
|
+
bind_key: Optional[str],
|
|
296
|
+
revision: str,
|
|
297
|
+
no_prompt: bool,
|
|
298
|
+
include: "tuple[str, ...]",
|
|
299
|
+
exclude: "tuple[str, ...]",
|
|
300
|
+
dry_run: bool,
|
|
146
301
|
) -> None:
|
|
147
302
|
"""Downgrade the database to the latest revision."""
|
|
148
303
|
from rich.prompt import Confirm
|
|
@@ -150,23 +305,59 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
|
|
|
150
305
|
from sqlspec.migrations.commands import MigrationCommands
|
|
151
306
|
|
|
152
307
|
ctx = click.get_current_context()
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
else Confirm.ask(f"Are you sure you want to downgrade the database to the `{revision}` revision?")
|
|
308
|
+
|
|
309
|
+
# Check if this is a multi-config operation
|
|
310
|
+
configs_to_process = process_multiple_configs(
|
|
311
|
+
ctx, bind_key, include, exclude, dry_run=dry_run, operation_name=f"downgrade to {revision}"
|
|
158
312
|
)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
313
|
+
|
|
314
|
+
if configs_to_process is not None:
|
|
315
|
+
if not configs_to_process:
|
|
316
|
+
return
|
|
317
|
+
|
|
318
|
+
if not no_prompt and not Confirm.ask(
|
|
319
|
+
f"[bold]Are you sure you want to downgrade {len(configs_to_process)} configuration(s) to revision {revision}?[/]"
|
|
320
|
+
):
|
|
321
|
+
console.print("[yellow]Operation cancelled.[/]")
|
|
322
|
+
return
|
|
323
|
+
|
|
324
|
+
console.rule("[yellow]Starting multi-configuration downgrade process[/]", align="left")
|
|
325
|
+
|
|
326
|
+
for config_name, config in configs_to_process:
|
|
327
|
+
console.print(f"[blue]Downgrading configuration: {config_name}[/]")
|
|
328
|
+
try:
|
|
329
|
+
migration_commands = MigrationCommands(config=config)
|
|
330
|
+
migration_commands.downgrade(revision=revision)
|
|
331
|
+
console.print(f"[green]✓ Successfully downgraded: {config_name}[/]")
|
|
332
|
+
except Exception as e:
|
|
333
|
+
console.print(f"[red]✗ Failed to downgrade {config_name}: {e}[/]")
|
|
334
|
+
else:
|
|
335
|
+
# Single config operation
|
|
336
|
+
console.rule("[yellow]Starting database downgrade process[/]", align="left")
|
|
337
|
+
input_confirmed = (
|
|
338
|
+
True
|
|
339
|
+
if no_prompt
|
|
340
|
+
else Confirm.ask(f"Are you sure you want to downgrade the database to the `{revision}` revision?")
|
|
341
|
+
)
|
|
342
|
+
if input_confirmed:
|
|
343
|
+
sqlspec_config = get_config_by_bind_key(ctx, bind_key)
|
|
344
|
+
migration_commands = MigrationCommands(config=sqlspec_config)
|
|
345
|
+
migration_commands.downgrade(revision=revision)
|
|
163
346
|
|
|
164
347
|
@database_group.command(name="upgrade", help="Upgrade database to a specific revision.")
|
|
165
348
|
@bind_key_option
|
|
166
349
|
@no_prompt_option
|
|
350
|
+
@include_option
|
|
351
|
+
@exclude_option
|
|
352
|
+
@dry_run_option
|
|
167
353
|
@click.argument("revision", type=str, default="head")
|
|
168
354
|
def upgrade_database( # pyright: ignore[reportUnusedFunction]
|
|
169
|
-
bind_key: Optional[str],
|
|
355
|
+
bind_key: Optional[str],
|
|
356
|
+
revision: str,
|
|
357
|
+
no_prompt: bool,
|
|
358
|
+
include: "tuple[str, ...]",
|
|
359
|
+
exclude: "tuple[str, ...]",
|
|
360
|
+
dry_run: bool,
|
|
170
361
|
) -> None:
|
|
171
362
|
"""Upgrade the database to the latest revision."""
|
|
172
363
|
from rich.prompt import Confirm
|
|
@@ -174,16 +365,44 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
|
|
|
174
365
|
from sqlspec.migrations.commands import MigrationCommands
|
|
175
366
|
|
|
176
367
|
ctx = click.get_current_context()
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
else Confirm.ask(f"[bold]Are you sure you want migrate the database to the `{revision}` revision?[/]")
|
|
368
|
+
|
|
369
|
+
# Check if this is a multi-config operation
|
|
370
|
+
configs_to_process = process_multiple_configs(
|
|
371
|
+
ctx, bind_key, include, exclude, dry_run, operation_name=f"upgrade to {revision}"
|
|
182
372
|
)
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
373
|
+
|
|
374
|
+
if configs_to_process is not None:
|
|
375
|
+
if not configs_to_process:
|
|
376
|
+
return
|
|
377
|
+
|
|
378
|
+
if not no_prompt and not Confirm.ask(
|
|
379
|
+
f"[bold]Are you sure you want to upgrade {len(configs_to_process)} configuration(s) to revision {revision}?[/]"
|
|
380
|
+
):
|
|
381
|
+
console.print("[yellow]Operation cancelled.[/]")
|
|
382
|
+
return
|
|
383
|
+
|
|
384
|
+
console.rule("[yellow]Starting multi-configuration upgrade process[/]", align="left")
|
|
385
|
+
|
|
386
|
+
for config_name, config in configs_to_process:
|
|
387
|
+
console.print(f"[blue]Upgrading configuration: {config_name}[/]")
|
|
388
|
+
try:
|
|
389
|
+
migration_commands = MigrationCommands(config=config)
|
|
390
|
+
migration_commands.upgrade(revision=revision)
|
|
391
|
+
console.print(f"[green]✓ Successfully upgraded: {config_name}[/]")
|
|
392
|
+
except Exception as e:
|
|
393
|
+
console.print(f"[red]✗ Failed to upgrade {config_name}: {e}[/]")
|
|
394
|
+
else:
|
|
395
|
+
# Single config operation
|
|
396
|
+
console.rule("[yellow]Starting database upgrade process[/]", align="left")
|
|
397
|
+
input_confirmed = (
|
|
398
|
+
True
|
|
399
|
+
if no_prompt
|
|
400
|
+
else Confirm.ask(f"[bold]Are you sure you want migrate the database to the `{revision}` revision?[/]")
|
|
401
|
+
)
|
|
402
|
+
if input_confirmed:
|
|
403
|
+
sqlspec_config = get_config_by_bind_key(ctx, bind_key)
|
|
404
|
+
migration_commands = MigrationCommands(config=sqlspec_config)
|
|
405
|
+
migration_commands.upgrade(revision=revision)
|
|
187
406
|
|
|
188
407
|
@database_group.command(help="Stamp the revision table with the given revision")
|
|
189
408
|
@click.argument("revision", type=str)
|
|
@@ -217,10 +436,14 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
|
|
|
217
436
|
)
|
|
218
437
|
if input_confirmed:
|
|
219
438
|
configs = [get_config_by_bind_key(ctx, bind_key)] if bind_key is not None else ctx.obj["configs"]
|
|
439
|
+
from sqlspec.extensions.litestar.config import DatabaseConfig
|
|
440
|
+
|
|
220
441
|
for config in configs:
|
|
221
|
-
|
|
442
|
+
# Extract the actual config from DatabaseConfig wrapper if needed
|
|
443
|
+
actual_config = config.config if isinstance(config, DatabaseConfig) else config
|
|
444
|
+
migration_config = getattr(actual_config, "migration_config", {})
|
|
222
445
|
directory = migration_config.get("script_location", "migrations") if directory is None else directory
|
|
223
|
-
migration_commands = MigrationCommands(config=
|
|
446
|
+
migration_commands = MigrationCommands(config=actual_config)
|
|
224
447
|
migration_commands.init(directory=cast("str", directory), package=package)
|
|
225
448
|
|
|
226
449
|
@database_group.command(name="make-migrations", help="Create a new migration revision.")
|
|
@@ -244,4 +467,29 @@ def add_migration_commands(database_group: Optional["Group"] = None) -> "Group":
|
|
|
244
467
|
migration_commands = MigrationCommands(config=sqlspec_config)
|
|
245
468
|
migration_commands.revision(message=message)
|
|
246
469
|
|
|
470
|
+
@database_group.command(name="show-config", help="Show all configurations with migrations enabled.")
|
|
471
|
+
def show_config() -> None: # pyright: ignore[reportUnusedFunction]
|
|
472
|
+
"""Show and display all configurations with migrations enabled."""
|
|
473
|
+
from rich.table import Table
|
|
474
|
+
|
|
475
|
+
ctx = click.get_current_context()
|
|
476
|
+
migration_configs = get_configs_with_migrations(ctx)
|
|
477
|
+
|
|
478
|
+
if not migration_configs:
|
|
479
|
+
console.print("[yellow]No configurations with migrations detected.[/]")
|
|
480
|
+
return
|
|
481
|
+
|
|
482
|
+
table = Table(title="Migration Configurations")
|
|
483
|
+
table.add_column("Configuration Name", style="cyan")
|
|
484
|
+
table.add_column("Migration Path", style="blue")
|
|
485
|
+
table.add_column("Status", style="green")
|
|
486
|
+
|
|
487
|
+
for config_name, config in migration_configs:
|
|
488
|
+
migration_config = getattr(config, "migration_config", {})
|
|
489
|
+
script_location = migration_config.get("script_location", "migrations")
|
|
490
|
+
table.add_row(config_name, script_location, "Migration Enabled")
|
|
491
|
+
|
|
492
|
+
console.print(table)
|
|
493
|
+
console.print(f"[blue]Found {len(migration_configs)} configuration(s) with migrations enabled.[/]")
|
|
494
|
+
|
|
247
495
|
return database_group
|