Использование

Программа запускается из командной строки.

Структура репозитория

Пример структуры репозитория:

versions/
    000001#baseline.py
    000001.mybranch.1#alter_some_table.py
    000001.mybranch2.1#alter_some_table.py
    000001.mybranch2.2#alter_another_table.py
    000001.mybranch.2#alter_another_table.py
    000002#create_some_table.py
    meta.yml

В этом примере репозиторий имеет структуру:

ver. 000001
    ver. 000001.mybranch.1
    ver. 000001.mybranch.2
    ver. 000001.mybranch2.1
    ver. 000001.mybranch2.2
ver. 000002

Миграции представляют собой исполняемые файлы на языке python, в которых должно быть объявлено 2 функции up и down. Файл meta.yml содержит в себе служебную информацию такую, как история слияния веток в репозитории, старые имена миграций и во что они были переименованы.

Имя файла состоит из номера версии и краткого описания изменений, разделенных символом #(для того чтоб сохранить корректную сортировку внутри директории). Имя файла может быть вида: 000001.b1.1.b2.2#myname.py, где 001 - корневая версия БД от которой создана ветка b1, 1 - номер версии внутри ветки b1 и от которой создана ветка b2, в которой есть как минимут 2 миграции, и текущий файл описывает изменения для второй версии в ветке b2.

Пример содержимого скрипта миграции:

"""
Name: alter_some_table
Version: 001.mybranch.1
Create Date: 2017-05-29 10:52:46

"""
from ipmt.db import transactional


@transactional()
def up(db):
    """
    :type db: ipmt.db.Database
    """
    db.execute("""\
        ALTER TABLE schema1.table1 ADD COLUMN name text;
    """)


@transactional()
def down(db):
    """
    :type db: ipmt.db.Database
    """
    db.execute("""\
        ALTER TABLE schema1.table1 DROP COLUMN name;
    """)

Команды

Для команд up, down, switch, init, actualize, grant необходимо передавать строку подключения к БД в качестве аргумента командной строки в формате –database username:password@hostname/dbname. Ее можно также передать как переменную окружения IPMT_DSN, тогда аргумент database будет необязательным. Если передать переменную окружения и аргумент командной строки, то будет использоваться аргумент командной строки.

Мирациии по-умолчанию сохраняются в директории versions текущего каталога. Эту директорию можно переопределить передав аргумент –path и указав нужную директорию. Либо директорию можно переопределить передав переменную окружения IPMT_PATH. Если передать переменную окружения и аргумент командной строки, то будет использоваться аргумент командной строки. По сути, переменная окружения переопределяет значение по-умолчанию для аргумента командной строки.

Команды up, down, switch, actualize, grant в качестве агрумента могут принимать аргумент –dry-run, который вместо применения изменений в БД выведет список sql запросов в stdout.

Команды up, down, switch, actualize в качестве аргумента могут принимать аргумент –show-plan или -s, который вместо применения миграций отобразит последовательность миграций, которые необходимо выполнить для выполнения команды. Т.е. поведение такое же как у –dry-run, но в виде плана. Например:

$ IPMT_DSN=svc_ars@localhost/ars6 ipmt up -s 000003
0 -> 000001(xUp) -> 000002(xUp) -> 000003(Up)

Означает, что текущая версия БД 0 и для обновления до версии 000002 необходимо выполнить обновление до версии 000001, затем до версии 000002 и затем до версии 000003. xUp - означает, что обновление будет происходить в транзакции. Up - означает, что миграция будет выполняться не в транзакции.

Перечень команд инструмента миграций:

version

Отображает установленную версию инструмента миграций IPMT.

$ ipmt version
1.0.0

Это означает, что текущая версия IPMT 1.0.0.

init

Отображает установленную версию инструмента миграций IPMT.

$ ipmt init
$ # или с созданием baseline версии
$ IPMT_DSN=username@hostname/dbname ipmt init

При инициализации создается директория versions. Если в качестве аргумента передать строку подключения к БД, то будет создана baseline миграция с текущей структурой БД, и для указанной БД будет установлена версия в таблице public.ipmt_version

create

Создает в директории path новый файл миграции.

$ ipmt create alter some table
Created versions/000007#alter_some_table.py

Инструмент поддерживает работу в разных ветках. Именами веток могут быть любые символы из a-zA-Z0-9_. Для создания новой версии в отдельной ветке необходимо передать аргументом –branch полный путь к ветке, например:

$ ipmt create --branch 000001.mybranch alter another table
Created versions/000001.mybranch.1#alter_another_table.py

В этом примере создается новая версия БД 000001.mybranch.1, которая идет следом за 000001. При применении такой миграции будет произведено обновление до версии 000001, а затем до версии 000001.mybranch.1. Поэтому, при создании верки необходимо, чтоб родительская версия, обязательно присутствовала в репозитории. В противном слючае инструмент не будет работать.

head

Отображает последнюю версию в репозитории. Команда может принимать аргумент –branch. В этом случае будет отображена последняя версия в указанной ветке.

$ ipmt head
000001
$ ipmt head --branch 000001.mybranch
000001.mybranch.2
show

Отображает все миграции репозитория в директории path

$ IPMT_DSN=username@hostname/dbname ipmt show
Repository:
  ver. 000001
      ver. 000001.mybranch.1
  ver. 000002
  ver. 000003

Жирным будут отмечены те миграции, которые применены к указанной БД

current

Отображает текущую версию БД

$ IPMT_DSN=username@hostname/dbname ipmt current
000006
up

Обновление БД к указанной версии. Если целевая версия не указана, то будет выполнено обновление до последней версии.

$ # обновление до версии 000003
$ IPMT_DSN=username@hostname/dbname ipmt up 000003
000003

$ # обновление до последней версии
$ IPMT_DSN=username@hostname/dbname ipmt up
000007
down

Откат БД к укзанной версии. Если целевая версия не указана, то будет произведен откат на одну версию назад. Если передать целевую версию 0, то будет произведен откат к начальному состаянию БД, т.е. будут отменены все миграции.

$ # откат к версии 000003
$ IPMT_DSN=username@hostname/dbname  ipmt down 000003
000003

$ # откат на одну версию назад
$ IPMT_DSN=username@hostname/dbname ipmt down
000002

$ # откат к начальному состоянию
$ IPMT_DSN=username@hostname/dbname  ipmt down 0
0
switch

Откат или обновление БД к указанной версии. Если в качестве целевой версии передать 0, то произойдет откат всех миграций.

$ # обновление/откат до версии 000007
$ IPMT_DSN=username@hostname/dbname  ipmt switch 000007
000007

$ # откат всех миграций
$ IPMT_DSN=username@hostname/dbname ipmt switch 0
0
rebase

Перенос миграций из указанной ветки в родительскую.

$ ipmt rebase 000001.mybranch
Moving 000001.mybranch.1 to 000007
actualize

Исправляет неверную последовательность миграций в БД путем отката и обновления миграций.

$ IPMT_DSN=username@hostname/dbname ipmt actualize
000007

Неверная последовательность миграций может получиться после выполнения команды rebase.

Например, в репозитории имеются миграции:

versions/
    000001#baseline.py
    000001.mybranch.1#alter_some_table.py
    000002#create_some_table.py

Текущая версия БД: 000001.mybranch.1. Т.е. в БД применены миграции 000001 и 000001.mybranch.1. Выполняется слияние ветки mybranch с родительской веткой:

$ ipmt rebase 000001.mybranch

В результате получается новая структура репозитория:

versions/
    000001#baseline.py
    000002#create_some_table.py
    000003#alter_some_table.py

Т.е. версия 000001.mybranch.1 превращается в 000003. При этом текущая версия БД 000001.mybranch.1. В новой схеме репозитория эта версия называется 000003(IPMT понимает это по файлу meta.yml). Т.о. получается, что у БД неконсистентное состояние(пропущена миграция 000002). В этом состоянии команды: up, down и switch будут недоступны и IPMT, при их вызове, будет выдавать ошибку. Для того, чтоб исправить это, необходимо выполнить команду actualize. Эта команда произведет откат миграции 000003(ранее известной как 000001.mybranch.1) и затем произведет обновление до версии 000002 и 000003 последовательно.

grant

Применяет привелегии из указанного yaml файла или выгружает в файл привелегии привелегии из указанной БД

$ # выгружает привелегии всех пользователей БД в файл permissions.yml
$ IPMT_DSN=svc_ars@localhost/ars6 ipmt grant -o permissions.yml

$ # выгружает привелегии пользователей user1 и user2 в файл, за
$ # исключением привелегий на объекты в схеме myschema
$ IPMT_DSN=svc_ars@localhost/ars6 ipmt grant --roles user1 user2 \
>  --exclude '~myschema\..*' -o permissions.yml

$ # применение привелегий из файла permissions.yml к указанной БД
$ IPMT_DSN=svc_ars@localhost/ars6 ipmt grant -i permissions.yml

$ # применение привелегий из файла, за исключением привелегий
$ # пользователей user3, user4 и за исключением объектов test.mytable и
$ # test.mytable_seq
$ IPMT_DSN=svc_ars@localhost/ars6 ipmt grant --roles user3 user4 \
>  --exclude test.mytable test.mytable_seq -i permissions.yml

Пример yaml файла:

roles:
  - user1
  - user2
objects:
  public:
    user1: usage
    user2: usage
  public.some_table:
    user1: select
  ~public\..*:
    user2: all
exclude:
  ~private_schema\..*
dump

Формирует дамп схемы БД указанной версии с помощью pg_dump. В качестве аргумента можно передать –docker-image и –docker-version с указанием требуемой версии postgresql или greenplum. Список доступных доступен на docker hub. Версии alpine не поддерживаются. Если не указан путь к файлу для сохранения дампа, то дамп будет выведен в стандартный вывод.

# postgres
$ ipmt dump -o dump.sql --docker-image postgres --docker-version 9.5
# greenplum
$ ipmt dump -o dump.sql --docker-image pivotaldata/gpdb-dev --docker-version ubuntu-16.04

Транзакционность

По-умолчанию команда ipmt create создает транзакционные миграции. Если требуется создать миграцию, которая не должна выполняться в транзакции, то необходимо у функции up или down удалить декоратор @transactional.

По-умочанию IPMT использует уровень изоляции READ COMMITTED. Если необходимо изменить его, то нужно прередать в декоратор параметр isolation_level, установленный в требуемый уровень.

Например:
from ipmt.db import transactional, SERIALIZABLE


@transactional(isolation_level=SERIALIZABLE)
def up(db):
    """
    :type db: ipmt.db.Database
    """
    db.execute(
        """\
        CREATE SCHEMA myschema;
    """
    )


@transactional(isolation_level=SERIALIZABLE)
def down(db):
    """
    :type db: ipmt.db.Database
    """
    db.execute(
        """\
        DROP SCHEMA myschema;
    """
    )

Транзакционные миграции с одинаковым уровнем изояции, идущие в плане выполнения последовательно друг за другом, будут выполняться в одной транзакции. В противном случае после транзации будет выполнен COMMIT, т.о. зафиксировав версию БД.

Работа с ветками

Ветки предназначены для разработки нескольких новых функций, которые незвестно в каком порядке будут обновляться в prod-среде. В одной базе данных может быть применена только одна конкретная версия из конкретной ветки. Создание новой ветки производится командой create с аргументом –branch. Перенос изменений из ветки происходи при помощи команды rebase. Исправление последствий некорректного слияния веток производится командой actualize.