garfield.database.migrations Package

garfield.database.migrations contains the flexible PostgreSQL database schema migration support.

exception garfield.database.migrations.SqlMigrationException

Bases: exceptions.Exception

garfield.database.migrations.check_repository_has_patches(sess, repository_name, patch_names)

garfield.database.migrations.db Module

Contains the methods and classes used for interaction with the database and for storage of the repository’s state.

class garfield.database.migrations.db.AppliedPatch(repository_id, patch_name)

Bases: sqlalchemy.ext.declarative.api.Base

@DynamicAttrs

static get_all(sess, repository_name)

Returns the list of applied patches for repository repository_name.

static is_applied(sess, repository_name, patch_name)
patch_name
repository_id
class garfield.database.migrations.db.Repository(repository_id=None, repository_name=None)

Bases: sqlalchemy.ext.declarative.api.Base

@DynamicAttrs

applied_patches
repository_id
repository_name
garfield.database.migrations.db.create_tables(bind, checkfirst=True)
garfield.database.migrations.db.drop_tables(bind, checkfirst=True)
garfield.database.migrations.db.execute_script(sess, sql_text)

Execute an SQL script on a session. Works around limitation in SQLite back-end, which doesn’t allow multiple statements, by using the SQLite-specific executescript()-method, when available. The approach was borrowed from migrate.versioning.script.sql.

garfield.database.migrations.driver Module

class garfield.database.migrations.driver.Driver(sess, repo_name, patch_repo)

Bases: object

Drives the upgrade and downgrade of a repository by applying or downgrading patches.

applied_patches
calculate_minimal_deps(patches)

Returns the minimal set of patches that is equivalent to patches. The returned list will be equal to or smaller than patches, due to potentially existing dependencies between the patches.

downgrade(execute_sql=True)
downgrade_patches(patches, execute_sql=True)
init_repo(patches=None)
renew_patches(patches)

Performs downgrade of a set of patches and then upgrades all patches again. Useful if one or more patches were upgraded and need to be refreshed.

test_upgrade()

Performs upgrade and downgrade more than once to help detect incomplete downgrade scripts.

test_upgrade_patches(patches)

Performs upgrade and downgrade on specific patches more than once to help detect incomplete downgrade scripts.

unapplied_patches
uninit_repo()
upgrade(execute_sql=True)
upgrade_patches(patches, execute_sql=True)
exception garfield.database.migrations.driver.PatchFailedException(operation, details)

Bases: exceptions.Exception

Is raised when an SQL snippet fails to apply.

garfield.database.migrations.driver_test Module

Tests the garfield.database.migration.driver module.

class garfield.database.migrations.driver_test.SQLiteForeignKeysListener

Bases: sqlalchemy.interfaces.PoolListener

Listens for DB connections and activates foreign key checks. Will not fail on older SQLite versions, because it uses PRAGMA.

connect(dbapi_con, _)
class garfield.database.migrations.driver_test.TestDriver

Bases: object

assert_table_exists(table_name)
assert_table_not_exists(table_name)
assert_tables_exist(table_names)
assert_tables_not_exist(table_names)
init_repo()
setUp()
test_calculate_minimal_deps()
test_downgrade()
test_downgrade_patch()
test_empty_upgrade()
test_init()
test_uninit()
test_uninit_other_repo()
test_uninit_with_patches()
test_upgrade_from_empty()
test_upgrade_to_patch()

garfield.database.migrations.patch Module

Provides support for parsing SQL patch files (snippets).

class garfield.database.migrations.patch.DirPatchLoader

Bases: object

Loads SQL patches from a directory-based structure.

A patch is represented by a directory, where the directory’s name is equal to the patch’s name. Within the directory, there are up to three files: depends_on, upgrade_sql and downgrade_sql. The depends_on file contains a single patch name per line. Each patch name represents a dependency. The upgrade_sql and downgrade_sql files contain SQL code for upgrading to the patch or downgrading from the patch (respectively).

Any of the files can be ommitted and any additional files within the directory will be ignored.

is_patch(patch_path)

Returns true in case patch_path is a valid path to a patch.

load_patch(patch_path)

Returns the Patch loaded from the path patch_path.

class garfield.database.migrations.patch.DirPatchRepositoryLoader(patch_loader)

Bases: object

Loads patches from directory of patches.

load_repo(repo_dir)

Returns a new repo with patches loaded from repo_dir.

class garfield.database.migrations.patch.Patch(name, depends_on_names=None, upgrade_sql=None, downgrade_sql=None, origin=None)

Bases: object

A Patch object represents a single SQL patch. Such a patch contains SQL directives to transform the database’s current schema to a new schema and possibly directives to revert such a step.

The patch may contain one or more dependencies, which means patches that need to be applied before this patch can be applied itsself. Dependencies may be optional, in which case it is acceptable for the patch to not exist. In case optional, depended-upon patches exist and aren’t applied, they need to be applied before this patch.

resolve_dependencies(patch_repo)

Search for the depended-on patches and populate depends_on with references to the patch instances.

patch_repo
repository of patches in which the dependencies are searched for.

Throws PatchNotFound in case a dependency can’t be resolved.

exception garfield.database.migrations.patch.PatchNotAccessible

Bases: exceptions.Exception

exception garfield.database.migrations.patch.PatchNotFound

Bases: exceptions.Exception

class garfield.database.migrations.patch.PatchRepository

Bases: object

Holds all patches for a certain repository that could potentially be applied.

add_patch(patch)
add_patches(*patches)
lookup_patch_name(patch_name)
lookup_patch_names(patch_names)
resolve_dependencies()
garfield.database.migrations.patch.generate_downgrade_plan(applied_patches, to_be_removed_patches)

Return the ordered list of patches to uninstall. The list will also contain any patches that depend on the patches that are supposed to be uninstalled according to to_be_removed_patches.

garfield.database.migrations.patch.generate_upgrade_plan(applied_patches, to_be_applied_patches)

Return the ordered list of patches to be installed, so that to_be_applied_patches, the list of patches that should be present after the upgrade, is installed, including all dependencies.

Note: Assumes that the patch dependency graph is acyclic, otherwise the method will enter an infinite loop.

garfield.database.migrations.patch_test Module

Tests the garfield.database.migrations.patch module.

class garfield.database.migrations.patch_test.TempDirTestCase

Bases: object

setUp()
tearDown()
tmp_dir_path = None
class garfield.database.migrations.patch_test.TestDirPatchLoader

Bases: garfield.database.migrations.patch_test.TempDirTestCase

test_dir_load()
class garfield.database.migrations.patch_test.TestDirPatchRepositoryLoader

Bases: garfield.database.migrations.patch_test.TempDirTestCase

test_repo_dir_load()
garfield.database.migrations.patch_test.test_complex_upgrade()
garfield.database.migrations.patch_test.test_complex_upgrade_from_empty()
garfield.database.migrations.patch_test.test_create_empty_patch()

Check whether creating a Patch instance works.

garfield.database.migrations.patch_test.test_downgrade()
garfield.database.migrations.patch_test.test_lookup_patches()
garfield.database.migrations.patch_test.test_resolve_deps()

Check that patch name lookup via the repo works.

garfield.database.migrations.patch_test.test_upgrade_from_empty()