一个高度个性化的postgresql迁移工具

pghops的Python项目详细描述


pghops是一个命令行postgresql模式迁移实用程序 用python。它旨在成为 PostgreSQL。

  1. 功能
  2. 演示
  3. 使用概述
  4. 安装
  5. 最佳实践
  6. 选项
  7. 管理索引
  8. 单元测试
  9. 常见问题
  10. 杂项
  11. 许可证
  12. < > >

    功能

    • 简单版本文件语法:pghops版本文件是yaml文件 具有表示一个或多个sql的目录和值的键 文件名。
    • 使用psql执行脚本:pghops使用psql执行所有sql, 利用PostgreSQL客户端的广泛功能。使用 脚本中的任何psql命令。
    • 单元测试框架:pghops自带单元 测试框架。不要再找借口跳过sql单元测试了!
    • 全部或无迁移:将整个迁移打包到 单个事务或其自身事务中的每个迁移脚本。
    • 保存到版本表的所有SQL命令pghops保存所有SQL 在迁移到其版本表期间执行。让审计员 快乐!

    演示

    下面的终端会话演示如何创建名为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在名为versionin的表中记录所有迁移 模式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
    

    这应该添加可执行的pghopspghops_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调用。有几个选项:

    1. 设置使用无密码身份验证运行pghops的用户, 例如信任或 对等体最好的 然后在与PostgreSQL相同的框中运行pghops。
    2. 使用a密码 文件
    3. < > >

      选项

      pghops有许多配置选项,您可以通过 命令行、环境变量或各种属性文件。选项 按以下顺序加载,从最高优先级到最低优先级:

      1. 命令行参数
      2. --options file命令行指定的文件中的属性 参数。
      3. 环境变量。
      4. <;cluster dir>;/<;db>;pghops.properties中的属性
      5. <;cluster dir>;pghops.properties中的属性
      6. 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,请确保以下各项:

        1. index语句位于后缀为".sql"的文件中。
        2. 整个索引语句位于一行。
        3. index语句从行的第一列开始。普霍普斯 忽略任何前面有空格的索引语句。如果有用, 例如,有一个函数可以创建临时表并 在所述临时表上定义索引,不希望pghops 管理此索引。
        4. 在索引定义中使用完全限定的表名 (schema.table_name)。创建索引脚本首先检查 在执行index语句之前存在表,并且 当pghops保存索引时,它不会分析任何前面的集合 路径语句。如果不使用完全限定的表名 pghops不会保存索引。
        5. 如果不存在,则语句使用,以便可以多次运行 不会导致错误。
        6. 索引扫描并不完美。如果你使用非常规 需要引用名称的索引或表的名称, pghops无法正确分析语句。
        7. < > >

          通过扫描代码中的索引,可以在 文件作为它们的表和pghops将它们添加到pghops.index 在下次迁移过程中自动执行。

          对于pghops.index中的每条记录,将首先创建pghops\u index。 检查表名是否有效。然后检查 enabled标志,如果设置,则在定义中执行sql。这个 脚本根据cpu核的数量并行运行,尽管 这种优势在最近的postgresqls中得到了缓解,postgresqls可以创建 自动并行的单个索引。

          单元测试

          使用pghops_test命令,您可以创建并运行简单的sql 单元测试。您需要安装Docker,因为测试是在 PostgreSQL Docker容器。工作原理如下:

          1. 在您的数据库目录中创建名为tests的目录。
          2. tests目录中,创建以 \u test.sql。通常您希望文件名包含 用于排序的编号,例如01_base_test.sql
          3. 运行pghops_test generate。这将启动PostgreSQL Docker 容器,运行迁移,然后为 每个sql测试文件。例如,对于测试文件 01_base_test.sql它将生成01_base_expected.txt
          4. 查看生成的预期文件。确保没有主机或 特定于环境的输出,如主机名或时间戳。
          5. 随着模式的发展,您可以运行pghops_test run来运行 单元测试。它将启动一个新的PostgreSQL Docker容器,运行 迁移,执行单元测试sql文件并比较 输出到预期文件的内容。
          6. < > >

            如果创建了许多测试,则可以通过创建 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_argspsql_参数数据库连接信息详细信息选项文件

            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复制

            欢迎加入QQ群-->: 979659372 Python中文网_新手群

            推荐PyPI第三方库


热门话题
java应该考虑使用DTO来代替Spring控制器层吗?   java为什么要将Maven与Git结合起来?   java试图将CSV转换为XLSX,但使用了错误的逗号拆分列   mysql Spring 3+Hibernate:java。sql。BatchUpdateException:无法添加或更新子行(ManyToMany)   java基本字符串反转器   java无法使用RestControllerAdvice为身份验证失败生成自定义错误消息   java当只允许SQLException时,如何抛出EOFEException?   java如何创建播放模块?   Android中匿名类的java实例化异常问题   java两个停靠组件,其中第二个组件填充剩余空间   java如何在按钮延迟时启用它   Java中正在运行的应用程序中的后台进程   java我正试图从一个字符串打印出这个字符输出   如何使用java socket通过两个不同的wifi连接两台电脑?   javaapachecamel:如何将分层数据从数据库转换为pojo   java Webrtc:OniconConnectionChange和onConnectionChange之间有什么区别   java如何重写已经创建的JTable方法   爪哇扫雷机堆垛机   雅加达ee Java ee EJB 3.0 Glassfish