After a few revisions this is what I ended up with. It uses the dblink extension (required as a pre-requisite, and in this case installed in a schema named "extension_data") to query three different environments from one session in pgAdmin, psql, etc. The output shows the object name, object type, and which of the three environments have the object loaded. In the cases of indexes I included the table name, type (USING clause) and ordered column list rather than the name since those are the important parts of the index and system generated names could theoretically be different. I also included a column for "object_info" which contains CREATE INDEX commands for indexes that don't exists for easy access and the description of the various pg_settings settings that are also queried.
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;