项目作者: ogheorghies

项目描述 :
Migration tool ◆ Database schema migration ◆ PostgreSQL
高级语言: Shell
项目地址: git://github.com/ogheorghies/clunky-migration-tool.git
创建时间: 2016-04-30T16:33:11Z
项目社区:https://github.com/ogheorghies/clunky-migration-tool

开源协议:ISC License

下载


Clunky migration tool

A version migration tool, written in Bash - fast and well tested. May be used for database schema and data migrations.

Upgrade a PostgreSQL database to the most recent schema, with:

  1. clunky-migration-tool --mode filetree:psql -D TARGET="psql postgres://user@localhost:5432/app"

An example SQL file tree that defines the upgrade process is given below:

  1. .cmt.env
  2. v0001
  3. from-scratch
  4. 000-schema.sql
  5. 010-data.sql
  6. v0002
  7. from-previous
  8. 000-patch-schema.sql
  9. 010-update-data.sql
  10. from-scratch
  11. 000-schema.sql
  12. 010-data.sql

To display the files involved in a migration from version v3 to version v5 (a “dry run”), use:

  1. clunky-migration-tool -m filetree:debug -D TARGET_VERSION=v3 v5

Command line

  1. clunky-migration-tool
  2. -v|--verbose enable verbose mode
  3. -m|--mode source:target upgrades taken from source type and applied to target type
  4. -D name=value defines a variable "CMT_${name}" set to "${value}"
  5. -C directory run in the specified directory
  6. <to-version> "last" or empty (equivalent), or a given version (e.g. "v5")

Overview

“Clunky migration tool” uses a “source” to update a “target”. If a target is at version 3, but a source contains
upgrades up to version 5, an upgrade from version 3 to version 4 may be first applied, followed by
an upgrade from version 4 to version 5. A fresh target may be directly populated to the desired version, without
going through incremental upgrades.

In common scenarios, a target may be a database, and a source may be a file tree containing SQL files, organized
by version.

However, this project does not prescribe what a target or a source may be. Instead, it defines the interfaces that
sources and targets must implement.

Sources and targets

A source must define the following interface:

  1. source_initialize
  2. source_get_version ${VERSION_NAME}
  3. source_get_changes ${VERSION_START} ${VERSION_END}

The function source_initialize is optional, and may perform initialization tasks.

The function source_get_version returns the actual identifier that corresponds to a given version name.
An actual version identifier must be returned for the special version names first and last - for example v1 and v5,
respectively.
If the given ${VERSION_NAME} cannot be resolved, the function must exit with an error code. If ${VERSION_NAME}
is scratch, then scratch must be returned. The names first, last, and scratch have a special meaning
and cannot be used as actual application versions.

The function source_get_changes returns a set of handles to patches that need to be applied in order to migrate
from ${VERSION_START} to ${VERSION_END} (both parameters are passed through source_get_version).
If ${VERSION_START} is "scratch", the function may return the changes needed to directly create the
target at ${VERSION_END}. If downgrades are not supported, namely when ${VERSION_END} is before ${VERSION_START},
the function must return an empty string.

A target must define the following interface:

  1. target_initialize
  2. target_get_current_version
  3. target_accept_changes ${VERSION}

The function target_initialize is optional, but it is likely needed to perform initialization tasks.

The function target_get_current_version returns the current version of the target, or scratch if the
target has not been populated with any version.

The function target_accept_changes transactionally applies changes to the target, and sets the
current version to its parameter ${VERSION}. It must allow the output of source_get_changes to be piped
into it. Moreover, it must not change the version if the set of changes provided by the source is empty.

For example, the following code template is expected to work.

  1. source_initialize
  2. target_initialize
  3. BEGIN=$(target_get_current_version)
  4. END=$(source_get_version "last") # or other version to upgrade to
  5. source_get_changes ${BEGIN} ${END} | target_accept_changes ${END}

Custom sources and targets

The type of the source and the type of the target are specified in the mode parameter, -m, and are separated by a
column. In the example below, the source is of type filetree, and the target of type psql.

  1. clunky-migration-tool -m filetree:psql v5

These two types are standard, and their interfaces are defined within the library, respectively, at:

  1. ${LIBRARY_ROOT}/source/filetree/main.sh
  2. ${LIBRARY_ROOT}/target/psql/main.sh

User-specified types can be used and implemented as follows:

  1. ls -l ./clunky-migration-tool/target/sqlite/main.sh
  2. clunky-migration-tool -m filetree:sqlite v5

The library first searches for user-defined source and target types, thus unintentional name clashes with
library-defined types are avoided.

Postgres migration example

This library was initially written to populate and upgrade Postgresql databases from SQL files grouped
by version into directories. In this scenario, the source is of type "filetree", and the target is
of type "psql". The corresponding implementations and tests are available in the ${LIBRARY_ROOT}/source/filetree and
${LIBRARY_ROOT}/target/psql directories, respectively.

A source of type filetree may contain directories and files as follows:

  1. v0001-0.0.0
  2. from-scratch
  3. 000-schema.sql
  4. 010-data.sql
  5. v0002-0.0.1
  6. from-previous
  7. 000-patch-schema.sql
  8. 010-update-data.sql
  9. from-scratch
  10. 000-schema.sql
  11. 010-data.sql

A target of type psql stores the current version into a specially created table, executes the SQL code
contained in the SQL files whose names are supplied by the source, and updates the current version only if everything
goes well during the upgrade.