in postgres, get name & schema of relations on which materialized view depends - postgresql

I'm looking to print schema & name of relations which all the materialized views in a schema depend on:
select c.relname, d.classid, d.objid,
pg_describe_object(
d.classid, d.objid, d.objsubid)
from pg_class c
join pg_namespace n on c.relnamespace = n.oid
left join pg_depend d on c.oid = d.objid
where n.nspname = 'direct' and d.deptype = 'n'
This gives something like:
relname | relname | classid | objid | pg_describe_object
------------------------+---------+---------+-------+---------------------------------------------------
cases | | 2618 | 33736 | rule _RETURN on materialized view case_categories
benefit_investigations | | 2618 | 33928 | rule _RETURN on materialized view bi_intervals
The description returned gives some hint, but it doesn't contain the schema of the relation. How do I get the actual dependency schema and name? [NB I'm using postgres 9.6]

Here you go:
SELECT DISTINCT view_cs.nspname, view_c.relname, tab_cs.nspname, tab_c.relname
FROM pg_depend view_d
JOIN pg_class view_c ON view_c.oid = view_d.refobjid AND view_c.relkind = 'm'
JOIN pg_type view_ct ON view_ct.oid = view_c.reltype
JOIN pg_namespace view_cs ON view_cs.oid = view_ct.typnamespace
JOIN pg_depend tab_d ON tab_d.objid = view_d.objid
JOIN pg_class tab_c ON tab_c.oid = tab_d.refobjid AND tab_c.relkind = 'r'
JOIN pg_type tab_ct ON tab_ct.oid = tab_c.reltype
JOIN pg_namespace tab_cs ON tab_cs.oid = tab_ct.typnamespace
WHERE view_d.deptype = 'n'
The trick is that you have to JOIN pg_depend twice - once for view-rule relation and once for rule-table relation.

Add
AND view_c.relname != tab_c.relname
to WHERE statement of the accepted answer by #Boris to avoid showing dependency of the table on itself

Related

How to get column name from pg_constraint

I need to get a list of constraints from db and match them with columns they are related to
For this task I'm going to use pg_constraint. Here is the query that I use
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 conrelid::regclass::text = 'test' order by contype;
As you can see I'm interested in constraints for table called test
Here is the output
table_from | conname | pg_get_constraintdef
------------+----------------+----------------------
test | test_age_check | CHECK ((age > 0))
But I want to get a column name for this constraint using query above (In this case column is age). According to doc Column constraints are not treated specially. But maybe there are some ways I can use to retrieve column name from pg_constraint ?
pg_constrint.conkey holds an array of the columns ordinals in the table. You can use them to join pg_attribute. Here the ordinal is in the column attnum.
SELECT c.conrelid::regclass AS table_from,
c.conname,
pg_get_constraintdef(c.oid),
a.attname
FROM pg_constraint c
INNER JOIN pg_namespace n
ON n.oid = c.connamespace
CROSS JOIN LATERAL unnest(c.conkey) ak(k)
INNER JOIN pg_attribute a
ON a.attrelid = c.conrelid
AND a.attnum = ak.k
WHERE c.conrelid::regclass::text = 'test'
ORDER BY c.contype;

Get information about table partitions

I have a partitioned table out of main table using range.
CREATE TABLE public.partition1 PARTITION OF public.maintable
FOR VALUES FROM ('2017-01-01 00:00:00') TO ('2050-01-01 00:00:00')
How can i get the Values range information using a query to postgres.
I have used a query that at least gives me information for the main and partitioned tables, but i cannot seem to find a way to access the value range
FROM ('2017-01-01 00:00:00') TO ('2050-01-01 00:00:00')
assigned to table partition1
Query used to get partition table information
WITH RECURSIVE partition_info
(relid,
relname,
relsize,
relispartition,
relkind) AS
(
(SELECT oid AS relid,
relname,
pg_relation_size(oid) AS relsize,
relispartition,
relkind
FROM pg_catalog.pg_class
WHERE relname = 'completedorders' AND
relkind = 'p')
UNION ALL
(SELECT
c.oid AS relid,
c.relname AS relname,
pg_relation_size(c.oid) AS relsize,
c.relispartition AS relispartition,
c.relkind AS relkind
FROM partition_info AS p,
pg_catalog.pg_inherits AS i,
pg_catalog.pg_class AS c
WHERE p.relid = i.inhparent AND
c.oid = i.inhrelid AND
c.relispartition = true)
)
SELECT * FROM partition_info;
The following query provides the information about partitions as well. From there on its just string manipulation in order to get further information.
Note: you will have to change the name of the table in the query.
with recursive inh as (
select i.inhrelid, null::text as parent
from pg_catalog.pg_inherits i
join pg_catalog.pg_class cl on i.inhparent = cl.oid
join pg_catalog.pg_namespace nsp on cl.relnamespace = nsp.oid
where nsp.nspname = 'public' ---<< change table schema here
and cl.relname = 'tablename' ---<< change table name here
union all
select i.inhrelid, (i.inhparent::regclass)::text
from inh
join pg_catalog.pg_inherits i on (inh.inhrelid = i.inhparent)
)
select c.relname as partition_name,
n.nspname as partition_schema,
pg_get_expr(c.relpartbound, c.oid, true) as partition_expression,
pg_get_expr(p.partexprs, c.oid, true) as sub_partition,
parent,
case p.partstrat
when 'l' then 'LIST'
when 'r' then 'RANGE'
end as sub_partition_strategy
from inh
join pg_catalog.pg_class c on inh.inhrelid = c.oid
join pg_catalog.pg_namespace n on c.relnamespace = n.oid
left join pg_partitioned_table p on p.partrelid = c.oid
order by n.nspname, c.relname

pg_catalog.pg_class.relfilenode after truncate

I have a view depends on a table: create view hello_v as select * from hello;
To retrieve the referenced table name from a view name, I think pg_class and pg_depend tables may be useful.
Truncating on the table results in no entry in pg_depend.
How to come back pg_depend.refobjid which is equals to pg_catalog.relfilenode ?
I can reproduce my point of view:
select version();
create table hello(name text);
create view hello_v as select * from hello;
select c.relname, c.relfilenode from pg_catalog.pg_class c inner join pg_catalog.pg_depend d on d.refobjid = c.relfilenode
where c.relname in ('hello', 'hello_v') group by relname, relfilenode order by c.relname;
relname | relfilenode
---------+-------------
hello | 305818
hello_v | 305824
truncate hello;
select c.relname, c.relfilenode from pg_catalog.pg_class c inner join pg_catalog.pg_depend d on d.refobjid = c.relfilenode
where c.relname in ('hello', 'hello_v') group by relname, relfilenode order by c.relname;
relname | relfilenode
---------+-------------
hello_v | 305824
-- I hope to exist a record where relname = hello and relfilenode = xxx
Or simply:
create table hello(name text);
select c.relname, c.relfilenode from pg_catalog.pg_class c inner join pg_catalog.pg_depend d on d.refobjid = c.relfilenode
where c.relname = 'hello' group by relname, relfilenode order by c.relname;
truncate hello;
select c.relname, c.relfilenode from pg_catalog.pg_class c inner join pg_catalog.pg_depend d on d.refobjid = c.relfilenode
where c.relname = 'hello' group by relname, relfilenode order by c.relname;
Thanks.
your join condition is wrong. You want to join against c.oid, not relfilenode.
This shows your table correctly:
select c.relname, c.relfilenode from pg_catalog.pg_class c inner join pg_catalog.pg_depend d on d.refobjid = c.oid
where c.relname = 'hello' group by relname, relfilenode order by c.relname;

List columns with indexes in Amazon Redshift

I need to query Redshift metadata to get a list of table columns that includes information whether the column is part of primary key or not.
There is a post already List columns with indexes in PostgreSQL that has an answer for PostgreSQL, however unfortunately, it fails on Redshift with "ERROR: 42809: op ANY/ALL (array) requires array on right side"
I figured out how to do it with the help of this https://bitbucket.org/zzzeek/sqlalchemy/pull-request/6/sqlalchemy-to-support-postgresql-80/diff
SELECT attname column_name, attnotnull,
format_type(atttypid, atttypmod) as column_type, atttypmod,
i.indisprimary as primary_key,
col_description(attrelid, attnum) as description
FROM pg_attribute c
LEFT OUTER JOIN pg_index i
ON c.attrelid = i.indrelid AND i.indisprimary AND
c.attnum = ANY(string_to_array(textin(int2vectorout(i.indkey)), ' '))
where c.attnum > 0 AND NOT c.attisdropped AND c.attrelid = :tableOid
order by attnum
The following worked for me:
SELECT n.nspname as schema_name,
t.relname as table_name,
i.relname as index_name,
c.contype as index_type,
a.attname as column_name,
a.attnum AS column_position
FROM pg_class t
INNER JOIN pg_index AS ix ON t.oid = ix.indrelid
INNER JOIN pg_constraint AS c ON ix.indrelid = c.conrelid
INNER JOIN pg_class AS i ON i.oid = ix.indexrelid
INNER JOIN pg_attribute AS a ON a.attrelid = t.oid
AND a.attnum= ANY(string_to_array(textin(int2vectorout(ix.indkey)),' ')::int[])
INNER JOIN pg_namespace AS n ON n.oid = t.relnamespace;
You can leverage the table DDL view AWS published a few months ago (https://github.com/awslabs/amazon-redshift-utils/blob/master/src/AdminViews/v_generate_tbl_ddl.sql) by picking out the constraint component and parsing out the key columns:
select schemaname,tablename, substring(ddl,charindex('(',ddl)+1, charindex(')',ddl)-1-charindex('(',ddl))
from
(
SELECT
n.nspname AS schemaname
,c.relname AS tablename
,200000000 + CAST(con.oid AS INT) AS seq
,'\t,' + pg_get_constraintdef(con.oid) 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'
ORDER BY seq
)
Note that this query also gives you foreign key columns. It's easy enough to filter those out by appending the query with
where ddl like '%PRIMARY KEY%'
Use below query:
select * from pg_table_def where tablename = 'mytablename'
This will give you all columns for table along with their data type , encoding and if it has sort key or dist key.

Find primary key of table in Postgresql from information_schema with only SELECT

I am using the following query to discover (1) the primary key columns and (2) if the columns have a default value from the information_schema in Postgresql 9.1.
SELECT kcu.column_name, (c.column_default is not null) AS has_default
FROM information_schema.key_column_usage kcu
JOIN information_schema.table_constraints tc ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.columns c on c.column_name = kcu.column_name and c.table_name = kcu.table_name
WHERE tc.constraint_type = 'PRIMARY KEY' AND kcu.table_name like :tablename
It works fine when run as the database owner, but when I run it as a "read-only" user (which I need to do in my application), it returns no data. Some research revealed that the problem is the information.table_constraints view; from the documentation:
The view table_constraints contains all constraints belonging to tables that the current user owns or has some non-SELECT privilege on.
So in order to retrieve table_constraints, my login role needs more than SELECT on the table? Is there no way to get the information from information_schema without giving write permissions to the login role?
Use pg_* views instead of information_schema views. pg_* views display all information regardles of granted privileges.
Try this query:
select
t.relname as table_name,
i.relname as index_name,
a.attname as column_name,
d.adsrc as default_value
from
pg_class t
join pg_attribute a on a.attrelid = t.oid
join pg_index ix on t.oid = ix.indrelid AND a.attnum = ANY(ix.indkey)
join pg_class i on i.oid = ix.indexrelid
left join pg_attrdef d on d.adrelid = t.oid and d.adnum = a.attnum
where
t.relkind = 'r'
and t.relname in ( 'aa', 'bb', 'cc' )
order by
t.relname,
i.relname,
a.attnum;
An example of the query results:
create table aa(
x int primary KEY
);
create table bb(
x int default 1,
constraint pk primary key ( x )
);
create table cc(
x int default 20,
y varchar(10) default 'something',
constraint cc_pk primary key ( x, y )
);
table_name | index_name | column_name | default_value
------------+------------+-------------+--------------------------------
aa | aa_pkey | x |
bb | pk | x | 1
cc | cc_pk | x | 20
cc | cc_pk | y | 'something'::character varying
This is correct, the official postgresql query is below
http://wiki.postgresql.org/wiki/Retrieve_primary_key_columns
if schema is needed the query is as follows
SELECT
pg_attribute.attname,
format_type(pg_attribute.atttypid, pg_attribute.atttypmod)
FROM pg_index, pg_class, pg_attribute, pg_namespace
WHERE
pg_class.oid = 'MY TABLE'::regclass AND
indrelid = pg_class.oid AND
nspname = 'MY CLASS' AND
pg_class.relnamespace = pg_namespace.oid AND
pg_attribute.attrelid = pg_class.oid AND
pg_attribute.attnum = any(pg_index.indkey)
AND indisprimary
The difference can be up to 6000~7000 times. The pg_ one runs often in 0.56ms where the schema based one can run up 6500ms. This is a huge difference especially if you have a high load on the server.
There is another way to provide access for data in information_schema.
A. Envelop SQL in a function with SECURITY DEFINER modifier
CREATE FUNCTION fn_inf(name)
RETURNS TABLE (column_name information_schema.sql_identifier, has_default bool)
LANGUAGE SQL
SECURITY DEFINER
AS $$
SELECT kcu.column_name, (c.column_default is not null) AS has_default
FROM information_schema.key_column_usage kcu
JOIN information_schema.table_constraints tc ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.columns c on c.column_name = kcu.column_name and c.table_name = kcu.table_name
WHERE tc.constraint_type = 'PRIMARY KEY' AND kcu.table_name like $1;
$$;
B. GRANT EXECUTE to user read_only
GRANT EXECUTE ON FUNCTION fn_inf to read_only;
C. Use as user read_only
SELECT * FROM fn_inf('spatial_ref_sys');
column_name
has_default
srid
false