Database 如何生成部署到不同PostgreSQL数据库或从不同PostgreSQL数据库中丢失的DDL对象或函数的列表

Database 如何生成部署到不同PostgreSQL数据库或从不同PostgreSQL数据库中丢失的DDL对象或函数的列表,database,postgresql,diff,ddl,Database,Postgresql,Diff,Ddl,我的任务是向需要一种简单方法来验证所有对象是否已部署到QA或生产PostgreSQL数据库服务器的队友提供“查询” 具体而言,请求者希望能够确认每个环境中都存在所有表、函数、序列、索引、视图、模式和触发器,以及/或者在部署新应用程序时,一个或另一个环境中缺少哪些表、函数、序列、索引、视图、模式和触发器 到目前为止,我已经找到了用于标识索引和它们所在的列/列顺序()的查询,可以在information_schema中查询其他对象的表,并且已经了解了用于生成模式的完整差异()的工具。这个有用的工具提

我的任务是向需要一种简单方法来验证所有对象是否已部署到QA或生产PostgreSQL数据库服务器的队友提供“查询”

具体而言,请求者希望能够确认每个环境中都存在所有表、函数、序列、索引、视图、模式和触发器,以及/或者在部署新应用程序时,一个或另一个环境中缺少哪些表、函数、序列、索引、视图、模式和触发器

到目前为止,我已经找到了用于标识索引和它们所在的列/列顺序()的查询,可以在information_schema中查询其他对象的表,并且已经了解了用于生成模式的完整差异()的工具。这个有用的工具提供了同步数据库的SQL输出,但我没有看到如何获得不同对象的摘要,请求者只是想要一个关于存在或不存在对象的电子表格,以便可以部署缺少的对象。验证所有对象是否存在后,可以使用apgdiff或其他工具进一步检查完整的表定义以及函数和触发器代码是否相同,但这将是以后的任务

在没有索引或函数参数的情况下,我最初的尝试是在每个环境中分别运行联合查询,但也需要针对所有环境运行该查询并合并结果的代码

select routine_name as object_name, routine_schema as schema_name, 'routine' as object_type, 'yes' as object_exists from information_schema.routines where routine_schema in ( 'shema1','schema2','schema3' ) union 
select schema_name as object_name, schema_name as schema_name, 'schema' as object_type, 'yes' as object_exists from information_schema.schemata where schema_name in ( 'shema1','schema2','schema3' ) union 
select sequence_name as object_name, sequence_schema as schema_name, 'sequence' as object_type, 'yes' as object_exists from information_schema.sequences where sequence_schema in ( 'shema1','schema2','schema3' ) union 
select table_name as object_name, table_schema as schema_name, 'table' as object_type, 'yes' as object_exists from information_schema.tables where table_schema in ( 'shema1','schema2','schema3' ) union 
select trigger_name as object_name, trigger_schema as schema_name, 'trigger' as object_type, 'yes' as object_exists from information_schema.triggers where trigger_schema in ( 'shema1','schema2','schema3' ) union
select table_name as object_name, table_schema as schema_name, 'view' as object_type, 'yes' as object_exists from information_schema.views where table_schema in ( 'shema1','schema2','schema3' ) 
order by object_type, schema_name, object_name; 

经过几次修改后,这就是我最终得到的结果。它使用dblink扩展(作为先决条件需要,在本例中安装在名为“extension_data”的模式中)从pgAdmin、psql等中的一个会话中查询三个不同的环境。输出显示对象名称、对象类型以及三个环境中加载了对象的环境。在索引的情况下,我包括表名、类型(USING子句)和有序列列表,而不是名称,因为它们是索引的重要部分,系统生成的名称理论上可能不同。我还为“object_info”添加了一个列,其中包含为不存在的索引创建索引命令,以便于访问,以及对各种pg_设置的描述,这些设置也会被查询

do $$
-- Two changes are needed below: 
--      (1) the schema list needs to be updated
--      (2) the dblink connection information for each environment needs to be updated
-- Execute this in pgadmin while connected to one of the environments.  If the username and 
-- password are the same in all three environments then they do not need to be listed in 
-- the dblink connection strings below
declare
    rc bigint := 0;
    r RECORD;
    -- Necessary change #1: put the schemas you care about here
    v_schema_list text := '''schema1'',''schema2'',''schema3'',''schema4''';
    -- This is a large query looking at database settings, functions (routines), schemas, sequences, tables, triggers, views, and indexes
    -- For functions, the parameter list is important since the same function name can exist with a different number of parameters 
    --      or the parameters in a different order.  This query is not looking at parameter defaults.
    -- For indexes, the name of the index isn't important to a functioning system, but the type of index and column order is
    -- For tables, this only looks for the existence of the table, not the column order nor constraints, but those could be added later
    v_sql text := 'select name||'' = ''||setting as object_name, ''setting'' as object_type, category||'': ''||short_desc as object_info 
                          from pg_settings 
                   union
                   select routine_schema||''.''||routine_name||''(''||coalesce(string_agg(parameters.parameter_name||'' ''||parameters.data_type,'', ''),'''')||'')'' as object_name
                        , ''routine'' as object_type
                        , ''''::text as object_info
                        from information_schema.routines 
                        left join lateral
                            (select parameter_name, parameters.data_type, parameters.ordinal_position
                            from information_schema.parameters 
                            where parameters.specific_schema = routines.specific_schema
                            and parameters.specific_name = routines.specific_name
                            order by ordinal_position) parameters
                            on true
                        where routine_schema in ('||v_schema_list||') 
                        group by routine_name, routine_schema
                   union 
                   select schema_name||''.''||schema_name as object_name, ''schema'' as object_type, ''''::text as object_info 
                        from information_schema.schemata where schema_name in ('||v_schema_list||') 
                   union 
                   select sequence_schema||''.''||sequence_name as object_name, ''sequence'' as object_type, ''''::text as object_info 
                        from information_schema.sequences where sequence_schema in ('||v_schema_list||') 
                   union 
                   select table_schema||''.''||table_name as object_name, ''table'' as object_type, ''''::text as object_info 
                        from information_schema.tables where table_schema in ('||v_schema_list||') 
                   union 
                   select trigger_schema||''.''||trigger_name as object_name, ''trigger'' as object_type, ''''::text as object_info 
                        from information_schema.triggers where trigger_schema in ('||v_schema_list||') 
                   union
                   select table_schema||''.''||table_name as object_name, ''view'' as object_type, ''''::text as object_info 
                        from information_schema.views where table_schema in ('||v_schema_list||') 
                   union 
                   select substring(indexdef,3+position(''ON'' in indexdef)) as object_name, ''index'' as object_type, indexdef as object_info 
                        from pg_indexes where schemaname in ('||v_schema_list||')
                   order by object_type, object_name; ';
begin
    drop table if exists object_list;
    drop table if exists object_comparison;
    create temp table object_list (object_name text, object_type text, object_info text, environment text);

    for r in 
        -- Necessary change #2: update connection information for each database here
        select 'prod' as conn, 'dbname=your_prod_db_name port=5432 host=your_prod_server username=your_prod_user password=your_prod_password' as conn_string union
        select 'qa' as conn, 'dbname=your_qa_db_name port=5432 host=your_qa_server username=your_qa_user password=your_qa_password' as conn_string union 
        select 'dev' as conn, 'dbname=your_dev_db_name port=5432 host=your_dev_server username=your_dev_user password=your_dev_password' as conn_string
    loop 
        begin
            perform extension_data.dblink_disconnect(r.conn);    
        exception when others then 
            null;
        end;
        perform extension_data.dblink_connect(r.conn, r.conn_string);

        perform extension_data.dblink_open(r.conn, 'object_list',v_sql);
        GET CURRENT DIAGNOSTICS rc := ROW_COUNT;

        while rc > 0 loop 
            insert into object_list 
                SELECT *, r.conn as environment 
                FROM extension_data.dblink_fetch(r.conn, 'object_list', 500) AS (object_name text, object_type text, object_info text);
            GET CURRENT DIAGNOSTICS rc := ROW_COUNT;
        end loop;

        perform extension_data.dblink_close(r.conn, 'object_list');

        perform extension_data.dblink_disconnect(r.conn);    
    end loop;

    create temp table object_comparison as (
        select coalesce(dev.object_name,coalesce(qa.object_name,prod.object_name)) as object_name
             , coalesce(dev.object_type,coalesce(qa.object_type,prod.object_type)) as object_type
             , dev.environment as dev
             , qa.environment as qa
             , prod.environment as prod
             , coalesce(prod.object_info,coalesce(qa.object_info,dev.object_info)) as object_info
        from (select * from object_list where environment = 'dev')  dev
        full outer join (select * from object_list where environment = 'qa')  qa 
            on dev.object_name = qa.object_name 
            and dev.object_type = qa.object_type 
        full outer join (select * from object_list where environment = 'prod')  prod 
            on coalesce(dev.object_name,qa.object_name) = prod.object_name 
            and coalesce(dev.object_type,qa.object_type) = prod.object_type
    );
end;
$$ language plpgsql;

select * from object_comparison where dev is null or qa is null or prod is null;

您应该使用适当的模式迁移工具,而不是“区分”模式。然后该工具将知道应用什么。看一看,一杯烈酒还是一杯Flyway@a_horse_with_no_name,谢谢你的提示。我看到过Liquibase,但不知道Flyway。我同意您的观点,并希望将整个应用程序环境朝着这个方向发展,但我们知道存在一些差异,团队成员希望了解这些差异的概况,以协调即将到来的部署。