How to find all object which will be dropped by CASCADE? - postgresql

When I drop something:
ALTER TABLE "order_bt" DROP COLUMN "billed_to"
Documentation says that I can see what depend on billed_to column:
DETAIL: view "order" depends on column billed_to of table order_bt
But on order view there are also triggers which depend on order, which will be dropped with CASCADE
Is there an option will allow to see full list of what would be dropped by CASCADE?

I created an example structure to check dependencies. You can try it on your database, of course objects ID will be different.
CREATE TABLE parents (
id_parent integer NOT NULL,
name_parent varchar(100),
CONSTRAINT pk_parents_id PRIMARY KEY (id_parent)
);
CREATE TABLE childs (
id_child integer NOT NULL,
name_child varchar(100),
parent_id integer,
CONSTRAINT pk_childs_id PRIMARY KEY (id_child),
CONSTRAINT fk_childs_parent_id__parents FOREIGN KEY (parent_id)
REFERENCES parents (id_parent) MATCH SIMPLE
ON UPDATE CASCADE ON DELETE CASCADE
);
CREATE OR REPLACE VIEW public.parents_view AS
SELECT parents.id_parent,
parents.name_parent
FROM parents;
CREATE FUNCTION test_trigger() RETURNS TRIGGER AS $$
BEGIN
IF TG_OP = 'INSERT' then
raise notice 'INSERT trigger, NEW = [%]', NEW;
ELSIF TG_OP = 'UPDATE' then
raise notice 'UPDATE trigger, OLD = [%], NEW = [%]', OLD, NEW;
ELSE
raise notice 'DELETE trigger, OLD = [%]', OLD;
END IF;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER parents_view_trigger INSTEAD OF INSERT OR UPDATE OR DELETE ON parents_view FOR EACH ROW EXECUTE PROCEDURE test_trigger();
Objects that depend of table "parents":
SELECT
refclassid, refobjid, refobjsubid,
pg_describe_object(refclassid, refobjid, refobjsubid),
--pg_identify_object (refclassid, refobjid, refobjsubid) ,
classid, objid, objsubid,
pg_describe_object(classid, objid, objsubid),
--pg_identify_object (classid, objid, objsubid) ,
pg_get_constraintdef(objid),
deptype
FROM pg_depend
WHERE refobjid IN ('parents'::regclass) -- and deptype IN ('n', 'a')
ORDER BY classid, refobjid;
Output:
refclassid
refobjid
refobjsubid
pg_describe_object refobject
classid
objid
objsubid
pg_describe_object object
pg_get_constraintdef
deptype
1259
2305241
0
table parents
1247
2305243
0
type parents
i
1259
2305241
1
table parents column id_parent
2606
2305245
0
constraint pk_parents_id on table parents
PRIMARY KEY (id_parent)
a
1259
2305241
1
table parents column id_parent
2606
2305251
0
constraint fk_childs_parent_id__parents on table childs
FOREIGN KEY (parent_id) REFERENCES parents(id_parent) ON UPDATE CASCADE ON DELETE CASCADE
n
1259
2305241
1
table parents column id_parent
2618
2305259
0
rule _RETURN on view parents_view
n
1259
2305241
2
table parents column name_parent
2618
2305259
0
rule _RETURN on view parents_view
n
Objects that depend of view "parents_view":
SELECT
refclassid, refobjid, refobjsubid,
pg_describe_object(refclassid, refobjid, refobjsubid),
--pg_identify_object (refclassid, refobjid, refobjsubid),
classid, objid, objsubid,
pg_describe_object(classid, objid, objsubid),
--pg_identify_object (classid, objid, objsubid) ,
pg_get_constraintdef(objid),
deptype
FROM pg_depend
WHERE refobjid IN ('parents_view'::regclass) -- and deptype IN ('n', 'a')
ORDER BY classid, refobjid;
Output:
refclassid
refobjid
refobjsubid
pg_describe_object refobj
classid
objid
objsubid
pg_describe_object obj
pg_get_constraintdef
deptype
1259
2305256
0
view parents_view
1247
2305258
0
type parents_view
i
1259
2305256
0
view parents_view
2618
2305259
0
rule _RETURN on view parents_view
i
1259
2305256
0
view parents_view
2618
2305259
0
rule _RETURN on view parents_view
n
1259
2305256
0
view parents_view
2620
2305260
0
trigger parents_view_trigger on view parents_view
a
You can check that view "parents_view" (object ID: 2305256) not depend of table parents but rule "rule _RETURN on view parents_view" (object ID: 2305259) does, and "trigger parents_view_trigger on view parents_view" depends on parents_view, this is the reason why recursive query do not return view dependent information.
Triggers are DEPENDENCY_AUTO type (a): The dependent object can be dropped separately from the referenced object, and should be automatically dropped (regardless of RESTRICT or CASCADE mode) if the referenced object is dropped. Example: a named constraint on a table is made auto-dependent on the table, so that it will go away if the table is dropped.
You can try a query like this, but you should try to improve it:
You can restrict the result filtering by deptype.
WITH RECURSIVE pg_depend_recursive AS (
SELECT d.refclassid, d.refobjid, d.refobjsubid, d.classid, d.objid, d.objsubid, d.deptype
FROM pg_depend d
where refobjid = 'parents'::regclass
UNION
SELECT d.refclassid, d.refobjid, d.refobjsubid, d.classid, d.objid, d.objsubid, d.deptype
FROM pg_depend d
INNER JOIN pg_depend_recursive dr ON d.refobjid = dr.objid),
pg_depend_recursive2 AS (SELECT d.refclassid, d.refobjid, d.refobjsubid, d.classid, d.objid, d.objsubid, d.deptype
FROM pg_depend_recursive d
UNION
SELECT d.refclassid, d.refobjid, d.refobjsubid, d.classid, d.objid, d.objsubid, d.deptype
FROM pg_depend d
INNER JOIN pg_depend_recursive2 dr ON d.objid = dr.objid AND pg_describe_object(dr.classid, dr.objid, dr.objsubid) ilike 'rule%'),
pg_depend_recursive3 AS (SELECT d.refclassid, d.refobjid, d.refobjsubid, d.classid, d.objid, d.objsubid, d.deptype
FROM pg_depend_recursive2 d
UNION
SELECT d.refclassid, d.refobjid, d.refobjsubid, d.classid, d.objid, d.objsubid, d.deptype
FROM pg_depend d
INNER JOIN pg_depend_recursive3 dr ON d.refobjid = dr.refobjid)
SELECT refclassid, refobjid, refobjsubid, pg_describe_object(refclassid, refobjid, refobjsubid),
classid, objid, objsubid, pg_describe_object(classid, objid, objsubid),
deptype
FROM pg_depend_recursive3;

Related

Getting the column name of a postgres check constraint from information_schema

I can get the constraint name, the table name and the check_clause of the check constraints on a table using this query:
SELECT tc.constraint_type, tc.constraint_name, tc.table_name, cc.check_clause
FROM information_schema.table_constraints tc
JOIN information_schema.check_constraints cc ON cc.constraint_name = tc.constraint_name
WHERE tc.table_name = $1 and tc.constraint_type = 'CHECK'
From information_schema.constraint_column_usage I can get the columns where a PRIMARY or UNIQUE constraint applies but not a CHECK constraint:
SELECT * FROM information_schema.constraint_column_usage where table_name = $1
This eventually worked following suggestion below:
SELECT
ccu.table_schema,
ccu.table_name,
ccu.constraint_name,
ccu.column_name,
cc.check_clause
FROM information_schema.constraint_column_usage ccu
JOIN information_schema.check_constraints cc ON ccu.constraint_name = cc.constraint_name
WHERE ccu.constraint_name IN
(
SELECT
constraint_name
FROM information_schema.check_constraints
)
AND ccu.table_name = $1;
CHECK constraints can exist on domains as well as tables. To get those that are on table columns only do:
SELECT
table_schema,
table_name,
column_name,
constraint_name
FROM
information_schema.constraint_column_usage
WHERE
constraint_name IN (
SELECT
constraint_name
FROM
information_schema.check_constraints);
The sub select will find all the CHECK constraints in the database and the constraint_name IN will filter to only those that are a column constraint.
UPDATE
An alternative is to use the system catalogs directly:
WITH c AS (
SELECT
conname,
conrelid,
unnest(conkey) AS col_id
FROM
pg_catalog.pg_constraint
WHERE
contype = 'c'
AND conrelid > 0
)
SELECT
conrelid::regclass AS table_name,
conname,
attname
FROM
pg_catalog.pg_attribute
JOIN c ON c.col_id = attnum
AND conrelid = attrelid;
What the above does is restrict the constraint type(contype) to 'c' for CHECK and then to only those CHECK constraints that exist on table conrelid > 0 in the CTE(WITH) and then joins the unnested array of columns(conkey) to the column information in pg_catalog.pg_attribute to get the column names.. The conrelid::regclass turns the conrelid oid value into a table name.
See pg_constraint for more information.

SQL Server: Left Join and unmatch rows are not coming

I am using left join as a result everything should come from my left table but only matching data coming. I am not able to understand what is wrong in my query or what is missing in it. so please have a look at sample code where anyone can run and reproduce the issue.
create table #tmpModel
(
CSM_ID int,
ID INT,
ParentID INT,
DisplayInCSM Varchar(200),
Type varchar(20),
LineItemID INT
)
create table #tmpLineItem
(
ID INT,
LineItem Varchar(200),
TickerID varchar(20),
)
create table #tmpOutputDetl_CSMTuner
(
Section Varchar(200),
LineItem Varchar(200),
Period Varchar(200),
ItemValue Decimal(18,2),
Ticker varchar(20)
)
INSERT INTO #tmpModel(CSM_ID,ID,ParentID,DisplayInCSM,Type,LineItemID)
VALUES (370,1,0,'Model','SHEET',0)
,(370,2,1,'Segment Details','GROUP',0)
,(370,3,2,'LoyaltyOne','GROUP',0)
,(370,4,3,'Revenue','LINEITEM',198359)
,(370,5,4,'EBITDA','LINEITEM',198360)
,(370,6,5,'Card Services','GROUP',0)
,(370,7,6,'Diluted','LINEITEM',198342)
INSERT INTO #tmpLineItem(ID,LineItem,TickerID)
VALUES (198359,'Net Revenue','ADK')
,(198360,'EBITDA Details','ADK')
,(198342,'Diluted Shares','ADK')
INSERT INTO #tmpOutputDetl_CSMTuner (Section,LineItem,Period,ItemValue,Ticker)
VALUES ('Consensus Model','Net Revenue','2010 FYA',203.22,'ADS')
,('Consensus Model','Net Revenue','2011 FYA',203.22,'ADS')
,('Consensus Model','Net Revenue','2012 FYA',203.22,'ADS')
,('Consensus Model','EBITDA Details','2010 FYA',103.22,'ADS')
,('Consensus Model','EBITDA Details','2011 FYA',109.22,'ADS')
,('Consensus Model','EBITDA Details','2012 FYA',105.22,'ADS')
,('Key Financial','Diluted Shares','2010 FYA',55.22,'ADS')
,('Key Financial','Diluted Shares','2011 FYA',20.22,'ADS')
,('Key Financial','Diluted Shares','2012 FYA',13.22,'ADS')
;with DirectReports as
(
select CSM_ID,
ID,
ParentID,
hierarchy = format(id,'0000'),
level = 0,
DisplayInCSM,
Type,
LineItemID
from #tmpModel
where isnull(ParentID, 0) = 0
union all
select e.CSM_ID,
e.ID,
e.ParentID,
hierarchy = d.hierarchy + '.' + format(e.id,'0000'),
level = level + 1,
e.DisplayInCSM,
e.Type,
e.LineItemID
from #tmpModel e
join DirectReports d on e.ParentID = d.ID
)
/*SELECT * FROM DirectReports Order By hierarchy*/
,Cte1 as
(
SELECT AA.Section,AA.LineItem,AA.Ticker, r.DisplayInCSM, r.Type,r.hierarchy, AA.ItemValue, AA.Period
FROM DirectReports r
LEFT OUTER JOIN
(
Select b.*,L.ID AS LineItemID,L.TickerID
From #tmpOutputDetl_CSMTuner b
INNER JOIN #tmpLineItem L ON b.LineItem= L.LineItem
WHERE L.TickerID='ADK' AND b.Ticker='ADS'
) AA
ON (AA.LineItemID=r.LineItemID)
WHERE AA.Ticker = 'ADS'
)
SELECT * FROM Cte1 ORDER By hierarchy
IF OBJECT_ID(N'tempdb..#tmpModel') IS NOT NULL
BEGIN
DROP TABLE #tmpModel
END
IF OBJECT_ID(N'tempdb..#tmpLineItem') IS NOT NULL
BEGIN
DROP TABLE #tmpLineItem
END
IF OBJECT_ID(N'tempdb..#tmpOutputDetl_CSMTuner') IS NOT NULL
BEGIN
DROP TABLE #tmpOutputDetl_CSMTuner
END
Data should come with Parent child hierarchy wise
Type Group should come but not coming, only Line Item is coming.
Please guide me which area i should rectify to get the desired output.
Solved this way comment this line /WHERE AA.Ticker = 'ADS'/
Now right data is coming.
create table #tmpModel
(
CSM_ID int,
ID INT,
ParentID INT,
DisplayInCSM Varchar(200),
Type varchar(20),
LineItemID INT
)
create table #tmpLineItem
(
ID INT,
LineItem Varchar(200),
TickerID varchar(20),
)
create table #tmpOutputDetl_CSMTuner
(
Section Varchar(200),
LineItem Varchar(200),
Period Varchar(200),
ItemValue Decimal(18,2),
Ticker varchar(20)
)
INSERT INTO #tmpModel(CSM_ID,ID,ParentID,DisplayInCSM,Type,LineItemID)
VALUES (370,1,0,'Model','SHEET',0)
,(370,2,1,'Segment Details','GROUP',0)
,(370,3,2,'LoyaltyOne','GROUP',0)
,(370,4,3,'Revenue','LINEITEM',198359)
,(370,5,4,'EBITDA','LINEITEM',198360)
,(370,6,5,'Card Services','GROUP',0)
,(370,7,6,'Diluted','LINEITEM',198342)
INSERT INTO #tmpLineItem(ID,LineItem,TickerID)
VALUES (198359,'Net Revenue','ADK')
,(198360,'EBITDA Details','ADK')
,(198342,'Diluted Shares','ADK')
INSERT INTO #tmpOutputDetl_CSMTuner (Section,LineItem,Period,ItemValue,Ticker)
VALUES ('Consensus Model','Net Revenue','2010 FYA',203.22,'ADS')
,('Consensus Model','Net Revenue','2011 FYA',203.22,'ADS')
,('Consensus Model','Net Revenue','2012 FYA',203.22,'ADS')
,('Consensus Model','EBITDA Details','2010 FYA',103.22,'ADS')
,('Consensus Model','EBITDA Details','2011 FYA',109.22,'ADS')
,('Consensus Model','EBITDA Details','2012 FYA',105.22,'ADS')
,('Key Financial','Diluted Shares','2010 FYA',55.22,'ADS')
,('Key Financial','Diluted Shares','2011 FYA',20.22,'ADS')
,('Key Financial','Diluted Shares','2012 FYA',13.22,'ADS')
;with DirectReports as
(
select CSM_ID,
ID,
ParentID,
hierarchy = format(id,'0000'),
level = 0,
DisplayInCSM,
Type,
LineItemID
from #tmpModel
where isnull(ParentID, 0) = 0
union all
select e.CSM_ID,
e.ID,
e.ParentID,
hierarchy = d.hierarchy + '.' + format(e.id,'0000'),
level = level + 1,
e.DisplayInCSM,
e.Type,
e.LineItemID
from #tmpModel e
join DirectReports d on e.ParentID = d.ID
)
/*SELECT * FROM DirectReports Order By hierarchy*/
,Cte1 as
(
SELECT AA.Section,AA.LineItem,AA.Ticker, r.DisplayInCSM, r.Type,r.hierarchy, AA.ItemValue, AA.Period
FROM DirectReports r
LEFT OUTER JOIN
(
Select b.*,L.ID AS LineItemID,L.TickerID
From #tmpOutputDetl_CSMTuner b
INNER JOIN #tmpLineItem L ON b.LineItem= L.LineItem
WHERE L.TickerID='ADK' AND b.Ticker='ADS'
) AA
ON (AA.LineItemID=r.LineItemID)
/*WHERE AA.Ticker = 'ADS'*/
)
SELECT * FROM Cte1 WHERE TYPE <> 'SHEET' ORDER By hierarchy
IF OBJECT_ID(N'tempdb..#tmpModel') IS NOT NULL
BEGIN
DROP TABLE #tmpModel
END
IF OBJECT_ID(N'tempdb..#tmpLineItem') IS NOT NULL
BEGIN
DROP TABLE #tmpLineItem
END
IF OBJECT_ID(N'tempdb..#tmpOutputDetl_CSMTuner') IS NOT NULL
BEGIN
DROP TABLE #tmpOutputDetl_CSMTuner
END

How to list tables affected by cascading delete

I'm trying to perform a cascading delete on 15+ tables but I'm not certain that all of the requisite foreign keys have been configured properly. I would like to check for missing constraints without manually reviewing each constraint.
Is there a way to obtain a list of tables that will be affected by a cascading delete query?
Use pg_depend. Example:
create table master (id int primary key);
create table detail_1 (id int, master_id int references master(id) on delete restrict);
create table detail_2 (id int, master_id int references master(id) on delete cascade);
select pg_describe_object(classid, objid, objsubid)
from pg_depend
where refobjid = 'master'::regclass and deptype = 'n';
pg_describe_object
------------------------------------------------------
constraint detail_1_master_id_fkey on table detail_1
constraint detail_2_master_id_fkey on table detail_2
(2 rows)
deptype = 'n' means:
DEPENDENCY NORMAL - A normal relationship between separately-created
objects. The dependent object can be dropped without affecting the
referenced object. The referenced object can only be dropped by
specifying CASCADE, in which case the dependent object is dropped,
too.
Use pg_get_constraintdef() to get constraint definitions:
select
pg_describe_object(classid, objid, objsubid),
pg_get_constraintdef(objid)
from pg_depend
where refobjid = 'master'::regclass and deptype = 'n';
pg_describe_object | pg_get_constraintdef
------------------------------------------------------+------------------------------------------------------------------
constraint detail_1_master_id_fkey on table detail_1 | FOREIGN KEY (master_id) REFERENCES master(id) ON DELETE RESTRICT
constraint detail_2_master_id_fkey on table detail_2 | FOREIGN KEY (master_id) REFERENCES master(id) ON DELETE CASCADE
(2 rows)
To find the full chain of cascading dependencies we should use recursion and look into the catalog pg_constraint to get id of a dependent table.
with recursive chain as (
select classid, objid, objsubid, conrelid
from pg_depend d
join pg_constraint c on c.oid = objid
where refobjid = 'the_table'::regclass and deptype = 'n'
union all
select d.classid, d.objid, d.objsubid, c.conrelid
from pg_depend d
join pg_constraint c on c.oid = objid
join chain on d.refobjid = chain.conrelid and d.deptype = 'n'
)
select pg_describe_object(classid, objid, objsubid), pg_get_constraintdef(objid)
from chain;
Using transitive closure, one can determine the referencing and referenced tables. A caveat is that this query/view depends on the existence of Foreign Keys to determine the dependencies, and will not find tables if the FK's are missing (and the latter seems to be what the OP is asking for).
Table dependencies via Foreign Keys
CREATE OR REPLACE VIEW table_dependencies AS (
WITH RECURSIVE t AS (
SELECT
c.oid AS origin_id,
c.oid::regclass::text AS origin_table,
c.oid AS referencing_id,
c.oid::regclass::text AS referencing_table,
c2.oid AS referenced_id,
c2.oid::regclass::text AS referenced_table,
ARRAY[c.oid::regclass,c2.oid::regclass] AS chain
FROM pg_catalog.pg_constraint AS co
INNER JOIN pg_catalog.pg_class AS c ON c.oid = co.conrelid
INNER JOIN pg_catalog.pg_class AS c2 ON c2.oid = co.confrelid
-- Add this line as an input parameter if you want to make a one-off query
-- WHERE c.oid::regclass::text = 'YOUR TABLE'
UNION ALL
SELECT
t.origin_id,
t.origin_table,
t.referenced_id AS referencing_id,
t.referenced_table AS referencing_table,
c3.oid AS referenced_id,
c3.oid::regclass::text AS referenced_table,
t.chain || c3.oid::regclass AS chain
FROM pg_catalog.pg_constraint AS co
INNER JOIN pg_catalog.pg_class AS c3 ON c3.oid = co.confrelid
INNER JOIN t ON t.referenced_id = co.conrelid
WHERE
-- prevent infinite recursion by pruning paths where the last entry in
-- the path already appears somewhere else in the path
NOT (
ARRAY[ t.chain[array_upper(t.chain, 1)] ] -- an array containing the last element
<# -- "is contained by"
t.chain[1:array_upper(t.chain, 1) - 1] -- a slice of the chain,
-- from element 1 to n-1
)
)
SELECT origin_table,
referenced_table,
array_upper(chain,1) AS "depth",
array_to_string(chain,',') as chain
FROM t
);
Tables referencing a specific table
SELECT * FROM table_dependencies WHERE origin_table = 'clients';
Tables directly related to the "clients" table
SELECT *
FROM table_dependencies
WHERE referenced_table = 'clients'
AND depth = 2
ORDER BY origin_table;
Yes. you can truncate cascade in transaction and rollback. Note ROLLBACK is a key to save the data.
postgres will NOTICE you what other referencing tables will be affected.
postgres=# begin;
BEGIN
postgres=# truncate table a cascade;
NOTICE: truncate cascades to table "b"
TRUNCATE TABLE
postgres=# rollback;
ROLLBACK

Postgres dynamically update constraint foreign key

I have lots of tables with lots of foreign keys and about all of them are UPDATE NO ACTION and DELETE NO ACTION.
Is it possible to dynamically update all this foreign keys to CASCADE instead of NO ACTION or RESTRICT?
For example:
ALTER TABLE * ALTER FOREIGN KEY * SET ON UPDATE CASCADE ON DELETE CASCADE;
Yours,
Diogo
No, this is not possible.
You will need to drop and re-create all constraints as a foreign key constraint cannot be altered like that.
The following statement will generate the necessary alter table statements to drop and re-create the foreign keys:
select 'alter table '||pgn.nspname||'.'||tbl.relname||' drop constraint '||cons.conname||';'
from pg_constraint cons
join pg_class tbl on cons.confrelid = tbl.oid
join pg_namespace pgn on pgn.oid = tbl.relnamespace
where contype = 'f'
union all
select 'alter table '||pgn.nspname||'.'||tbl.relname||' add constraint '||cons.conname||' '||pg_get_constraintdef(cons.oid, true)||' ON UPDATE CASCADE ON DELETE CASCADE;'
from pg_constraint cons
join pg_class tbl on cons.confrelid = tbl.oid
join pg_namespace pgn on pgn.oid = tbl.relnamespace
where contype = 'f'
Save the output of this statement to a file and run it.
Make sure you validate the generated statements before running them!
I would use the following code to generate the necessary alter table SQL statements to drop and re-create the foreign keys (in order):
select 'ALTER TABLE '||pgn.nspname||'.'||tbl.relname||' DROP CONSTRAINT '||cons.conname||';' as sqlstr
from pg_constraint cons
join pg_class tbl on cons.conrelid = tbl.oid
join pg_namespace pgn on pgn.oid = tbl.relnamespace
where contype = 'f'
union
select 'ALTER TABLE '||pgn.nspname||'.'||tbl.relname||' ADD CONSTRAINT '||cons.conname||' FOREIGN KEY ('||(select attname from pg_attribute where attrelid=cons.conrelid and attnum = ANY(cons.conkey))||') REFERENCES '||tblf.relname||' ('||(select attname from pg_attribute where attrelid=cons.confrelid and attnum = ANY(cons.confkey))||') ON UPDATE CASCADE ON DELETE CASCADE;' as sqlstr
from pg_constraint cons
join pg_class tbl on cons.conrelid = tbl.oid
join pg_namespace pgn on pgn.oid = tbl.relnamespace
join pg_class tblf on cons.confrelid = tblf.oid
where contype = 'f'
ORDER BY sqlstr desc

Find dependent objects for a table or view

Background
When dropping (or replacing) objects in PostgreSQL, if there are dependencies, the drop will fail (without specifying CASCADE).
Problem
The error message returned by the database does not list the dependent objects.
Example Solution
The query might look something like:
SELECT * FROM information_schema i, pg_depend pd WHERE
i.object_id = pd.object_id AND
i.object_type = 'TABLE' AND
i.object_schema = 'public' AND
i.object_name = 'table_with_dependents';
The objid is missing.
Related
http://postgresql.1045698.n5.nabble.com/information-schema-problem-td2144069.html
http://www.alberton.info/postgresql_meta_info.html
Question
How do you generate a list of dependent objects by name and type?
The suggested solution didn't work for me with postgresql 9.1.4
this worked:
SELECT dependent_ns.nspname as dependent_schema
, dependent_view.relname as dependent_view
, source_ns.nspname as source_schema
, source_table.relname as source_table
, pg_attribute.attname as column_name
FROM pg_depend
JOIN pg_rewrite ON pg_depend.objid = pg_rewrite.oid
JOIN pg_class as dependent_view ON pg_rewrite.ev_class = dependent_view.oid
JOIN pg_class as source_table ON pg_depend.refobjid = source_table.oid
JOIN pg_attribute ON pg_depend.refobjid = pg_attribute.attrelid
AND pg_depend.refobjsubid = pg_attribute.attnum
JOIN pg_namespace dependent_ns ON dependent_ns.oid = dependent_view.relnamespace
JOIN pg_namespace source_ns ON source_ns.oid = source_table.relnamespace
WHERE
source_ns.nspname = 'my_schema'
AND source_table.relname = 'my_table'
AND pg_attribute.attnum > 0
AND pg_attribute.attname = 'my_column'
ORDER BY 1,2;
The easy way is:
BEGIN;
DROP TABLE tablename CASCADE;
DROP VIEW viewname CASCADE;
ROLLBACK;
For PostgreSQL 9.3 onward use the following view and functions to show any user object dependency. I also updated https://wiki.postgresql.org/wiki/Pg_depend_display.
/**** Usage Examples ****
-- Examine the entire object hierarchy
SELECT report.dependency_tree('');
-- Dependencies for any relations with names containing match (in regular expression)
SELECT report.dependency_tree('match');
-- Dependencies for relations person & address
SELECT report.dependency_tree('{person,address}'::text[]);
-- Dependencies for function slice
SELECT report.dependency_tree(ARRAY['slice'::regproc]);
-- Dependencies for type hstore
SELECT report.dependency_tree(ARRAY['hstore'::regtype]);
-- Dependencies for triggers by the name updated
SELECT report.dependency_tree(ARRAY(
SELECT oid FROM pg_trigger WHERE tgname ~ 'updated'
));
-- Dependencies for foreign key constraint names starting with product
SELECT report.dependency_tree(ARRAY(
SELECT oid FROM pg_constraint
WHERE conname ~ '^product.*_fk'
));
*/
DROP VIEW IF EXISTS report.dependency;
CREATE OR REPLACE VIEW report.dependency AS
WITH RECURSIVE preference AS (
SELECT 10 AS max_depth
, 16384 AS min_oid -- user objects only
, '^(londiste|pgq|pg_toast)'::text AS schema_exclusion
, '^pg_(conversion|language|ts_(dict|template))'::text AS class_exclusion
, '{"SCHEMA":"00", "TABLE":"01", "TABLE CONSTRAINT":"02", "DEFAULT VALUE":"03",
"INDEX":"05", "SEQUENCE":"06", "TRIGGER":"07", "FUNCTION":"08",
"VIEW":"10", "MATERIALIZED VIEW":"11", "FOREIGN TABLE":"12"}'::json AS type_sort_orders
)
, dependency_pair AS (
SELECT objid
, array_agg(objsubid ORDER BY objsubid) AS objsubids
, upper(obj.type) AS object_type
, coalesce(obj.schema, substring(obj.identity, E'(\\w+?)\\.'), '') AS object_schema
, obj.name AS object_name
, obj.identity AS object_identity
, refobjid
, array_agg(refobjsubid ORDER BY refobjsubid) AS refobjsubids
, upper(refobj.type) AS refobj_type
, coalesce(CASE WHEN refobj.type='schema' THEN refobj.identity
ELSE refobj.schema END
, substring(refobj.identity, E'(\\w+?)\\.'), '') AS refobj_schema
, refobj.name AS refobj_name
, refobj.identity AS refobj_identity
, CASE deptype
WHEN 'n' THEN 'normal'
WHEN 'a' THEN 'automatic'
WHEN 'i' THEN 'internal'
WHEN 'e' THEN 'extension'
WHEN 'p' THEN 'pinned'
END AS dependency_type
FROM pg_depend dep
, LATERAL pg_identify_object(classid, objid, 0) AS obj
, LATERAL pg_identify_object(refclassid, refobjid, 0) AS refobj
, preference
WHERE deptype = ANY('{n,a}')
AND objid >= preference.min_oid
AND (refobjid >= preference.min_oid OR refobjid = 2200) -- need public schema as root node
AND coalesce(obj.schema, substring(obj.identity, E'(\\w+?)\\.'), '') !~ preference.schema_exclusion
AND coalesce(CASE WHEN refobj.type='schema' THEN refobj.identity
ELSE refobj.schema END
, substring(refobj.identity, E'(\\w+?)\\.'), '') !~ preference.schema_exclusion
GROUP BY objid, obj.type, obj.schema, obj.name, obj.identity
, refobjid, refobj.type, refobj.schema, refobj.name, refobj.identity, deptype
)
, dependency_hierarchy AS (
SELECT DISTINCT
0 AS level,
refobjid AS objid,
refobj_type AS object_type,
refobj_identity AS object_identity,
--refobjsubids AS objsubids,
NULL::text AS dependency_type,
ARRAY[refobjid] AS dependency_chain,
ARRAY[concat(preference.type_sort_orders->>refobj_type,refobj_type,':',refobj_identity)] AS dependency_sort_chain
FROM dependency_pair root
, preference
WHERE NOT EXISTS
(SELECT 'x' FROM dependency_pair branch WHERE branch.objid = root.refobjid)
AND refobj_schema !~ preference.schema_exclusion
UNION ALL
SELECT
level + 1 AS level,
child.objid,
child.object_type,
child.object_identity,
--child.objsubids,
child.dependency_type,
parent.dependency_chain || child.objid,
parent.dependency_sort_chain || concat(preference.type_sort_orders->>child.object_type,child.object_type,':',child.object_identity)
FROM dependency_pair child
JOIN dependency_hierarchy parent ON (parent.objid = child.refobjid)
, preference
WHERE level < preference.max_depth
AND child.object_schema !~ preference.schema_exclusion
AND child.refobj_schema !~ preference.schema_exclusion
AND NOT (child.objid = ANY(parent.dependency_chain)) -- prevent circular referencing
)
SELECT * FROM dependency_hierarchy
ORDER BY dependency_chain ;
-- Procedure to report depedency tree using regexp search pattern (relation-only)
CREATE OR REPLACE FUNCTION report.dependency_tree(search_pattern text)
RETURNS TABLE(dependency_tree text)
SECURITY DEFINER LANGUAGE SQL
AS $function$
WITH target AS (
SELECT objid, dependency_chain
FROM report.dependency
WHERE object_identity ~ search_pattern
)
, list AS (
SELECT
format('%*s%s %s', -4*level
, CASE WHEN object_identity ~ search_pattern THEN '*' END
, object_type, object_identity
) AS dependency_tree
, dependency_sort_chain
FROM target
JOIN report.dependency report
ON report.objid = ANY(target.dependency_chain) -- root-bound chain
OR target.objid = ANY(report.dependency_chain) -- leaf-bound chain
WHERE length(search_pattern) > 0
-- Do NOT waste search time on blank/null search_pattern.
UNION
-- Query the entire dependencies instead.
SELECT
format('%*s%s %s', 4*level, '', object_type, object_identity) AS depedency_tree
, dependency_sort_chain
FROM report.dependency
WHERE length(coalesce(search_pattern,'')) = 0
)
SELECT dependency_tree FROM list
ORDER BY dependency_sort_chain;
$function$ ;
-- Procedure to report depedency tree by specific relation name(s) (in text array)
CREATE OR REPLACE FUNCTION report.dependency_tree(object_names text[])
RETURNS TABLE(dependency_tree text)
SECURITY DEFINER LANGUAGE SQL
AS $function$
WITH target AS (
SELECT objid, dependency_chain
FROM report.dependency
JOIN unnest(object_names) AS target(objname) ON objid = objname::regclass
)
, list AS (
SELECT DISTINCT
format('%*s%s %s', -4*level
, CASE WHEN object_identity = ANY(object_names) THEN '*' END
, object_type, object_identity
) AS dependency_tree
, dependency_sort_chain
FROM target
JOIN report.dependency report
ON report.objid = ANY(target.dependency_chain) -- root-bound chain
OR target.objid = ANY(report.dependency_chain) -- leaf-bound chain
)
SELECT dependency_tree FROM list
ORDER BY dependency_sort_chain;
$function$ ;
-- Procedure to report depedency tree by oid
CREATE OR REPLACE FUNCTION report.dependency_tree(object_ids oid[])
RETURNS TABLE(dependency_tree text)
SECURITY DEFINER LANGUAGE SQL
AS $function$
WITH target AS (
SELECT objid, dependency_chain
FROM report.dependency
JOIN unnest(object_ids) AS target(objid) USING (objid)
)
, list AS (
SELECT DISTINCT
format('%*s%s %s', -4*level
, CASE WHEN report.objid = ANY(object_ids) THEN '*' END
, object_type, object_identity
) AS dependency_tree
, dependency_sort_chain
FROM target
JOIN report.dependency report
ON report.objid = ANY(target.dependency_chain) -- root-bound chain
OR target.objid = ANY(report.dependency_chain) -- leaf-bound chain
)
SELECT dependency_tree FROM list
ORDER BY dependency_sort_chain;
$function$ ;
Include nested views in the query as follows:
WITH RECURSIVE view_deps AS (
SELECT DISTINCT dependent_ns.nspname as dependent_schema
, dependent_view.relname as dependent_view
, source_ns.nspname as source_schema
, source_table.relname as source_table
FROM pg_depend
JOIN pg_rewrite ON pg_depend.objid = pg_rewrite.oid
JOIN pg_class as dependent_view ON pg_rewrite.ev_class = dependent_view.oid
JOIN pg_class as source_table ON pg_depend.refobjid = source_table.oid
JOIN pg_namespace dependent_ns ON dependent_ns.oid = dependent_view.relnamespace
JOIN pg_namespace source_ns ON source_ns.oid = source_table.relnamespace
WHERE NOT (dependent_ns.nspname = source_ns.nspname AND dependent_view.relname = source_table.relname)
UNION
SELECT DISTINCT dependent_ns.nspname as dependent_schema
, dependent_view.relname as dependent_view
, source_ns.nspname as source_schema
, source_table.relname as source_table
FROM pg_depend
JOIN pg_rewrite ON pg_depend.objid = pg_rewrite.oid
JOIN pg_class as dependent_view ON pg_rewrite.ev_class = dependent_view.oid
JOIN pg_class as source_table ON pg_depend.refobjid = source_table.oid
JOIN pg_namespace dependent_ns ON dependent_ns.oid = dependent_view.relnamespace
JOIN pg_namespace source_ns ON source_ns.oid = source_table.relnamespace
INNER JOIN view_deps vd
ON vd.dependent_schema = source_ns.nspname
AND vd.dependent_view = source_table.relname
AND NOT (dependent_ns.nspname = vd.dependent_schema AND dependent_view.relname = vd.dependent_view)
)
SELECT *
FROM view_deps
ORDER BY source_schema, source_table;
If you care about specific table attributes add this to the top portion of the recursive CTE:
JOIN pg_attribute ON pg_depend.refobjid = pg_attribute.attrelid
AND pg_depend.refobjsubid = pg_attribute.attnum
...
WHERE
source_ns.nspname = 'my_schema'
AND source_table.relname = 'my_table'
AND pg_attribute.attnum > 0
AND pg_attribute.attname = 'my_column'
pg_constraint contains all constrains in the database you can list the oid of dependent tables using confrelid and conrelid from all all the foreign key constraints
query looks like this
select confrelid,conrelid from pg_constraint where contype='f';