Find all partition tables "inheriting" from master table - postgresql

Say I have a table, "foo", with partition tables "foo1", "foo2", and "foo3". But at the current moment all I know is there are parition tables which inherit from table "foo". How can I find that foo has 3 partitions, foo1, foo2, and foo3?

To list all your partitions (child tables) - tested with PG v9-v13:
SELECT c.relname FROM pg_inherits i JOIN pg_class p ON i.inhparent = p.oid
JOIN pg_class c ON i.inhrelid = c.oid WHERE p.relname='parentTableName';

Use pg_inherits. Example:
create table my_parent_table (id int);
create table my_child_table_no_1 (check (id < 10)) inherits (my_parent_table);
create table my_child_table_no_2 (check (id >= 10)) inherits (my_parent_table);
select relname
from pg_inherits i
join pg_class c on c.oid = inhrelid
where inhparent = 'my_parent_table'::regclass
relname
---------------------
my_child_table_no_1
my_child_table_no_2
(2 rows)
You can also select check constraints using pg_constraint:
select relname "child table", consrc "check"
from pg_inherits i
join pg_class c on c.oid = inhrelid
join pg_constraint on c.oid = conrelid
where contype = 'c'
and inhparent = 'my_parent_table'::regclass
child table | check
---------------------+------------
my_child_table_no_1 | (id < 10)
my_child_table_no_2 | (id >= 10)
(2 rows)

Starting with Postgres 12, there is a built-in function:
select *
from pg_partition_tree('the_table'::regclass)
where parentrelid is not null;

The way I dig into such catalog information is by using psql. Start psql with the -eE options:
psql -eE <your database>
And then show your table:
\d <your table>
This will list all the queries which psql generates to fetch the information from the catalog. This includes inherited tables.
Keep in mind that it is possible that the catalog changes from one major version to another - although for such basic functionality it is unlikely.

Related

Show only list of tables without child partitions

I would like to show only the list of top level tables without child partitioned tables in PostgreSQL. (Currently using PostgreSQL 12.)
\dt in psql gives me all tables, including partitions of tables. I see this:
postgres=# \dt
List of relations
Schema | Name | Type | Owner
--------+------------------------------+-------------------+--------
public | tablea | table | me
public | partitionedtable1 | partitioned table | me
public | partitionedtable1_part1 | table | me
public | partitionedtable1_part2 | table | me
public | partitionedtable1_part3 | table | me
public | tableb | table | me
But I want a list like this, without child partitions of the parent partitioned table:
List of relations
Schema | Name | Type | Owner
--------+------------------------------+-------------------+--------
public | tablea | table | me
public | partitionedtable1 | partitioned table | me
public | tableb | table | me
Query to get all ordinary tables, including root partitioned tables, but excluding all non-root partitioned tables:
SELECT n.nspname AS "Schema"
, c.relname AS "Name"
, CASE c.relkind
WHEN 'p' THEN 'partitioned table'
WHEN 'r' THEN 'ordinary table'
-- more types?
ELSE 'unknown table type'
END AS "Type"
, pg_catalog.pg_get_userbyid(c.relowner) AS "Owner"
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE c.relkind = ANY ('{p,r,""}') -- add more types?
AND NOT c.relispartition -- exclude child partitions
AND n.nspname !~ ALL ('{^pg_,^information_schema$}') -- exclude system schemas
ORDER BY 1, 2;
The manual about relispartition:
... True if table or index is a partition
pg_get_userbyid() is instrumental to get the name of the owning role.
There are more types of "tables" in Postgres 12. The manual about relkind:
r = ordinary table, i = index, S = sequence, t = TOAST table,
v = view, m = materialized view, c = composite type, f =
foreign table, p = partitioned table, I = partitioned index
Postgres 12 also added the meta-command \dP to psql:
The manual:
\dP[itn+] [ pattern ]
Lists partitioned relations. If pattern is specified, only entries whose name matches the pattern are listed. The modifiers t (tables) and i (indexes) can be appended to the command, filtering the kind of relations to list. By default, partitioned tables and indexes are listed.
If the modifier n (“nested”) is used, or a pattern is specified, then non-root partitioned relations are included, and a column is shown displaying the parent of each partitioned relation.
So \dPt gets all root partitioned tables - but not ordinary tables.
Version 1
I can't test this right now, but this ought to give you only top-level tables that are partitioned
select relname
from pg_class
where oid in (select partrelid from pg_partitioned_table);
You should be able to refine/expand that to get more details.
Version 2
Here's a comically verbose solution:
with
partition_parents as (
select relnamespace::regnamespace::text as schema_name,
relname as table_name,
'partition_parent' as info,
*
from pg_class
where relkind = 'p'), -- The parent table is relkind 'p', the partitions are regular tables, relkind 'r'
unpartitioned_tables as (
select relnamespace::regnamespace::text as schema_name,
relname as table_name,
'unpartitioned_table' as info,
*
from pg_class
where relkind = 'r'
and not relispartition
) -- Regular table
select * from partition_parents where schema_name not in ('information_schema','pg_catalog','api','extensions') -- Whatever you've got for schemas
union
select * from unpartitioned_tables where schema_name not in ('information_schema','pg_catalog','api','extensions') -- Whatever you've got for schemas
order by 1,2,3
You should be able to cut the size of that way down to match what you really want. Someone here who really gets the system catalogs should likely be able to provide a more concise version. In the plus column, it's kind of nice to add in the "partition_parent" versus "unpartitioned_table" detail, as above.
If you don't have to use a \d* shortcut, this query should work (though you'll need to filter out schemas not in your search_path):
SELECT relname FROM pg_class WHERE relkind IN ('r','p') AND NOT relispartition;
--do some test in greenplum 6.14
--to find normal table ,partition table
SELECT
*
FROM
(
SELECT
t1.schemaname,
t1.tablename,
t1.tableowner,
CASE
WHEN t3.tablename IS NULL
AND t2.tablename IS NULL THEN
'r'
WHEN t3.tablename IS NOT NULL
AND t2.tablename IS NULL THEN
'p-root' ELSE'p-child'
END AS tabletype,
t4.actionname,
t4.statime
FROM
pg_tables t1
LEFT JOIN pg_partitions t2 ON t1.tablename = t2.partitiontablename
AND t1.schemaname = t2.partitionschemaname
LEFT JOIN ( SELECT DISTINCT schemaname, tablename FROM pg_partitions WHERE schemaname IN ( 'public', 'ods', 'tmp' ) ) t3 ON t1.tablename = t3.tablename
AND t1.schemaname = t3.schemaname
LEFT JOIN (
SELECT
*
FROM
(
SELECT
schemaname,
objname,
actionname,
statime,
ROW_NUMBER ( ) OVER ( PARTITION BY schemaname, objname ORDER BY statime DESC ) AS rn
FROM
pg_catalog.pg_stat_operations
WHERE
classname = 'pg_class'
AND schemaname IN ( 'public', 'ods', 'tmp' )
AND actionname IN ( 'CREATE', 'ALTER' )
) n
WHERE
n.rn = 1
) t4 ON t1.schemaname = t4.schemaname
AND t1.tablename = t4.objname
WHERE
t1.schemaname IN ( 'public', 'ods', 'tmp' )
) o
WHERE
tabletype IN ( 'r', 'p-root' )
ORDER BY
1,
2,
3,
4

Get table size of partitioned table (Postgres 10+)

I came across this query on Postgres weekly which shows tables, their sizes, toast sizes and index sizes in bytes:
SELECT
relname AS table_name,
pg_size_pretty(pg_total_relation_size(relid)) AS total,
pg_size_pretty(pg_relation_size(relid)) AS internal,
pg_size_pretty(pg_table_size(relid) - pg_relation_size(relid)) AS external,
pg_size_pretty(pg_indexes_size(relid)) AS indexes
FROM pg_catalog.pg_statio_user_tables
ORDER BY pg_total_relation_size(relid) DESC;
I know that Postgres is creating a table for each partition so I am getting entries for each partition separately, but is there a way to get one row per table, regardless of whether this table is partitioned or not?
Going by instructions from #Laurenz Albe I created a query that satisfies my needs. This will get total memory for all partitioned tables from specific database.
SELECT
pi.inhparent::regclass AS parent_table_name,
pg_size_pretty(sum(pg_total_relation_size(psu.relid))) AS total,
pg_size_pretty(sum(pg_relation_size(psu.relid))) AS internal,
pg_size_pretty(sum(pg_table_size(psu.relid) - pg_relation_size(psu.relid))) AS external, -- toast
pg_size_pretty(sum(pg_indexes_size(psu.relid))) AS indexes
FROM pg_catalog.pg_statio_user_tables psu
JOIN pg_class pc ON psu.relname = pc.relname
JOIN pg_database pd ON pc.relowner = pd.datdba
JOIN pg_inherits pi ON pi.inhrelid = pc.oid
WHERE pd.datname = :database_name
GROUP BY pi.inhparent
ORDER BY sum(pg_total_relation_size(psu.relid)) DESC;
Note that in the case when we have partitions of partitions, this will not have one row for the root table, but every parent table will have it's own row
This gives table size per parent table even if we have multiple partition levels:
WITH RECURSIVE tables AS (
SELECT
c.oid AS parent,
c.oid AS relid,
1 AS level
FROM pg_catalog.pg_class c
LEFT JOIN pg_catalog.pg_inherits AS i ON c.oid = i.inhrelid
-- p = partitioned table, r = normal table
WHERE c.relkind IN ('p', 'r')
-- not having a parent table -> we only get the partition heads
AND i.inhrelid IS NULL
UNION ALL
SELECT
p.parent AS parent,
c.oid AS relid,
p.level + 1 AS level
FROM tables AS p
LEFT JOIN pg_catalog.pg_inherits AS i ON p.relid = i.inhparent
LEFT JOIN pg_catalog.pg_class AS c ON c.oid = i.inhrelid AND c.relispartition
WHERE c.oid IS NOT NULL
)
SELECT
parent ::REGCLASS AS table_name,
array_agg(relid :: REGCLASS) AS all_partitions,
pg_size_pretty(sum(pg_total_relation_size(relid))) AS pretty_total_size,
sum(pg_total_relation_size(relid)) AS total_size
FROM tables
GROUP BY parent
ORDER BY sum(pg_total_relation_size(relid)) DESC
Sure, but you'd have to join with a bunch of other catalog tables and use GROUP BY.
Attribute relkind from he catalog pg_class will tell you if a relation is partitioned (p) or not (r).
The catalog pg_inherits will tell you which partition (inhrelid) belongs to which partitioned table (inhparent).
Since partitions can be partitioned again, you will have to write a recursive common table expression if you want to cover all bases.

Getting a column comment in PostgreSql [duplicate]

I'm running a project on a Postgres database and need to retrieve the comments on columns within the DB to be used as table headings and such. I have seen that there are a couple of built in functions (pg_description and col_description) but i haven't been able to find examples on how to use them and playing around with them has proved pretty futile.
So I was wondering if any has been able to do this before and if so, how?
select
c.table_schema,
c.table_name,
c.column_name,
pgd.description
from pg_catalog.pg_statio_all_tables as st
inner join pg_catalog.pg_description pgd on (
pgd.objoid = st.relid
)
inner join information_schema.columns c on (
pgd.objsubid = c.ordinal_position and
c.table_schema = st.schemaname and
c.table_name = st.relname
);
It all works by oid,
mat=> SELECT c.oid FROM pg_catalog.pg_class c WHERE c.relname = 'customers';
oid
-------
23208
(1 row)
Now, I have the oid for that table, so I can ask :
mat=> select pg_catalog.obj_description(23208);
obj_description
-------------------
Customers
(1 row)
Then, I can ask for the description of the fourth column :
mat=> select pg_catalog.col_description(23208,4);
col_description
-----------------------------------------
Customer codes, CHS, FACTPOST, POWER...
(1 row)
If you want to know which queries does psql run when you do \dt+ or \d+ customers, just run it with -E.
Just to be here if somebody will need it.
There are many answers here, but none of them was as simple as I would like it to be. So, based on previous answers and current postgres 9.4, I have created this query:
SELECT
obj_description(format('%s.%s',isc.table_schema,isc.table_name)::regclass::oid, 'pg_class') as table_description,
pg_catalog.col_description(format('%s.%s',isc.table_schema,isc.table_name)::regclass::oid,isc.ordinal_position) as column_description
FROM
information_schema.columns isc
It fetches table and column descriptions, without any confusing joins and ugly string concatenations.
Take care with schemas, this code considers them:
SELECT
cols.column_name, (
SELECT
pg_catalog.col_description(c.oid, cols.ordinal_position::int)
FROM
pg_catalog.pg_class c
WHERE
c.oid = (SELECT ('"' || cols.table_name || '"')::regclass::oid)
AND c.relname = cols.table_name
) AS column_comment
FROM
information_schema.columns cols
WHERE
cols.table_catalog = 'your_database'
AND cols.table_name = 'your_table'
AND cols.table_schema = 'your_schema';
References:
Postgresql Document Table and Column Description Comments on Table and Column
Determining the OID of a table in Postgres 9.1?
A slight change to one of the other answers which only gives you columns that have comments on them, this gives you all columns whether they have a comment or not.
select c.table_schema, st.relname as TableName, c.column_name,
pgd.description
from pg_catalog.pg_statio_all_tables as st
inner join information_schema.columns c
on c.table_schema = st.schemaname
and c.table_name = st.relname
left join pg_catalog.pg_description pgd
on pgd.objoid=st.relid
and pgd.objsubid=c.ordinal_position
where st.relname = 'YourTableName';
This works for me using the PostBooks 3.2.2 DB:
select cols.column_name,
(select pg_catalog.obj_description(oid) from pg_catalog.pg_class c where c.relname=cols.table_name) as table_comment
,(select pg_catalog.col_description(oid,cols.ordinal_position::int) from pg_catalog.pg_class c where c.relname=cols.table_name) as column_comment
from information_schema.columns cols
where cols.table_catalog='postbooks' and cols.table_name='apapply'
Regards,
Sylnsr
If you just need to show the comments for your columns among other data, you can also use:
\d+ my_table
Enhance for #Nick and #mat suggestions: use
SELECT obj_description('schemaName.tableName'::regclass, 'pg_class');
when you have string name (not oid).
To avoid to remember 'pg_class' parameter, and to avoid ugly concatenations at the function calls, as (tname||'.'||schema)::regclass, an useful overload for obj_description:
CREATE FUNCTION obj_description(
p_rname text, p_schema text DEFAULT NULL,
p_catalname text DEFAULT 'pg_class'
) RETURNS text AS $f$
SELECT obj_description((CASE
WHEN strpos($1, '.')>0 OR $2 IS NULL OR $2='' THEN $1
ELSE $2||'.'||$1
END)::regclass, $3);
$f$ LANGUAGE SQL IMMUTABLE;
-- USAGE: obj_description('mytable')
-- SELECT obj_description('s.t');
-- PS: obj_description('s.t', 'otherschema') is a syntax error,
-- but not generates exception: returns the same as ('s.t')
Now is easy to use, because the table name (rname parameter) is a varchar and can be expressed with a separated field for schema name, as in the main tables and queries.
See also "Getting list of table comments in PostgreSQL" or the new pg9.3 Guide
This answer is a little late, but it popped up on a google search I did to research this problem. We only needed Table descriptions, but the method would be the same for columns.
The column descriptions are in the pg_description table also, referenced by objoid.
Add this view:
CREATE OR REPLACE VIEW our_tables AS
SELECT c.oid, n.nspname AS schemaname, c.relname AS tablename, d.description,
pg_get_userbyid(c.relowner) AS tableowner, t.spcname AS "tablespace",
c.relhasindex AS hasindexes, c.relhasrules AS hasrules, c.reltriggers > 0 AS hastriggers
FROM pg_class c
LEFT JOIN pg_namespace n ON n.oid = c.relnamespace
LEFT JOIN pg_tablespace t ON t.oid = c.reltablespace
LEFT JOIN pg_description d ON c.oid = d.objoid
WHERE c.relkind = 'r'::"char";
ALTER TABLE our_tables OWNER TO postgres;
GRANT SELECT, UPDATE, INSERT, DELETE, REFERENCES, TRIGGER ON TABLE our_tables TO postgres;
GRANT SELECT ON TABLE our_tables TO public;
Then run:
SELECT tablename, description FROM our_tables WHERE schemaname = 'public'
The view is a modified version of the pg_tables view which adds in the description column.
You could also monkey around with the view definition to make it a single query.
I accessed table comments like this:
select c.relname table_name, pg_catalog.obj_description(c.oid) as comment from pg_catalog.pg_class c where c.relname = 'table_name';
and column comments thusly:
SELECT c.column_name, pgd.description FROM pg_catalog.pg_statio_all_tables as st inner join pg_catalog.pg_description pgd on (pgd.objoid=st.relid) inner join information_schema.columns c on (pgd.objsubid=c.ordinal_position and c.table_schema=st.schemaname and c.table_name=st.relname and c.table_name = 'table_name' and c.table_schema = 'public');
I asked a similar question about Postgresql comments last month. If you dig through that, you'll come across some Perl code over on my blog that automates the process of extracting a comment.
To pull out the column names of a table, you can use something like the following:
select
a.attname as "colname"
,a.attrelid as "tableoid"
,a.attnum as "columnoid"
from
pg_catalog.pg_attribute a
inner join pg_catalog.pg_class c on a.attrelid = c.oid
where
c.relname = 'mytable' -- better to use a placeholder
and a.attnum > 0
and a.attisdropped is false
and pg_catalog.pg_table_is_visible(c.oid)
order by a.attnum
You can then use the tableoid,columnoid tuple to extract the comment of each column (see my question).
I just found this here. It will provide you with all kind of metadata on one specific table (type, default value, not null flag, length, comment, foreign key name, primary key name). It seems to work well.
SELECT pg_tables.tablename, pg_attribute.attname AS field,
format_type(pg_attribute.atttypid, NULL) AS "type",
pg_attribute.atttypmod AS len,
(SELECT col_description(pg_attribute.attrelid,
pg_attribute.attnum)) AS comment,
CASE pg_attribute.attnotnull
WHEN false THEN 1 ELSE 0
END AS "notnull",
pg_constraint.conname AS "key", pc2.conname AS ckey,
(SELECT pg_attrdef.adsrc FROM pg_attrdef
WHERE pg_attrdef.adrelid = pg_class.oid
AND pg_attrdef.adnum = pg_attribute.attnum) AS def
FROM pg_tables, pg_class
JOIN pg_attribute ON pg_class.oid = pg_attribute.attrelid
AND pg_attribute.attnum > 0
LEFT JOIN pg_constraint ON pg_constraint.contype = 'p'::"char"
AND pg_constraint.conrelid = pg_class.oid AND
(pg_attribute.attnum = ANY (pg_constraint.conkey))
LEFT JOIN pg_constraint AS pc2 ON pc2.contype = 'f'::"char"
AND pc2.conrelid = pg_class.oid
AND (pg_attribute.attnum = ANY (pc2.conkey))
WHERE pg_class.relname = pg_tables.tablename
-- AND pg_tables.tableowner = "current_user"()
AND pg_attribute.atttypid <> 0::oid
AND tablename='your_table'
ORDER BY field ASC
Source: http://golden13.blogspot.de/2012/08/how-to-get-some-information-about_7.html
Ok, so i worked it out to degree...
select col_description(table id, column number)...
ie: select col_description(36698,2);
That worked, but is there an easier way to do this maybe bringing all the comments on all the columns and using the table name instead of the oid???
To display comments from all columns of all table :
SELECT
cols.table_name,
cols.column_name, (
SELECT
pg_catalog.col_description(c.oid, cols.ordinal_position::int)
FROM
pg_catalog.pg_class c
WHERE
c.oid = (SELECT ('"' || cols.table_name || '"')::regclass::oid)
AND c.relname = cols.table_name
) AS column_comment
FROM
information_schema.columns cols
WHERE
cols.table_name IN (SELECT cols.table_name FROM information_schema.columns)
AND cols.table_catalog = 'your_database_name'
AND cols.table_schema = 'your_schema_name';
You need to execute this query outside any schema/catalog/db
This query is based on another answer in this question which display comments from one table only
To extend on the response provided by #amxy; I found that adding a schema filter can help in some environments. As I found #amxy's solution didn't work until I added by schema filters
SELECT
pg_tables.schemaname,
pg_tables.TABLENAME,
pg_attribute.attname AS field,
format_type(pg_attribute.atttypid, NULL) AS "type",
pg_attribute.atttypmod AS len,
(
SELECT col_description(pg_attribute.attrelid, pg_attribute.attnum)) AS COMMENT,
CASE pg_attribute.attnotnull
WHEN FALSE THEN 1
ELSE 0
END AS "notnull",
pg_constraint.conname AS "key", pc2.conname AS ckey,
(
SELECT pg_attrdef.adsrc
FROM pg_attrdef
WHERE pg_attrdef.adrelid = pg_class.oid
AND pg_attrdef.adnum = pg_attribute.attnum) AS def
FROM pg_tables, pg_class
JOIN pg_attribute
ON pg_class.oid = pg_attribute.attrelid
AND pg_attribute.attnum > 0
LEFT JOIN pg_constraint
ON pg_constraint.contype = 'p'::"char"
AND pg_constraint.conrelid = pg_class.oid
AND
(pg_attribute.attnum = ANY (pg_constraint.conkey))
LEFT JOIN pg_constraint AS pc2
ON pc2.contype = 'f'::"char"
AND pc2.conrelid = pg_class.oid
AND (pg_attribute.attnum = ANY (pc2.conkey))
WHERE pg_class.relname = pg_tables.TABLENAME
AND pg_tables.schemaname IN ('op', 'im', 'cs','usr','li')
-- AND pg_tables.tableowner = "current_user"()
AND pg_attribute.atttypid <> 0::oid
---AND TABLENAME='your_table'
ORDER BY pg_tables.schemaname,
pg_tables.TABLENAME ASC;
RESULTS:
SELECT
relname table_name,
obj_description(oid) table_description,
column_name,
pgd.description column_description
FROM pg_class
INNER JOIN
information_schema.columns
ON table_name = pg_class.relname
LEFT JOIN
pg_catalog.pg_description pgd
ON pgd.objsubid = ordinal_position
WHERE
relname = 'your_table_name'
SELECT sc.table_schema , sc.table_name, sc.column_name, col_description(pc."oid" , sc.ordinal_position) col_description FROM pg_class pc
INNER JOIN pg_namespace ns ON ns."oid" =pc.relnamespace
INNER JOIN information_schema.COLUMNS sc ON sc.table_name=pc.relname AND sc.table_schema=ns.nspname
WHERE 1=1
AND upper(ns.nspname) = 'TABLE_SCHEMA'
AND upper(pc.relname) = 'TABLE_NAME'
Retrieving Comments from a PostgreSQL DB

How to find inherited tables programatically in PostgreSQL?

I have a PostgreSQL 8.3 database where table inheritance is being used. I would like to get a list of all tables along with its schema name which is inherited from a base table using query. Is there any way we can get this using PGSQL?
Since you're on such an old version of PostgreSQL you'll probably have to use a PL/PgSQL function to handle inheritance depths of > 1. On modern PostgreSQL (or even 8.4) you'd use a recursive common table expression (WITH RECURSIVE).
The pg_catalog.pg_inherits table is the key. Given:
create table pp( ); -- The parent we'll search for
CREATE TABLE notpp(); -- Another root for multiple inheritance
create table cc( ) inherits (pp); -- a 1st level child of pp
create table dd( ) inherits (cc,notpp); -- a 2nd level child of pp that also inherits aa
create table notshown( ) inherits (notpp); -- Table that inherits only notpp
create table ccdd () inherits (cc,dd) -- Inheritance is a graph not a tree; join node
A correct result will find cc, dd, and ccdd, but not find notpp or notshown.
A single-depth query is:
SELECT pg_namespace.nspname, pg_class.relname
FROM pg_catalog.pg_inherits
INNER JOIN pg_catalog.pg_class ON (pg_inherits.inhrelid = pg_class.oid)
INNER JOIN pg_catalog.pg_namespace ON (pg_class.relnamespace = pg_namespace.oid)
WHERE inhparent = 'pp'::regclass;
... but this will only find cc.
For multi-depth inheritance (ie tableC inherits tableB inherits tableA) you have to extend that via a recursive CTE or a loop in PL/PgSQL, using the children of the last loop as parents in the next.
Update: Here's an 8.3 compatible version that should recursively find all tables that inherit directly or indirectly from a given parent. If multiple inheritance is used, it should find any table that has the target table as one of its parents at any point along the tree.
CREATE OR REPLACE FUNCTION find_children(oid) RETURNS SETOF oid as $$
SELECT i.inhrelid FROM pg_catalog.pg_inherits i WHERE i.inhparent = $1
UNION
SELECT find_children(i.inhrelid) FROM pg_catalog.pg_inherits i WHERE i.inhparent = $1;
$$ LANGUAGE 'sql' STABLE;
CREATE OR REPLACE FUNCTION find_children_of(parentoid IN regclass, schemaname OUT name, tablename OUT name) RETURNS SETOF record AS $$
SELECT pg_namespace.nspname, pg_class.relname
FROM find_children($1) inh(inhrelid)
INNER JOIN pg_catalog.pg_class ON (inh.inhrelid = pg_class.oid)
INNER JOIN pg_catalog.pg_namespace ON (pg_class.relnamespace = pg_namespace.oid);
$$ LANGUAGE 'sql' STABLE;
Usage:
regress=# SELECT * FROM find_children_of('pp'::regclass);
schemaname | tablename
------------+-----------
public | cc
public | dd
public | ccdd
(3 rows)
Here's the recursive CTE version, which will work if you update Pg, but won't work on your current version. It's much cleaner IMO.
WITH RECURSIVE inh AS (
SELECT i.inhrelid FROM pg_catalog.pg_inherits i WHERE inhparent = 'pp'::regclass
UNION
SELECT i.inhrelid FROM inh INNER JOIN pg_catalog.pg_inherits i ON (inh.inhrelid = i.inhparent)
)
SELECT pg_namespace.nspname, pg_class.relname
FROM inh
INNER JOIN pg_catalog.pg_class ON (inh.inhrelid = pg_class.oid)
INNER JOIN pg_catalog.pg_namespace ON (pg_class.relnamespace = pg_namespace.oid);
The following statement retrieves all child tables of the table public.base_table_name:
select bt.relname as table_name, bns.nspname as table_schema
from pg_class ct
join pg_namespace cns on ct.relnamespace = cns.oid and cns.nspname = 'public'
join pg_inherits i on i.inhparent = ct.oid and ct.relname = 'base_table_name'
join pg_class bt on i.inhrelid = bt.oid
join pg_namespace bns on bt.relnamespace = bns.oid
It should work with 8.3 although I'm not 100% sure.
For those who are running a version of PostgreSQL with RECURSIVE support here's a function that finds derived tables for the specified base table.
CREATE OR REPLACE FUNCTION tables_derived_from(base_namespace name, base_table name)
RETURNS TABLE (table_schema name, table_name name, oid oid)
AS $BODY$
WITH RECURSIVE inherited_id AS
(
SELECT i.inhrelid AS oid
FROM pg_inherits i
JOIN pg_class base_t ON i.inhparent = base_t.oid
JOIN pg_namespace base_ns ON base_t.relnamespace = base_ns.oid
WHERE base_ns.nspname = base_namespace AND base_t.relname = base_table
UNION
SELECT i.inhrelid AS oid
FROM pg_inherits i
JOIN inherited_id b ON i.inhparent = b.oid
)
SELECT child_ns.nspname as table_schema, child_t.relname as table_name, child_t.oid
FROM inherited_id i
JOIN pg_class child_t ON i.oid = child_t.oid
JOIN pg_namespace child_ns ON child_t.relnamespace = child_ns.oid
ORDER BY 1, 2, 3;
$BODY$ LANGUAGE sql STABLE;
It's important to note that one table can inherit multiple tables, and none of the solutions listed really expose that; they just walk down the tree of a single parent. Consider:
CREATE TABLE a();
CREATE TABLE b();
CREATE TABLE ab_() INHERITS (a,b);
CREATE TABLE ba_() INHERITS (b,a);
CREATE TABLE ab__() INHERITS (ab_);
CREATE TABLE ba__() INHERITS (ba_);
CREATE TABLE ab_ba_() INHERITS (ab_, ba_);
CREATE TABLE ba_ab_() INHERITS (ba_, ab_);
WITH RECURSIVE inh AS (
SELECT i.inhparent::regclass, i.inhrelid::regclass, i.inhseqno FROM pg_catalog.pg_inherits i WHERE inhparent = 'a'::regclass
UNION
SELECT i.inhparent::regclass, i.inhrelid::regclass, i.inhseqno FROM inh INNER JOIN pg_catalog.pg_inherits i ON (inh.inhrelid = i.inhparent)
) SELECT * FROM inh;
inhparent | inhrelid | inhseqno
-----------+----------+----------
a | ab_ | 1
a | ba_ | 2
ab_ | ab__ | 1
ba_ | ba__ | 1
ab_ | ab_ba_ | 1
ba_ | ab_ba_ | 2
ba_ | ba_ab_ | 1
ab_ | ba_ab_ | 2
(8 rows)
Notice that b doesn't show up at all which is incorrect, as both ab_ and ba_ inherit b.
I suspect the "best" way to handle this would be a column that's text[] and contains (array[inhparent::regclass])::text for each table. That would give you something like
inhrelid path
ab_ {"{a,b}"}
ba_ {"{b,a}"}
ab_ba_ {"{a,b}","{b,a}"}
While obviously not ideal, that would at least expose the complete inheritance path and allow you to access it with enough gymnastics. Unfortunately, constructing that is not at all easy.
A somewhat simpler alternative is not to include the full inheritance path at each level, only each tables direct parents. That would give you this:
inhrelid parents
ab_ {a,b}
ba_ {b,a}
ab_ba_ {ab_,ba_}

How do you view new sequence ownership information in Postgres after using ALTER SEQUENCE?

I'm using "ALTER SEQUENCE sequence OWNED BY table.column" to change sequence association in Postgres. Is there a way to view this new information using \ds or something similar? \ds still shows the table owner as the sequence owner.
SELECT c.relname,u.usename
FROM pg_class c, pg_user u
WHERE c.relowner = u.usesysid and c.relkind = 'S'
AND relnamespace IN (
SELECT oid
FROM pg_namespace
WHERE nspname NOT LIKE 'pg_%'
AND nspname != 'information_schema'
);
OWNED BY table.column changes the associated column; OWNER TO newowner changes the role which owns the sequence. The owner role is what is displayed in \ds. I don't know a psql command to see the linked column of a sequence. OWNED BY is primary used to cascade a delete of the associated sequence when the referent column is removed.
You can get visibility into the owned by column by constructing a query against the system catalog tables pg_class, pg_depend, and pg_attribute. I'm not sure of all the exact semantics of pg_depend, but this query should show you a sequence's column dependency:
select tab.relname as tabname, attr.attname as column
from pg_class as seq
join pg_depend as dep on (seq.relfilenode = dep.objid)
join pg_class as tab on (dep.refobjid = tab.relfilenode)
join pg_attribute as attr on (attr.attnum = dep.refobjsubid and attr.attrelid = dep.refobjid)
where seq.relname = 'sequence';
Yuri Levinsky's answer modified to include the namespace column:
SELECT n.nspname, c.relname, u.usename
FROM pg_class c
INNER JOIN pg_user u ON c.relowner = u.usesysid
INNER JOIN pg_namespace n ON c.relnamespace = n.oid
WHERE c.relkind = 'S'
AND n.nspname NOT LIKE 'pg_%'
AND n.nspname != 'information_schema'
ORDER BY 1, 2, 3;