mirror of
https://github.com/simonw/sqlite-utils.git
synced 2026-07-23 09:24:31 +02:00
migrate --stop-before an already-applied migration is now an error
The CLI validated --stop-before names against both pending and applied migrations, but Migrations.apply() only looked for the stop name among pending ones - naming an applied migration passed validation and then silently applied every migration after it, the exact outcome the option exists to prevent. apply() now raises ValueError before applying anything if a stop_before name matches an applied migration in its set; names not in the set are still ignored, since unqualified CLI values are offered to every set. The migrate command reports it as a clean error. Refs https://github.com/simonw/sqlite-utils/issues/769#issuecomment-4900034150 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
3de8507c6b
commit
b3aa3f47b7
6 changed files with 79 additions and 3 deletions
|
|
@ -3462,7 +3462,10 @@ def migrate(db_path, migrations, stop_before, list_, verbose):
|
|||
for migration_set in migration_sets:
|
||||
matches = _stop_before_for_migration_set(stop_before, migration_set.name)
|
||||
if isinstance(migration_set, sqlite_utils.Migrations):
|
||||
migration_set.apply(db, stop_before=matches)
|
||||
try:
|
||||
migration_set.apply(db, stop_before=matches)
|
||||
except ValueError as e:
|
||||
raise click.ClickException(str(e))
|
||||
else:
|
||||
# Legacy sqlite-migrate Migrations objects take a single string
|
||||
# for stop_before, not a list
|
||||
|
|
|
|||
|
|
@ -96,14 +96,34 @@ class Migrations:
|
|||
changes are rolled back, no record is written and the migration stays
|
||||
pending. Migrations registered with ``transactional=False`` run
|
||||
outside of a transaction.
|
||||
|
||||
:raises ValueError: if a ``stop_before`` name matches a migration in
|
||||
this set that has already been applied - stopping before it is
|
||||
impossible to honor, and no pending migrations are applied
|
||||
"""
|
||||
self.ensure_migrations_table(db)
|
||||
if stop_before is None:
|
||||
stop_before_names = set()
|
||||
elif isinstance(stop_before, str):
|
||||
stop_before_names = {stop_before}
|
||||
else:
|
||||
stop_before_names = set(stop_before)
|
||||
# A stop_before naming an already-applied migration cannot be
|
||||
# honored - error rather than applying everything after it. Names
|
||||
# not in this set at all are ignored, because unqualified CLI
|
||||
# values are offered to every migration set
|
||||
already_applied = stop_before_names.intersection(
|
||||
migration.name for migration in self.applied(db)
|
||||
)
|
||||
if already_applied:
|
||||
raise ValueError(
|
||||
"Cannot stop before migration{} {} in set '{}' - already "
|
||||
"been applied".format(
|
||||
"s" if len(already_applied) > 1 else "",
|
||||
", ".join(sorted(already_applied)),
|
||||
self.name,
|
||||
)
|
||||
)
|
||||
self.ensure_migrations_table(db)
|
||||
for migration in self.pending(db):
|
||||
name = migration.name
|
||||
if name in stop_before_names:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue