Postgres dynamically update constraint foreign key - postgresql

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

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

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

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

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

amazon redshift utility - view v_generate_tbl_ddl not returning primary key constraint in ddl after table has been populated

I'm using the amazon redshift utility v_generate_tbl_ddl to generate a table ddl, but have found that after my table has been populated the primary key constraint doesn't show (example below)
Structure table creation:
DROP TABLE IF EXISTS temp_table;
CREATE TABLE temp_table
(
id INTEGER NOT NULL,
text VARCHAR,
PRIMARY KEY (id)
)
DISTSTYLE ALL SORTKEY (id);
Checking the ddl after the table has been created shows the primary key constraint is present:
SELECT *
FROM admin.v_generate_tbl_ddl
WHERE tablename = 'temp_table'
AND schemaname = 'myschema';
Inserting some data:
COPY temp_table
FROM 's3://s3_bucket_location/manifest_file_temp_table.json' CREDENTIALS 'aws_access_key_id=...;aws_secret_access_key=...' DELIMITER AS '|' gzip manifest
Checking again the ddl and now the primary key constraint isn't there
SELECT *
FROM admin.v_generate_tbl_ddl
WHERE tablename = 'temp_table'
AND schemaname = 'myschema';
I'm sure the primary key is still there as I've doubled checked using:
SELECT pg_table_def.tablename,
pg_table_def.column
FROM pg_table_def
WHERE schemaname = 'myschema'
AND tablename = 'temp_table_pkey';
Digging into the sql of v_generate_tbl_ddl it is this section that isn't returning rows after the table has been populated, but I can't figure out why this stops returning rows? Any help here would be appreciated
SELECT n.nspname AS schemaname,
c.relname AS tablename,
200000000 + CAST(con.oid AS INT) AS seq,
('\t,' + pg_get_constraintdef(con.oid))::CHARACTER VARYING (500) AS ddl
FROM pg_constraint AS con
INNER JOIN pg_class AS c
ON c.relnamespace = con.connamespace
AND c.relfilenode = con.conrelid
INNER JOIN pg_namespace AS n ON n.oid = c.relnamespace
WHERE c.relkind = 'r'
AND pg_get_constraintdef (con.oid) NOT LIKE 'FOREIGN KEY%'
AND c.relname = 'temp_table'
ORDER BY seq