sqlspec 0.14.0__py3-none-any.whl → 0.15.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 +50 -25
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +1 -3
- sqlspec/_serialization.py +1 -2
- sqlspec/_sql.py +256 -120
- sqlspec/_typing.py +278 -142
- sqlspec/adapters/adbc/__init__.py +4 -3
- sqlspec/adapters/adbc/_types.py +12 -0
- sqlspec/adapters/adbc/config.py +115 -248
- sqlspec/adapters/adbc/driver.py +462 -353
- sqlspec/adapters/aiosqlite/__init__.py +18 -3
- sqlspec/adapters/aiosqlite/_types.py +13 -0
- sqlspec/adapters/aiosqlite/config.py +199 -129
- sqlspec/adapters/aiosqlite/driver.py +230 -269
- sqlspec/adapters/asyncmy/__init__.py +18 -3
- sqlspec/adapters/asyncmy/_types.py +12 -0
- sqlspec/adapters/asyncmy/config.py +80 -168
- sqlspec/adapters/asyncmy/driver.py +260 -225
- sqlspec/adapters/asyncpg/__init__.py +19 -4
- sqlspec/adapters/asyncpg/_types.py +17 -0
- sqlspec/adapters/asyncpg/config.py +82 -181
- sqlspec/adapters/asyncpg/driver.py +285 -383
- sqlspec/adapters/bigquery/__init__.py +17 -3
- sqlspec/adapters/bigquery/_types.py +12 -0
- sqlspec/adapters/bigquery/config.py +191 -258
- sqlspec/adapters/bigquery/driver.py +474 -646
- sqlspec/adapters/duckdb/__init__.py +14 -3
- sqlspec/adapters/duckdb/_types.py +12 -0
- sqlspec/adapters/duckdb/config.py +415 -351
- sqlspec/adapters/duckdb/driver.py +343 -413
- sqlspec/adapters/oracledb/__init__.py +19 -5
- sqlspec/adapters/oracledb/_types.py +14 -0
- sqlspec/adapters/oracledb/config.py +123 -379
- sqlspec/adapters/oracledb/driver.py +507 -560
- sqlspec/adapters/psqlpy/__init__.py +13 -3
- sqlspec/adapters/psqlpy/_types.py +11 -0
- sqlspec/adapters/psqlpy/config.py +93 -254
- sqlspec/adapters/psqlpy/driver.py +505 -234
- sqlspec/adapters/psycopg/__init__.py +19 -5
- sqlspec/adapters/psycopg/_types.py +17 -0
- sqlspec/adapters/psycopg/config.py +143 -403
- sqlspec/adapters/psycopg/driver.py +706 -872
- sqlspec/adapters/sqlite/__init__.py +14 -3
- sqlspec/adapters/sqlite/_types.py +11 -0
- sqlspec/adapters/sqlite/config.py +202 -118
- sqlspec/adapters/sqlite/driver.py +264 -303
- sqlspec/base.py +105 -9
- sqlspec/{statement/builder → builder}/__init__.py +12 -14
- sqlspec/{statement/builder → builder}/_base.py +120 -55
- sqlspec/{statement/builder → builder}/_column.py +17 -6
- sqlspec/{statement/builder → builder}/_ddl.py +46 -79
- sqlspec/{statement/builder → builder}/_ddl_utils.py +5 -10
- sqlspec/{statement/builder → builder}/_delete.py +6 -25
- sqlspec/{statement/builder → builder}/_insert.py +6 -64
- sqlspec/builder/_merge.py +56 -0
- sqlspec/{statement/builder → builder}/_parsing_utils.py +3 -10
- sqlspec/{statement/builder → builder}/_select.py +11 -56
- sqlspec/{statement/builder → builder}/_update.py +12 -18
- sqlspec/{statement/builder → builder}/mixins/__init__.py +10 -14
- sqlspec/{statement/builder → builder}/mixins/_cte_and_set_ops.py +48 -59
- sqlspec/{statement/builder → builder}/mixins/_insert_operations.py +22 -16
- sqlspec/{statement/builder → builder}/mixins/_join_operations.py +1 -3
- sqlspec/{statement/builder → builder}/mixins/_merge_operations.py +3 -5
- sqlspec/{statement/builder → builder}/mixins/_order_limit_operations.py +3 -3
- sqlspec/{statement/builder → builder}/mixins/_pivot_operations.py +4 -8
- sqlspec/{statement/builder → builder}/mixins/_select_operations.py +21 -36
- sqlspec/{statement/builder → builder}/mixins/_update_operations.py +3 -14
- sqlspec/{statement/builder → builder}/mixins/_where_clause.py +52 -79
- sqlspec/cli.py +4 -5
- sqlspec/config.py +180 -133
- sqlspec/core/__init__.py +63 -0
- sqlspec/core/cache.py +873 -0
- sqlspec/core/compiler.py +396 -0
- sqlspec/core/filters.py +828 -0
- sqlspec/core/hashing.py +310 -0
- sqlspec/core/parameters.py +1209 -0
- sqlspec/core/result.py +664 -0
- sqlspec/{statement → core}/splitter.py +321 -191
- sqlspec/core/statement.py +651 -0
- sqlspec/driver/__init__.py +7 -10
- sqlspec/driver/_async.py +387 -176
- sqlspec/driver/_common.py +527 -289
- sqlspec/driver/_sync.py +390 -172
- sqlspec/driver/mixins/__init__.py +2 -19
- sqlspec/driver/mixins/_result_tools.py +168 -0
- sqlspec/driver/mixins/_sql_translator.py +6 -3
- sqlspec/exceptions.py +5 -252
- sqlspec/extensions/aiosql/adapter.py +93 -96
- sqlspec/extensions/litestar/config.py +0 -1
- sqlspec/extensions/litestar/handlers.py +15 -26
- sqlspec/extensions/litestar/plugin.py +16 -14
- sqlspec/extensions/litestar/providers.py +17 -52
- sqlspec/loader.py +424 -105
- sqlspec/migrations/__init__.py +12 -0
- sqlspec/migrations/base.py +92 -68
- sqlspec/migrations/commands.py +24 -106
- sqlspec/migrations/loaders.py +402 -0
- sqlspec/migrations/runner.py +49 -51
- sqlspec/migrations/tracker.py +31 -44
- sqlspec/migrations/utils.py +64 -24
- sqlspec/protocols.py +7 -183
- sqlspec/storage/__init__.py +1 -1
- sqlspec/storage/backends/base.py +37 -40
- sqlspec/storage/backends/fsspec.py +136 -112
- sqlspec/storage/backends/obstore.py +138 -160
- sqlspec/storage/capabilities.py +5 -4
- sqlspec/storage/registry.py +57 -106
- sqlspec/typing.py +136 -115
- sqlspec/utils/__init__.py +2 -3
- sqlspec/utils/correlation.py +0 -3
- sqlspec/utils/deprecation.py +6 -6
- sqlspec/utils/fixtures.py +6 -6
- sqlspec/utils/logging.py +0 -2
- sqlspec/utils/module_loader.py +7 -12
- sqlspec/utils/singleton.py +0 -1
- sqlspec/utils/sync_tools.py +16 -37
- sqlspec/utils/text.py +12 -51
- sqlspec/utils/type_guards.py +443 -232
- {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/METADATA +7 -2
- sqlspec-0.15.0.dist-info/RECORD +134 -0
- sqlspec-0.15.0.dist-info/entry_points.txt +2 -0
- sqlspec/driver/connection.py +0 -207
- sqlspec/driver/mixins/_cache.py +0 -114
- sqlspec/driver/mixins/_csv_writer.py +0 -91
- sqlspec/driver/mixins/_pipeline.py +0 -508
- sqlspec/driver/mixins/_query_tools.py +0 -796
- sqlspec/driver/mixins/_result_utils.py +0 -138
- sqlspec/driver/mixins/_storage.py +0 -912
- sqlspec/driver/mixins/_type_coercion.py +0 -128
- sqlspec/driver/parameters.py +0 -138
- sqlspec/statement/__init__.py +0 -21
- sqlspec/statement/builder/_merge.py +0 -95
- sqlspec/statement/cache.py +0 -50
- sqlspec/statement/filters.py +0 -625
- sqlspec/statement/parameters.py +0 -996
- sqlspec/statement/pipelines/__init__.py +0 -210
- sqlspec/statement/pipelines/analyzers/__init__.py +0 -9
- sqlspec/statement/pipelines/analyzers/_analyzer.py +0 -646
- sqlspec/statement/pipelines/context.py +0 -115
- sqlspec/statement/pipelines/transformers/__init__.py +0 -7
- sqlspec/statement/pipelines/transformers/_expression_simplifier.py +0 -88
- sqlspec/statement/pipelines/transformers/_literal_parameterizer.py +0 -1247
- sqlspec/statement/pipelines/transformers/_remove_comments_and_hints.py +0 -76
- sqlspec/statement/pipelines/validators/__init__.py +0 -23
- sqlspec/statement/pipelines/validators/_dml_safety.py +0 -290
- sqlspec/statement/pipelines/validators/_parameter_style.py +0 -370
- sqlspec/statement/pipelines/validators/_performance.py +0 -714
- sqlspec/statement/pipelines/validators/_security.py +0 -967
- sqlspec/statement/result.py +0 -435
- sqlspec/statement/sql.py +0 -1774
- sqlspec/utils/cached_property.py +0 -25
- sqlspec/utils/statement_hashing.py +0 -203
- sqlspec-0.14.0.dist-info/RECORD +0 -143
- sqlspec-0.14.0.dist-info/entry_points.txt +0 -2
- /sqlspec/{statement/builder → builder}/mixins/_delete_operations.py +0 -0
- {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/licenses/LICENSE +0 -0
- {sqlspec-0.14.0.dist-info → sqlspec-0.15.0.dist-info}/licenses/NOTICE +0 -0
sqlspec/migrations/commands.py
CHANGED
|
@@ -8,11 +8,11 @@ from typing import TYPE_CHECKING, Any, Union, cast
|
|
|
8
8
|
from rich.console import Console
|
|
9
9
|
from rich.table import Table
|
|
10
10
|
|
|
11
|
+
from sqlspec._sql import sql
|
|
11
12
|
from sqlspec.migrations.base import BaseMigrationCommands
|
|
12
13
|
from sqlspec.migrations.runner import AsyncMigrationRunner, SyncMigrationRunner
|
|
13
14
|
from sqlspec.migrations.tracker import AsyncMigrationTracker, SyncMigrationTracker
|
|
14
15
|
from sqlspec.migrations.utils import create_migration_file
|
|
15
|
-
from sqlspec.statement.sql import SQL
|
|
16
16
|
from sqlspec.utils.logging import get_logger
|
|
17
17
|
from sqlspec.utils.sync_tools import await_
|
|
18
18
|
|
|
@@ -26,7 +26,7 @@ console = Console()
|
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class SyncMigrationCommands(BaseMigrationCommands["SyncConfigT", Any]):
|
|
29
|
-
"""SQLSpec native migration commands
|
|
29
|
+
"""SQLSpec native migration commands."""
|
|
30
30
|
|
|
31
31
|
def __init__(self, config: "SyncConfigT") -> None:
|
|
32
32
|
"""Initialize migration commands.
|
|
@@ -95,8 +95,6 @@ class SyncMigrationCommands(BaseMigrationCommands["SyncConfigT", Any]):
|
|
|
95
95
|
|
|
96
96
|
current = self.tracker.get_current_version(driver)
|
|
97
97
|
all_migrations = self.runner.get_migration_files()
|
|
98
|
-
|
|
99
|
-
# Determine pending migrations
|
|
100
98
|
pending = []
|
|
101
99
|
for version, file_path in all_migrations:
|
|
102
100
|
if (current is None or version > current) and (revision == "head" or version <= revision):
|
|
@@ -108,21 +106,16 @@ class SyncMigrationCommands(BaseMigrationCommands["SyncConfigT", Any]):
|
|
|
108
106
|
|
|
109
107
|
console.print(f"[yellow]Found {len(pending)} pending migrations[/]")
|
|
110
108
|
|
|
111
|
-
# Execute migrations
|
|
112
109
|
for version, file_path in pending:
|
|
113
110
|
migration = self.runner.load_migration(file_path)
|
|
114
111
|
|
|
115
112
|
console.print(f"\n[cyan]Applying {version}:[/] {migration['description']}")
|
|
116
113
|
|
|
117
114
|
try:
|
|
118
|
-
# Execute migration
|
|
119
115
|
_, execution_time = self.runner.execute_upgrade(driver, migration)
|
|
120
|
-
|
|
121
|
-
# Record in tracking table
|
|
122
116
|
self.tracker.record_migration(
|
|
123
117
|
driver, migration["version"], migration["description"], execution_time, migration["checksum"]
|
|
124
118
|
)
|
|
125
|
-
|
|
126
119
|
console.print(f"[green]✓ Applied in {execution_time}ms[/]")
|
|
127
120
|
|
|
128
121
|
except Exception as e:
|
|
@@ -137,19 +130,14 @@ class SyncMigrationCommands(BaseMigrationCommands["SyncConfigT", Any]):
|
|
|
137
130
|
"""
|
|
138
131
|
with self.config.provide_session() as driver:
|
|
139
132
|
self.tracker.ensure_tracking_table(driver)
|
|
140
|
-
|
|
141
133
|
applied = self.tracker.get_applied_migrations(driver)
|
|
142
134
|
if not applied:
|
|
143
135
|
console.print("[yellow]No migrations to downgrade[/]")
|
|
144
136
|
return
|
|
145
|
-
|
|
146
|
-
# Determine migrations to revert
|
|
147
137
|
to_revert = []
|
|
148
138
|
if revision == "-1":
|
|
149
|
-
# Downgrade one step
|
|
150
139
|
to_revert = [applied[-1]]
|
|
151
140
|
else:
|
|
152
|
-
# Downgrade to specific version
|
|
153
141
|
for migration in reversed(applied):
|
|
154
142
|
if migration["version_num"] > revision:
|
|
155
143
|
to_revert.append(migration)
|
|
@@ -159,29 +147,18 @@ class SyncMigrationCommands(BaseMigrationCommands["SyncConfigT", Any]):
|
|
|
159
147
|
return
|
|
160
148
|
|
|
161
149
|
console.print(f"[yellow]Reverting {len(to_revert)} migrations[/]")
|
|
162
|
-
|
|
163
|
-
# Load migration files
|
|
164
150
|
all_files = dict(self.runner.get_migration_files())
|
|
165
|
-
|
|
166
151
|
for migration_record in to_revert:
|
|
167
152
|
version = migration_record["version_num"]
|
|
168
|
-
|
|
169
153
|
if version not in all_files:
|
|
170
154
|
console.print(f"[red]Migration file not found for {version}[/]")
|
|
171
155
|
continue
|
|
172
|
-
|
|
173
156
|
migration = self.runner.load_migration(all_files[version])
|
|
174
157
|
console.print(f"\n[cyan]Reverting {version}:[/] {migration['description']}")
|
|
175
|
-
|
|
176
158
|
try:
|
|
177
|
-
# Execute downgrade
|
|
178
159
|
_, execution_time = self.runner.execute_downgrade(driver, migration)
|
|
179
|
-
|
|
180
|
-
# Remove from tracking table
|
|
181
160
|
self.tracker.remove_migration(driver, version)
|
|
182
|
-
|
|
183
161
|
console.print(f"[green]✓ Reverted in {execution_time}ms[/]")
|
|
184
|
-
|
|
185
162
|
except Exception as e:
|
|
186
163
|
console.print(f"[red]✗ Failed: {e}[/]")
|
|
187
164
|
raise
|
|
@@ -194,51 +171,37 @@ class SyncMigrationCommands(BaseMigrationCommands["SyncConfigT", Any]):
|
|
|
194
171
|
"""
|
|
195
172
|
with self.config.provide_session() as driver:
|
|
196
173
|
self.tracker.ensure_tracking_table(driver)
|
|
197
|
-
|
|
198
|
-
# Validate revision exists
|
|
199
174
|
all_migrations = dict(self.runner.get_migration_files())
|
|
200
175
|
if revision not in all_migrations:
|
|
201
176
|
console.print(f"[red]Unknown revision: {revision}[/]")
|
|
202
177
|
return
|
|
203
|
-
|
|
204
|
-
# Clear existing records and stamp
|
|
205
|
-
clear_sql = SQL(f"DELETE FROM {self.tracker.version_table}")
|
|
178
|
+
clear_sql = sql.delete().from_(self.tracker.version_table)
|
|
206
179
|
driver.execute(clear_sql)
|
|
207
|
-
|
|
208
180
|
self.tracker.record_migration(driver, revision, f"Stamped to {revision}", 0, "manual-stamp")
|
|
209
|
-
|
|
210
181
|
console.print(f"[green]Database stamped at revision {revision}[/]")
|
|
211
182
|
|
|
212
|
-
def revision(self, message: str) -> None:
|
|
183
|
+
def revision(self, message: str, file_type: str = "sql") -> None:
|
|
213
184
|
"""Create a new migration file.
|
|
214
185
|
|
|
215
186
|
Args:
|
|
216
187
|
message: Description for the migration.
|
|
188
|
+
file_type: Type of migration file to create ('sql' or 'py').
|
|
217
189
|
"""
|
|
218
|
-
# Determine next version number
|
|
219
190
|
existing = self.runner.get_migration_files()
|
|
220
|
-
if existing
|
|
221
|
-
last_version = existing[-1][0]
|
|
222
|
-
next_num = int(last_version) + 1
|
|
223
|
-
else:
|
|
224
|
-
next_num = 1
|
|
225
|
-
|
|
191
|
+
next_num = int(existing[-1][0]) + 1 if existing else 1
|
|
226
192
|
next_version = str(next_num).zfill(4)
|
|
227
|
-
|
|
228
|
-
# Create migration file
|
|
229
|
-
file_path = create_migration_file(self.migrations_path, next_version, message)
|
|
230
|
-
|
|
193
|
+
file_path = create_migration_file(self.migrations_path, next_version, message, file_type)
|
|
231
194
|
console.print(f"[green]Created migration:[/] {file_path}")
|
|
232
195
|
|
|
233
196
|
|
|
234
197
|
class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
|
|
235
|
-
"""SQLSpec native
|
|
198
|
+
"""SQLSpec native migration commands."""
|
|
236
199
|
|
|
237
200
|
def __init__(self, sqlspec_config: "AsyncConfigT") -> None:
|
|
238
|
-
"""Initialize
|
|
201
|
+
"""Initialize migration commands.
|
|
239
202
|
|
|
240
203
|
Args:
|
|
241
|
-
sqlspec_config: The
|
|
204
|
+
sqlspec_config: The SQLSpec configuration.
|
|
242
205
|
"""
|
|
243
206
|
super().__init__(sqlspec_config)
|
|
244
207
|
self.tracker = AsyncMigrationTracker(self.version_table)
|
|
@@ -251,7 +214,6 @@ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
|
|
|
251
214
|
directory: Directory path for migrations.
|
|
252
215
|
package: Whether to create __init__.py in the directory.
|
|
253
216
|
"""
|
|
254
|
-
# For async, we still use sync directory operations
|
|
255
217
|
self.init_directory(directory, package)
|
|
256
218
|
|
|
257
219
|
async def current(self, verbose: bool = False) -> None:
|
|
@@ -269,17 +231,14 @@ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
|
|
|
269
231
|
return
|
|
270
232
|
|
|
271
233
|
console.print(f"[green]Current version:[/] {current}")
|
|
272
|
-
|
|
273
234
|
if verbose:
|
|
274
235
|
applied = await self.tracker.get_applied_migrations(driver)
|
|
275
|
-
|
|
276
236
|
table = Table(title="Applied Migrations")
|
|
277
237
|
table.add_column("Version", style="cyan")
|
|
278
238
|
table.add_column("Description")
|
|
279
239
|
table.add_column("Applied At")
|
|
280
240
|
table.add_column("Time (ms)", justify="right")
|
|
281
241
|
table.add_column("Applied By")
|
|
282
|
-
|
|
283
242
|
for migration in applied:
|
|
284
243
|
table.add_row(
|
|
285
244
|
migration["version_num"],
|
|
@@ -288,50 +247,36 @@ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
|
|
|
288
247
|
str(migration.get("execution_time_ms", "")),
|
|
289
248
|
migration.get("applied_by", ""),
|
|
290
249
|
)
|
|
291
|
-
|
|
292
250
|
console.print(table)
|
|
293
251
|
|
|
294
252
|
async def upgrade(self, revision: str = "head") -> None:
|
|
295
253
|
"""Upgrade to a target revision.
|
|
296
254
|
|
|
297
255
|
Args:
|
|
298
|
-
revision: Target revision
|
|
256
|
+
revision: Target revision or "head" for latest.
|
|
299
257
|
"""
|
|
300
258
|
async with self.config.provide_session() as driver:
|
|
301
259
|
await self.tracker.ensure_tracking_table(driver)
|
|
302
260
|
|
|
303
261
|
current = await self.tracker.get_current_version(driver)
|
|
304
262
|
all_migrations = await self.runner.get_migration_files()
|
|
305
|
-
|
|
306
|
-
# Determine pending migrations
|
|
307
263
|
pending = []
|
|
308
264
|
for version, file_path in all_migrations:
|
|
309
265
|
if (current is None or version > current) and (revision == "head" or version <= revision):
|
|
310
266
|
pending.append((version, file_path))
|
|
311
|
-
|
|
312
267
|
if not pending:
|
|
313
268
|
console.print("[green]Already at latest version[/]")
|
|
314
269
|
return
|
|
315
|
-
|
|
316
270
|
console.print(f"[yellow]Found {len(pending)} pending migrations[/]")
|
|
317
|
-
|
|
318
|
-
# Execute migrations
|
|
319
271
|
for version, file_path in pending:
|
|
320
272
|
migration = await self.runner.load_migration(file_path)
|
|
321
|
-
|
|
322
273
|
console.print(f"\n[cyan]Applying {version}:[/] {migration['description']}")
|
|
323
|
-
|
|
324
274
|
try:
|
|
325
|
-
# Execute migration
|
|
326
275
|
_, execution_time = await self.runner.execute_upgrade(driver, migration)
|
|
327
|
-
|
|
328
|
-
# Record in tracking table
|
|
329
276
|
await self.tracker.record_migration(
|
|
330
277
|
driver, migration["version"], migration["description"], execution_time, migration["checksum"]
|
|
331
278
|
)
|
|
332
|
-
|
|
333
279
|
console.print(f"[green]✓ Applied in {execution_time}ms[/]")
|
|
334
|
-
|
|
335
280
|
except Exception as e:
|
|
336
281
|
console.print(f"[red]✗ Failed: {e}[/]")
|
|
337
282
|
raise
|
|
@@ -340,7 +285,7 @@ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
|
|
|
340
285
|
"""Downgrade to a target revision.
|
|
341
286
|
|
|
342
287
|
Args:
|
|
343
|
-
revision: Target revision
|
|
288
|
+
revision: Target revision or "-1" for one step back.
|
|
344
289
|
"""
|
|
345
290
|
async with self.config.provide_session() as driver:
|
|
346
291
|
await self.tracker.ensure_tracking_table(driver)
|
|
@@ -349,30 +294,21 @@ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
|
|
|
349
294
|
if not applied:
|
|
350
295
|
console.print("[yellow]No migrations to downgrade[/]")
|
|
351
296
|
return
|
|
352
|
-
|
|
353
|
-
# Determine migrations to revert
|
|
354
297
|
to_revert = []
|
|
355
298
|
if revision == "-1":
|
|
356
|
-
# Downgrade one step
|
|
357
299
|
to_revert = [applied[-1]]
|
|
358
300
|
else:
|
|
359
|
-
# Downgrade to specific version
|
|
360
301
|
for migration in reversed(applied):
|
|
361
302
|
if migration["version_num"] > revision:
|
|
362
303
|
to_revert.append(migration)
|
|
363
|
-
|
|
364
304
|
if not to_revert:
|
|
365
305
|
console.print("[yellow]Nothing to downgrade[/]")
|
|
366
306
|
return
|
|
367
307
|
|
|
368
308
|
console.print(f"[yellow]Reverting {len(to_revert)} migrations[/]")
|
|
369
|
-
|
|
370
|
-
# Load migration files
|
|
371
309
|
all_files = dict(await self.runner.get_migration_files())
|
|
372
|
-
|
|
373
310
|
for migration_record in to_revert:
|
|
374
311
|
version = migration_record["version_num"]
|
|
375
|
-
|
|
376
312
|
if version not in all_files:
|
|
377
313
|
console.print(f"[red]Migration file not found for {version}[/]")
|
|
378
314
|
continue
|
|
@@ -381,14 +317,9 @@ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
|
|
|
381
317
|
console.print(f"\n[cyan]Reverting {version}:[/] {migration['description']}")
|
|
382
318
|
|
|
383
319
|
try:
|
|
384
|
-
# Execute downgrade
|
|
385
320
|
_, execution_time = await self.runner.execute_downgrade(driver, migration)
|
|
386
|
-
|
|
387
|
-
# Remove from tracking table
|
|
388
321
|
await self.tracker.remove_migration(driver, version)
|
|
389
|
-
|
|
390
322
|
console.print(f"[green]✓ Reverted in {execution_time}ms[/]")
|
|
391
|
-
|
|
392
323
|
except Exception as e:
|
|
393
324
|
console.print(f"[red]✗ Failed: {e}[/]")
|
|
394
325
|
raise
|
|
@@ -402,39 +333,27 @@ class AsyncMigrationCommands(BaseMigrationCommands["AsyncConfigT", Any]):
|
|
|
402
333
|
async with self.config.provide_session() as driver:
|
|
403
334
|
await self.tracker.ensure_tracking_table(driver)
|
|
404
335
|
|
|
405
|
-
# Validate revision exists
|
|
406
336
|
all_migrations = dict(await self.runner.get_migration_files())
|
|
407
337
|
if revision not in all_migrations:
|
|
408
338
|
console.print(f"[red]Unknown revision: {revision}[/]")
|
|
409
339
|
return
|
|
410
340
|
|
|
411
|
-
|
|
412
|
-
clear_sql = SQL(f"DELETE FROM {self.tracker.version_table}")
|
|
341
|
+
clear_sql = sql.delete().from_(self.tracker.version_table)
|
|
413
342
|
await driver.execute(clear_sql)
|
|
414
|
-
|
|
415
343
|
await self.tracker.record_migration(driver, revision, f"Stamped to {revision}", 0, "manual-stamp")
|
|
416
|
-
|
|
417
344
|
console.print(f"[green]Database stamped at revision {revision}[/]")
|
|
418
345
|
|
|
419
|
-
async def revision(self, message: str) -> None:
|
|
346
|
+
async def revision(self, message: str, file_type: str = "sql") -> None:
|
|
420
347
|
"""Create a new migration file.
|
|
421
348
|
|
|
422
349
|
Args:
|
|
423
|
-
message: Description
|
|
350
|
+
message: Description for the migration.
|
|
351
|
+
file_type: Type of migration file to create ('sql' or 'py').
|
|
424
352
|
"""
|
|
425
|
-
# Determine next version number
|
|
426
353
|
existing = await self.runner.get_migration_files()
|
|
427
|
-
if existing
|
|
428
|
-
last_version = existing[-1][0]
|
|
429
|
-
next_num = int(last_version) + 1
|
|
430
|
-
else:
|
|
431
|
-
next_num = 1
|
|
432
|
-
|
|
354
|
+
next_num = int(existing[-1][0]) + 1 if existing else 1
|
|
433
355
|
next_version = str(next_num).zfill(4)
|
|
434
|
-
|
|
435
|
-
# Create migration file
|
|
436
|
-
file_path = create_migration_file(self.migrations_path, next_version, message)
|
|
437
|
-
|
|
356
|
+
file_path = create_migration_file(self.migrations_path, next_version, message, file_type)
|
|
438
357
|
console.print(f"[green]Created migration:[/] {file_path}")
|
|
439
358
|
|
|
440
359
|
|
|
@@ -442,19 +361,17 @@ class MigrationCommands:
|
|
|
442
361
|
"""Unified migration commands that adapt to sync/async configs."""
|
|
443
362
|
|
|
444
363
|
def __init__(self, config: "Union[SyncConfigT, AsyncConfigT]") -> None:
|
|
445
|
-
"""Initialize migration commands with
|
|
364
|
+
"""Initialize migration commands with sync/async implementation.
|
|
446
365
|
|
|
447
366
|
Args:
|
|
448
|
-
config: The SQLSpec configuration
|
|
367
|
+
config: The SQLSpec configuration.
|
|
449
368
|
"""
|
|
450
|
-
|
|
451
369
|
if config.is_async:
|
|
452
370
|
self._impl: Union[AsyncMigrationCommands[Any], SyncMigrationCommands[Any]] = AsyncMigrationCommands(
|
|
453
371
|
cast("AsyncConfigT", config)
|
|
454
372
|
)
|
|
455
373
|
else:
|
|
456
374
|
self._impl = SyncMigrationCommands(cast("SyncConfigT", config))
|
|
457
|
-
|
|
458
375
|
self._is_async = config.is_async
|
|
459
376
|
|
|
460
377
|
def init(self, directory: str, package: bool = True) -> None:
|
|
@@ -513,13 +430,14 @@ class MigrationCommands:
|
|
|
513
430
|
else:
|
|
514
431
|
cast("SyncMigrationCommands[Any]", self._impl).stamp(revision)
|
|
515
432
|
|
|
516
|
-
def revision(self, message: str) -> None:
|
|
433
|
+
def revision(self, message: str, file_type: str = "sql") -> None:
|
|
517
434
|
"""Create a new migration file.
|
|
518
435
|
|
|
519
436
|
Args:
|
|
520
437
|
message: Description for the migration.
|
|
438
|
+
file_type: Type of migration file to create ('sql' or 'py').
|
|
521
439
|
"""
|
|
522
440
|
if self._is_async:
|
|
523
|
-
await_(cast("AsyncMigrationCommands[Any]", self._impl).revision, raise_sync_error=False)(message)
|
|
441
|
+
await_(cast("AsyncMigrationCommands[Any]", self._impl).revision, raise_sync_error=False)(message, file_type)
|
|
524
442
|
else:
|
|
525
|
-
cast("SyncMigrationCommands[Any]", self._impl).revision(message)
|
|
443
|
+
cast("SyncMigrationCommands[Any]", self._impl).revision(message, file_type)
|