Function for generating queries to restore constraints after drop based on table with constraint information - postgresql

I have to drop all constraints in my Postgres database to update datatypes used by PKs and FKs. Before that I want to make sure that I can rebuild those constraints after the update. At first I execute the following code to get a table with three columns table_from, conname and pg_get_constraintdef, containing all constraint information.
select conrelid::regclass AS table_from, conname, pg_get_constraintdef(c.oid)
from pg_constraint c
join pg_namespace n ON n.oid = c.connamespace
where contype in ('f', 'p','c','u') order by contype
table_from contains the table name, e.g. foo. conname the name of the constraint, e.g. fk_foo_2_bar and pg_get_constraintdef contains the constraint definition, e.g.
FOREIGN KEY (foo_id) REFERENCES bar(bar_id) DEFERRABLE INITIALLY DEFERRED
How can I generate all the queries for creating my constraints? I don't want to do it by hand, because there are 1000+ constraints active.

Most of the work was actually done already...
select 'alter table ' || conrelid::regclass || ' add constraint ' || conname || ' ' || pg_get_constraintdef(c.oid)
from pg_constraint c
join pg_namespace n ON n.oid = c.connamespace
where contype in ('f', 'p','c','u') order by contype

Related

Postgres Check Constraint definition

I want to write a query to list the check constraints in a database along with its constraint definition.
I am not able to find a column to find the check constraint definition just like the search condition column in oracle all_constraints table.
Is there a PG alternate for this?
The catalog pg_constraint stores check, primary key, unique, foreign key, and exclusion constraints on tables and contype column saves data about the type of the constraint i,e
c = check constraint
f = foreign key constraint
p = primary key constraint
u = unique constraint
t = constraint trigger
x = exclusion constraint
select pgc.conname as constraint_name,
ccu.table_schema as table_schema,
ccu.table_name,
ccu.column_name,
pg_get_constraintdef(pgc.oid)
from pg_constraint pgc
join pg_namespace nsp on nsp.oid = pgc.connamespace
join pg_class cls on pgc.conrelid = cls.oid
left join information_schema.constraint_column_usage ccu
on pgc.conname = ccu.constraint_name
and nsp.nspname = ccu.constraint_schema
where contype ='c'
order by pgc.conname;

Get all the column names having a Foreign Key contraint

I am using the PostgreSQL. I want to write a query that returns all the column names having foreign key constraint and also the name of the table these columns they refer to.
As far as I can see, the information_schema views don't give you the column names, so you'll have to use the catalog:
SELECT c.conrelid::regclass AS source_table,
a.attname AS column_name,
k.n AS position,
c.confrelid::regclass AS referenced_table
FROM pg_constraint AS c
CROSS JOIN LATERAL unnest(c.conkey) WITH ORDINALITY AS k(attnum, n)
JOIN pg_attribute AS a
ON k.attnum = a.attnum AND c.conrelid = a.attrelid
WHERE c.contype = 'f'
ORDER BY c.conrelid::regclass::text, k.n;
To get the data for only a specific table, add the following to the WHERE condition:
AND c.conrelid = 'mytable'::regclass

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

postgresql drop table

I have two tables (tbl and tbl_new) that both use the same sequence (tbl_id_seq). I'd like to drop one of those tables. On tbl, I've removed the modifier "not null default nextval('tbl_id_seq'::regclass)" but that modifier remains on tbl_new. I'm getting the following error:
ERROR: cannot drop table tbl because other objects depend on it
DETAIL: default for table tbl_new column id depends on sequence tbl_id_seq
After reviewing http://www.postgresql.org/docs/9.1/static/sql-droptable.html
It looks like there is only CASCADE and RESTRICT as options.
You need to decouple the sequence and the table it "belongs" to:
ALTER SEQUENCE "tbl_id_seq" OWNED BY NONE;
I suppose it was created automatically (and "bound") by defining the tbl_id field of tbl as SERIAL.
To find sequences and all tables that depend on them via column default:
SELECT sn.nspname || '.' || s.relname AS seq
,tn.nspname || '.' || t.relname AS tbl
FROM pg_class s
JOIN pg_namespace sn ON sn.oid = s.relnamespace
LEFT JOIN pg_depend d ON d.refobjid = s.oid AND d.deptype <> 'i'
LEFT JOIN pg_attrdef ad ON ad.oid = d.objid
LEFT JOIN pg_class t ON t.oid = ad.adrelid
LEFT JOIN pg_namespace tn ON tn.oid = t.relnamespace
WHERE s.relkind = 'S'
AND s.relname ~~ '%part_of_seq_name%' -- enter search term here
ORDER BY 1,2;
Now with LEFT JOIN to show "free-standing" sequences as well.
You can then use the method #Milen posted to make the sequence "free-standing".
I posted a related answer a few days ago.