PostgreSQL - List all UNIQUE constraints in a schema - postgresql

I'd like to figure a better way to list all UNIQUE constraints in a specific schema. I was able to list them using this below query from old answers to similar questions:
SELECT
tc.table_schema,
tc.constraint_name,
tc.table_name,
kcu.column_name,
ccu.table_schema AS foreign_table_schema,
ccu.table_name AS foreign_table_name,
ccu.column_name AS foreign_column_name
FROM
information_schema.table_constraints AS tc
JOIN
information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name AND tc.table_schema = kcu.table_schema
JOIN
information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name AND ccu.table_schema = tc.table_schema
WHERE
tc.constraint_type = 'UNIQUE' AND tc.table_schema = 'mySchema'
But this query don't return pretty results. For example if I have this table here:
CREATE TABLE myTable (
id int not null primary key,
col1 text,
col2 text,
col3 text,
unique (col1, col2, col3)
)
The SELECT statement will return all 9 rows as a result for each column with the other columns contributed in the UNIQUE constraint -including itself-.
Sample output
"public" "mytable_col1_col2_col3_key" "mytable" "col1" "public" "mytable" "col1"
"public" "mytable_col1_col2_col3_key" "mytable" "col1" "public" "mytable" "col2"
"public" "mytable_col1_col2_col3_key" "mytable" "col1" "public" "mytable" "col3"
"public" "mytable_col1_col2_col3_key" "mytable" "col2" "public" "mytable" "col1"
"public" "mytable_col1_col2_col3_key" "mytable" "col2" "public" "mytable" "col2"
"public" "mytable_col1_col2_col3_key" "mytable" "col2" "public" "mytable" "col3"
"public" "mytable_col1_col2_col3_key" "mytable" "col3" "public" "mytable" "col1"
"public" "mytable_col1_col2_col3_key" "mytable" "col3" "public" "mytable" "col2"
"public" "mytable_col1_col2_col3_key" "mytable" "col3" "public" "mytable" "col3"
Expected output
"public" "mytable_col1_col2_col3_key" "mytable" ["col1","col2","col3"]
So can anyone help me fix this query to return a prettified representation of the UNIQUE constraint?

You can query the metadata directly:
SELECT c.conrelid::regclass AS table_name,
c.conname AS constraint_name,
array_agg(a.attname ORDER BY k.n) AS columns
FROM pg_constraint AS c
CROSS JOIN LATERAL unnest(c.conkey) WITH ORDINALITY AS k(c,n)
JOIN pg_attribute AS a
ON a.attnum = k.c AND a.attrelid = c.conrelid
WHERE c.contype = 'u'
AND c.connamespace = 'mySchema'::regnamespace
GROUP BY c.oid, c.conrelid, c.conname;

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.

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

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;

postgresql: INSERT INTO statement with select query

I have to insert some data in my table of existing client_id column , so i am using select with insert
INSERT into 'my_table' (column1, client_id, column3) VALUES (val1,select distinct client_id from 'my_table', val3)
I need client_id from the same table my_table and i need client_ids in insert statement.
SELECT DISTINCT client_id FROM my_table gives me 113 client_id so i want to insert some row for each 113 client using the above approach.
I did this query
INSERT INTO client_notification_preferences (client_id, object_type , frequency,created_at,updated_at) SELECT DISTINCT client_id, 'ClientShipment',1, CURRENT_TIMESTAMP , CURRENT_TIMESTAMP FROM client_notification_preferences;
but this gives me this error
create_table "client_notification_preferences", id: :uuid, default: "uuid_generate_v4()", force: :cascade do |t|
t.uuid "client_id"
t.string "object_type"
t.integer "frequency"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
if val1 and val3 are variables and client_id is a field from my_table you can use from below code.
INSERT into 'my_table' (column1, client_id, column3) select distinct val1,
client_id,val3 from 'my_table'
for example
INSERT into 'my_table' (column1, client_id, column3) select distinct 1, client_id,2 from 'my_table'

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

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