2017-01-05 22 views
2

我的任务是向想要简单方法验证所有对象已部署到QA或生产PostgreSQL数据库服务器。如何生成部署到不同PostgreSQL数据库或从不同PostgreSQL数据库中丢失的DDL对象或函数的列表

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

识别指标到目前为止,我已经找到查询和列/列的顺序,他们在(Position of the column in the index),可以查询INFORMATION_SCHEMA其他对象表,并了解了生成模式的完整的diff工具(How to check difference between two databases in PostgreSQL?) 。有用的工具apgdiff提供了SQL输出来同步数据库,但是我没有看到如何获得不同对象的摘要,并且请求者只是想要一个电子表格,以便可以部署缺少的对象。一旦所有对象都被验证存在,apgdiff或其他工具可用于进一步检查全表定义和触发代码的功能是否相同,但这将是以后的任务。

我最初尝试这样做,没有索引或函数参数,UNION查询是在每个环境中单独运行,但是针对所有环境运行此代码并合并结果的代码也是需要的。

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; 
+1

而不是“差异”模式,你应该使用适当的模式迁移工具。然后该工具会知道要应用什么。看看Liquibase或Flyway –

+0

@a_horse_with_no_name,谢谢你的提示。我曾看过Liquibase的参考资料,但并不知道Flyway。我同意你的看法,并希望将整个应用环境转移到这个方向,但是存在已知存在的差异,以及希望看到这些差异协调即将部署的概况的团队成员。 – Dan

回答

0

经过一些修改,这是我最终结束了。它使用dblink扩展(作为必备条件,在这种情况下安装在名为“extension_data”的模式中)来从pgAdmin,psql等的一个会话中查询三个不同的环境。输出显示对象名称,对象类型,以及三个环境中的哪一个具有加载的对象。在索引的情况下,我包括表名,类型(USING子句)和有序列列表而不是名称,因为这些是索引的重要部分,系统生成的名称在理论上可能不同。我还为“object_info”添加了一个列,其中包含CREATE INDEX命令,用于不便于访问的索引以及还查询各种pg_settings设置的说明。

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; 
相关问题