一个高度个性化的postgresql迁移工具
pghops的Python项目详细描述
pghops是一个命令行postgresql模式迁移实用程序 用python。它旨在成为 PostgreSQL。
- 功能
- 演示
- 使用概述
- 安装
- 最佳实践
- 选项
- 管理索引
- 单元测试
- 常见问题
- 杂项
- 许可证 < > >
- 简单版本文件语法:pghops版本文件是yaml文件 具有表示一个或多个sql的目录和值的键 文件名。
- 使用psql执行脚本:pghops使用psql执行所有sql, 利用PostgreSQL客户端的广泛功能。使用 脚本中的任何psql命令。
- 单元测试框架:pghops自带单元 测试框架。不要再找借口跳过sql单元测试了!
- 全部或无迁移:将整个迁移打包到 单个事务或其自身事务中的每个迁移脚本。
- 保存到版本表的所有SQL命令pghops保存所有SQL 在迁移到其版本表期间执行。让审计员 快乐!
- 设置使用无密码身份验证运行pghops的用户, 例如信任或 对等体最好的 然后在与PostgreSQL相同的框中运行pghops。
- 使用a密码 文件 < > >
- 命令行参数
- 由
--options file
命令行指定的文件中的属性 参数。 - 环境变量。
<;cluster dir>;/<;db>;pghops.properties中的属性
<;cluster dir>;pghops.properties中的属性
pghops/conf/default.properties中的属性 < > >
。 检查表名是否有效。然后检查属性文件应采用yaml格式,并包含键/值对。
pghops处理属性文件中仅大小写不同的选项 下划线与连字符的用法相同。例如:
wrap-all-in-transaction wrap_all_in_transaction Wrap_All_In_Transaction
都指同一个选项。环境变量应使用 下划线而不是连字符,全部大写,前缀为 普霍普斯例如,对于 上面的wrap all-in事务属性是 pghops_wrap_all_in_事务。
psql的环境变量也有效。
pghops选项如下:
cluster_directory-pghops的第一个参数。默认为 当前工作目录。包含数据库的基目录 SQL.
dbname-默认情况下,pghops将迁移集群中的所有dbs 目录。使用此选项仅更新指定的数据库。
群集映射-指向包含群集名称映射的yaml文件的路径 目录。然后可以将集群名称作为 群集目录参数而不是目录。
干运行-不要执行迁移,只打印 会被执行的。
详细程度-详细程度。默认的、冗长的或简洁的。 "terse"只打印错误。verbose"回送所有已执行的SQL。
psql_base_args-"base"参数到psql。默认设置为"--set on_error_stop=1——无psqlrc"。与此结合使用 psql_参数。
psql_参数-要提供给psql的参数列表,例如 --主机,-端口等。
db_conninfo-指定连接参数的另一种方法 PSQL.
在事务中包装所有的事务-如果为true,则默认情况下,pghops将包装 单个事务中的整个迁移。
将每个版本的脚本包装到事务中 在自己的事务中运行,而不是在整个迁移过程中运行。
如果无法连接,则失败-如果为true,则默认情况下,pghops将升高 无法连接到数据库服务器时出错。
如果处于待机状态,则失败-如果为true,则默认情况下,pghops将引发错误 如果它可以连接到数据库服务器,但数据库服务器位于 待机模式。
将SQL保存到版本表-如果为true,则默认情况下,pghops将保存 对pghops.version表执行的所有SQL。考虑设置为 对于包含敏感信息的大型迁移或迁移,为False。
保存索引-如果为true,则默认情况下,pghops会扫描SQL代码 创建索引语句并将其保存到pghops.index表中。见 下面是详细信息。
迁移文件-使用此选项仅执行提供的文件 而不是版本目录中的所有文件。
脚本后缀-不区分大小写的逗号分隔列表 迁移文件名必须匹配的后缀 执行。默认为YML和YAML。
选项文件-提供时,还加载选项(续)内部 此属性文件。
管理索引
随着模式的发展,您可能会发现需要在 大型现有表。如果在迁移期间创建索引是 不可接受,您可以让pghops为您管理索引,以便 以后异步创建它们。
通过将选项
save indexes
设置为true(默认值),pghops 将扫描SQL代码中的CREATE INDEX语句并将任何保存到pghops.index
。对于要跟踪索引的pghops,请确保以下各项:- index语句位于后缀为".sql"的文件中。
- 整个索引语句位于一行。
- index语句从行的第一列开始。普霍普斯 忽略任何前面有空格的索引语句。如果有用, 例如,有一个函数可以创建临时表并 在所述临时表上定义索引,不希望pghops 管理此索引。
- 在索引定义中使用完全限定的表名 (schema.table_name)。创建索引脚本首先检查 在执行index语句之前存在表,并且 当pghops保存索引时,它不会分析任何前面的集合 路径语句。如果不使用完全限定的表名 pghops不会保存索引。
- 如果不存在,则语句使用
,以便可以多次运行 不会导致错误。
- 索引扫描并不完美。如果你使用非常规 需要引用名称的索引或表的名称, pghops无法正确分析语句。 < > >
通过扫描代码中的索引,可以在 文件作为它们的表和pghops将它们添加到pghops.index 在下次迁移过程中自动执行。
对于
pghops.index
中的每条记录,将首先创建pghops\u indexenabled
标志,如果设置,则在定义中执行sql。这个 脚本根据cpu核的数量并行运行,尽管 这种优势在最近的postgresqls中得到了缓解,postgresqls可以创建 自动并行的单个索引。
单元测试
使用
pghops_test
命令,您可以创建并运行简单的sql 单元测试。您需要安装Docker,因为测试是在 PostgreSQL Docker容器。工作原理如下:- 在您的数据库目录中创建名为
tests
的目录。 - 在
tests
目录中,创建以\u test.sql
。通常您希望文件名包含 用于排序的编号,例如01_base_test.sql
- 运行pghops_test generate。这将启动PostgreSQL Docker
容器,运行迁移,然后为
每个sql测试文件。例如,对于测试文件
01_base_test.sql
它将生成01_base_expected.txt
- 查看生成的预期文件。确保没有主机或 特定于环境的输出,如主机名或时间戳。
- 随着模式的发展,您可以运行
pghops_test run
来运行 单元测试。它将启动一个新的PostgreSQL Docker容器,运行 迁移,执行单元测试sql文件并比较 输出到预期文件的内容。 < > >
如果创建了许多测试,则可以通过创建
tests
目录中的子目录。每个套房都在 它自己的码头集装箱。测试选项
pghops_test
的选项加载方式与pghops
除了在中查找名为pghops test.properties
的属性文件 测试和测试套件目录。仅测试特定属性 在运行测试时应用,而不是在运行初始迁移时应用 在Docker公司Nestor,命令-唯一需要的选项。要么
运行
要么生成
测试-提供时,仅运行特定测试。您可以指定 套件名称、特定文件或套件中的特定文件,如 我的套房/my-file_test.sql。
docker_name-要使用的docker映像的名称。默认为 PostgreSQL。
Docker_标记-可选的Docker标记。如果省略,则使用最新版本。
docker_name-要创建的docker容器的名称。默认值 到PGHOPS PostgreSQL。
跳过Docker关闭-在 运行测试。
忽略空白-是否忽略空白 将输出与预期文件进行比较。
psql_base_migrations_args-类似于psql_base_args,但仅限于 在运行迁移时应用。
pghops_test
还接受以下相同的参数 到pghops
参数:群集目录,数据库名,psql_base_args, psql_参数,数据库连接信息,详细信息,选项文件
FAQ
pghops代表什么?
或者PostgreSQL高度自以为是的迁移。或者你可以用 pghops"跳"到下一个数据库版本。选吧。
为什么要使pghops postgressql特定?为什么不使用python数据库api的驱动程序使其与数据库无关呢?
通过使用psql,您可以利用它的所有功能-您的sql可以包含 任何psql元命令,它不能与适配器 作为心理医生。
是否支持回滚迁移?
没有内置的支持。在完美的世界中,每个数据库迁移脚本 将附带一个回滚脚本。但如果出了什么事 在生产上,你需要后退,你真的觉得 是否可以轻松执行回滚脚本?你都试过了吗 回滚脚本可能遇到的状态?
以我的经验来看,不经常需要回滚 必须在 可能会发生变化。但是,如果你坚持要回滚 脚本,最初可以在相同版本中创建回滚文件 目录并用非yaml后缀命名,例如 回滚。然后当你需要回滚时,用
--运行回滚脚本的迁移文件
选项。如果你愿意 从pghops.version表中删除记录,必须执行以下操作 手动操作。我的数据库之间有依赖关系,我需要pghop以特定顺序执行迁移。
在
cluster_目录中,创建名为
databases
的文件并列出 数据库按所需顺序排列。如果我需要执行不在事务中的sql,会发生什么情况?
最好在前面包含一个
commit
语句 不能在事务内部运行的sql,后跟begin
语句以启动新事务。您还可以省略事务 对于此pghops,通过设置选项wrap all in transaction和 将事务中的每个版本包装为false。在进行单元测试时,我不想在检查更改时重新运行整个迁移。
将skip_docker_shutdown设置为true,并在命令中提供测试名称。例子:
pghops_test--跳过Docker shutdown t generate 01_base_test.sql
然后下次重新运行上述命令时,它将立即 对Docker容器执行01_base_test.sql,而不必 重新启动。
其他
pghops是在gnu/linux上开发和测试的。随时报告错误 并提供修补程序。
许可证
GPLV3。See复制
推荐PyPI第三方库
功能
演示
下面的终端会话演示如何创建名为mydb的数据库
包含两个表:
帐户
和帐户电子邮件
。我们将
创建一个简单的测试,以确保您不能在
帐户电子邮件
表。然后我们将为
创建帐户和另一个单元测试。
[mycluster]$ # Create a directory named after the database, along with a script to create the database.
[mycluster]$ mkdir mydb
[mycluster]$ echo "create database mydb;" > mydb/create_database.sql
[mycluster]$ # Create a directory to hold our table definitions.
[mycluster]$ mkdir -p mydb/schemas/public/tables
[mycluster]$ # Create SQL files containing our table definitions.
[mycluster]$ cat - > mydb/schemas/public/tables/account.sql <<EOF
> create table if not exists public.account (
> account_id bigserial primary key
> );
> EOF
[mycluster]$ cat - > mydb/schemas/public/tables/account_email.sql <<EOF
> create table if not exists public.account_email (
> account_email_id bigserial primary key
> , account_id bigint not null references account
> , email text not null
> );
> EOF
[mycluster]$ # Create our first migration file
[mycluster]$ mkdir mydb/versions
[mycluster]$ cat - > mydb/versions/0001.0001.0001.init.yml <<EOF
> public/tables:
> - account
> - account_email
> EOF
[mycluster]$ # Create our first unit test to ensure we cannot insert NULLs into account_email.email
[mycluster]$ mkdir mydb/tests
[mycluster]$ cat - > mydb/tests/01_account_email_test.sql <<EOF
> insert into account values (default);
> insert into account_email (account_id, email) values ((select max(account_id) from account), null);
> EOF
[mycluster]$ # Generated the 'expected' file and review it.
[mycluster]$ pghops_test generate
2019-02-23 14:18:42.452426: Looping through tests in /tmp/mycluster/mydb/tests
2019-02-23 14:18:42.458661: Stopping Postgres pghops-postgresql.
2019-02-23 14:18:42.476721: Starting Postgres pghops-postgresql postgres.
2019-02-23 14:18:45.632209: Done starting postgres pghops-postgresql.
2019-02-23 14:18:45.673046: Migrating cluster /tmp/mycluster.
2019-02-23 14:18:45.673440: Migrating database mydb
2019-02-23 14:18:45.674398: Database mydb does not exist. Creating it with /tmp/mycluster/mydb/create_database.sql.
create database mydb;
CREATE DATABASE
...
<output elided>
...
2019-02-23 14:18:46.106990: Done migrating database mydb
2019-02-23 14:18:46.107123: Done all migrations.
2019-02-23 14:18:46.132066: Generated 01_account_email_test.sql expected file.
2019-02-23 14:18:46.132145: Stopping Postgres pghops-postgresql.
2019-02-23 14:18:48.449079: Done generating expected files!
[mycluster]$ # Review the expected file.
[mycluster]$ cat mydb/tests/01_account_email_expected.txt
insert into account values (default);
INSERT 0 1
insert into account_email (account_id, email) values ((select max(account_id) from account), null);
ERROR: null value in column "email" violates not-null constraint
DETAIL: Failing row contains (1, 1, null).
[mycluster]$ # Looks good! We received the error as expected. As a sanity check, run the tests and they should succeeded
[mycluster]$ pghops_test run
...
<output elided>
...
2019-02-23 14:22:31.269604: All tests passed!
[mycluster]$ # Lets run our first migration against a real db!
[mycluster]$ pghops
2019-02-23 14:23:47.225273: Migrating cluster /tmp/mycluster.
2019-02-23 14:23:47.225539: Migrating database mydb
2019-02-23 14:23:47.226114: Database mydb does not exist. Creating it with /tmp/mycluster/mydb/create_database.sql.
CREATE DATABASE
BEGIN
CREATE SCHEMA
CREATE TABLE
CREATE TABLE
CREATE INDEX
INSERT 0 1
CREATE TABLE
CREATE TABLE
INSERT 0 1
COMMIT
2019-02-23 14:23:47.827395: Done migrating database mydb
2019-02-23 14:23:47.827536: Done all migrations.
[mycluster]$ # Check the version table if you wish
[mycluster]$ psql --dbname=mydb --command="select major, minor, patch, label, file_name from pghops.version;"
major | minor | patch | label | file_name
-------+-------+-------+-------------+---------------------------------
0000 | 0000 | 0000 | pghops-init | 0000.0000.0000.pghops-init.yaml
0001 | 0001 | 0001 | init | 0001.0001.0001.init.yml
(2 rows)
[mycluster]$ # Create a function that creates accounts.
[mycluster]$ mkdir mydb/schemas/public/functions
[mycluster]$ cat - > mydb/schemas/public/functions/create_account.sql <<EOF
create or replace function public.create_account
> (
> p_email text
> )
> returns bigint
> language plpgsql
> as \$\$
> declare l_account_id bigint;
> begin
>
> insert into account (account_id) values (default) returning account_id into l_account_id;
>
> insert into account_email (account_id, email) values (l_account_id, p_email);
>
> return l_account_id;
>
> end\$\$;
> EOF
[mycluster]$ # Next create our second migration file.
[mycluster]$ cat - > mydb/versions/0001.0002.0001.create_account.yml <<EOF
> public/functions: create_account
> EOF
[mycluster]$ # Create our second test
[mycluster]$ echo "select create_account('x@example.com');" > mydb/tests/02_create_account_test.sql
[mycluster]$ pghops_test generate
2019-02-23 14:35:44.742060: Looping through tests in /tmp/mycluster/mydb/tests
2019-02-23 14:35:44.748353: Stopping Postgres pghops-postgresql.
2019-02-23 14:35:44.764216: Starting Postgres pghops-postgresql postgres.
2019-02-23 14:35:47.726734: Done starting postgres pghops-postgresql.
2019-02-23 14:35:47.767687: Migrating cluster /tmp/mycluster.
2019-02-23 14:35:47.768116: Migrating database mydb
2019-02-23 14:35:47.769223: Database mydb does not exist. Creating it with /tmp/mycluster/mydb/create_database.sql.
...
<output elided>
...
2019-02-23 14:35:48.230893: Done migrating database mydb
2019-02-23 14:35:48.230981: Done all migrations.
2019-02-23 14:35:48.251236: Generated 01_account_email_test.sql expected file.
2019-02-23 14:35:48.269521: Generated 02_create_account_test.sql expected file.
2019-02-23 14:35:48.269596: Stopping Postgres pghops-postgresql.
2019-02-23 14:35:50.672626: Done generating expected files!
[mycluster]$ cat mydb/tests/02_create_account_expected.txt
select create_account('x@example.com');
create_account
----------------
2
(1 row)
[mycluster]$ # Our new db function works! Lets run a migartion to update our db
[mycluster]$ pghops
2019-02-23 14:37:13.523780: Migrating cluster /tmp/mycluster.
2019-02-23 14:37:13.524050: Migrating database mydb
BEGIN
CREATE FUNCTION
INSERT 0 1
COMMIT
2019-02-23 14:37:13.572099: Done migrating database mydb
2019-02-23 14:37:13.572224: Done all migrations.
[mycluster]$ psql --dbname=mydb --command="select major, minor, patch, label, file_name from pghops.version;"
major | minor | patch | label | file_name
-------+-------+-------+----------------+-----------------------------------
0000 | 0000 | 0000 | pghops-init | 0000.0000.0000.pghops-init.yaml
0001 | 0001 | 0001 | init | 0001.0001.0001.init.yml
0001 | 0002 | 0001 | create_account | 0001.0002.0001.create_account.yml
(3 rows)
[mycluster]$
使用概述
安装postgresql时,需要初始化磁盘上的存储区域
调用数据库
群集
它是由
PostgreSQLpghops希望您放置与
在单个目录中构建和定义集群,请参阅
从此以后作为群集目录
。中的每个子目录
cluster_directory
应该是
群集(如果没有,可以添加名为databases
的文件,该文件包含
数据库目录列表)。
例如,假设您的cluster\u目录是/tmp/pghops/main,则
有两个数据库-dba和dbb。您的目录结构将
喜欢:
└── main
├── dba
└── dbb
pghops要求每个数据库目录都有一个名为
版本
迁移文件。每个迁移文件必须遵循以下内容
版本控制约定:
<;主要>;<;次要>;<;修补程序>;<;标签>;.yml
这允许您遵循语义版本控制
如果你愿意。pghops解析这些文件名并将它们保存到
pghops.version
表格。
如果pghops检测到集群上不存在数据库,则pghops
如果数据库目录中有一个名为
create_database.sql
包含数据库创建
命令。pghops在名为version
in的表中记录所有迁移
模式pghops
。如果此表不存在,则pghops将运行
包括0000.0000.0000.pghops init.yaml
先编写脚本来创建它。
每个版本文件必须采用yaml格式,并且具有yaml或yml
后缀。F文件只能包含注释和键/值对,其中
表示单个文件或
要执行的文件列表。目录可以是绝对的,也可以是相对的
数据库目录或名为schemas
的目录
数据库目录。我们建议你列出你的目录
结构与pgadmin相同。例如,如果
群集目录
看起来像:
├── cluster_a
│ ├── databases
│ ├── db_a1
│ │ ├── create_database.sql
│ │ ├── schemas
│ │ │ └── public
│ │ │ ├── functions
│ │ │ ├── tables
│ │ │ │ └── visits.sql
│ │ │ └── views
│ │ │ ├── vistor_view.sql
│ │ │ └── location_view.sql
│ │ └── versions
│ │ ├── 0000.0011.0001.change-a.yml
│ │ ├── 0000.0021.0002.change-b.yml
│ │ └── 0000.0032.0000.change-c.yml
│ ├── db_a2
│ │ ├── create_database.sql
│ │ ├── data
│ │ │ └── init-data.sql
│ │ ├── schemas
│ │ │ └── public
│ │ │ ├── functions
│ │ │ ├── tables
│ │ │ │ └── user.sql
│ │ │ └── views
│ │ │ ├── user_view.sql
│ │ │ └── accounts_view.sql
│ │ └── versions
│ │ ├── 0000.0001.0001.feature-a.yml
│ │ ├── 0000.0001.0002.feature-b.yml
│ │ └── 0000.0002.0000.feature-c.yml
并且您希望使用pghops创建在visitor\u视图中定义的新视图
和location_视图,在db_a1/versions中创建一个新的迁移脚本
例如0000.0033.0000.new views.yml
并添加以下行:
schemas/public/views:
- visitor_view.sql
- location_view.sql
您可以选择省略sql后缀。同样,模式
是可选的
同样,
运行pghops,方法是将cd'ing放入cluster\u目录并运行
pghops
请参见下面的命令行参数。你也可以通过
群集目录
作为第一个参数。
当您运行pghops时,它将连接 visitor_view.sql和location_view.sql合并到一个文件中,然后 在单个事务中通过psql执行它。如果成功,一个新的 记录已添加到pghops.version,您的迁移已完成!为了 更多示例请参见测试 群集
安装
pghops
需要Python3.7和PSQL客户端。pghops_测试
需要Docker。用pip安装pghops
pip3 install pghops
这应该添加可执行的pghops
,pghops_test
,以及
pghops\u创建索引
到您的路径。
最佳实践
SQL代码的目录布局
我建议遵循与pgadmin相同的布局。例如,如果你 有一个名为dba的数据库,一种可能性是:
├── dba
│ ├── data
│ ├── schemas
│ │ ├── myschema
│ │ │ ├── functions
│ │ │ ├── tables
│ │ │ └── views
│ │ └── public
│ │ ├── functions
│ │ ├── tables
│ │ └── views
│ └── versions
data
目录可以包含在
迁移。
版本控制
在确定要将哪些迁移文件迁移到 执行。它忽略了 pghops.version表,只查看文件名。
因此,您可以使用您喜欢的任何版本控制方案。语义
版本控制肯定是一个可靠的选择。另一
scheme,它需要稍微多一些的跟踪工作,但是可以工作
当与多个分支机构的多人打交道时,
为main
使用自动递增的数字
合并到主/生产分支。对于minor
,请使用
指的是一个特性分支或链接回
售票系统对于修补程序
,请使用递增的数字
为功能创建的每个移植文件。使用标签
区分两个人为
同时具有相同的功能。这也有助于防止合并
冲突。
幂等性
从本质上说,这意味着如果执行相同的sql两次所有更改
只会生效一次。所以在编写ddl时使用"如果不存在"
声明并检查您的记录是否存在
执行update语句(或在冲突时使用不执行任何操作
条款)
使旧的迁移文件保持最新
pghops.version表和git(或其他vcs)应该是您的全部 审计和历史目的的需要。如果你做出改变 在新数据库上运行时中断旧的迁移脚本,最好 返回并更新旧脚本。然后可以使用pghops创建 从头开始为故障转移创建新数据库,设置新环境, 或测试目的。
密码和PSQL
通常情况下,您不必为每个 PSQL调用。有几个选项:
选项
pghops有许多配置选项,您可以通过 命令行、环境变量或各种属性文件。选项 按以下顺序加载,从最高优先级到最低优先级: