65

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?

  • PostgreSQL is not very friendly, but it is because each user have your personal (not-standard) "util library". Below a function (`rel_description`) of my library, that can help you. – Peter Krauss Oct 08 '12 at 10:33

17 Answers17

68
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
);
Paul Vernon
  • 3,818
  • 1
  • 10
  • 23
user609896
  • 689
  • 5
  • 2
  • 3
    i don't quite understand how this code works, but is does what i need, so i don't have to. – ssc Nov 22 '11 at 09:29
  • i ran into a case there are definitely tables and the result set was of 0 size. something isn't quite right – spy Oct 27 '19 at 14:42
  • Works great, but what a complicated query. – DevonDahon Jun 29 '20 at 07:56
  • 2
    `pg_statio_all_tables` is heavy view, so do not use it for retrieve columns descriptons. Great solution is answer by "T.Z.". – Rinat Oct 14 '21 at 10:10
  • filter by table name and order by row num: where table_name='parametros_gestora_academica' order by c.ordinal_position ; – Bruno Freitas Oct 28 '21 at 19:39
20

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.

T.Z.
  • 2,092
  • 1
  • 24
  • 39
20

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.

mat
  • 12,943
  • 5
  • 39
  • 44
  • "obj_description(object_oid)" is now deprecated, see https://www.postgresql.org/docs/current/static/functions-info.html – alfonx Jul 20 '17 at 16:38
18

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:

Marcio Mazzucato
  • 8,841
  • 9
  • 64
  • 79
  • 1
    The following line allows for more flexibility on table names: ```c.oid = (SELECT ('"' || cols.table_name || '"')::regclass::oid) AND``` – jcristovao Feb 11 '15 at 14:39
  • @jcristovao, Can you explain it better, please? – Marcio Mazzucato Feb 11 '15 at 18:21
  • I use that line so that I can specify the table_name just once in the ```WHERE``` clause. However, the simple solution of ```cols.table_name``` failed with table names like ```WeirdCaps``` – jcristovao Feb 12 '15 at 15:58
  • @MarcioMazzucato: see https://www.postgresql.org/docs/current/datatype-oid.html, the `regclass` type takes a string as input and finds the corresponding object OID for it. – Martijn Pieters Apr 23 '20 at 16:02
7

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';
DatabaseShouter
  • 814
  • 9
  • 11
6

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

  • It works, but a small note from the Postgres docs: The one-parameter form of obj_description requires only the object OID. It is now deprecated since there is no guarantee that OIDs are unique across different system catalogs; therefore, the wrong comment could be returned. – Ondřej Bouda Jan 23 '13 at 15:13
6

If you just need to show the comments for your columns among other data, you can also use:

\d+ my_table
DevonDahon
  • 7,460
  • 6
  • 69
  • 114
3

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

Community
  • 1
  • 1
Peter Krauss
  • 13,174
  • 24
  • 167
  • 304
2

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.

2

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');
James Roscoe
  • 650
  • 5
  • 10
1

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

amxy
  • 21
  • 3
1

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).

Community
  • 1
  • 1
dland
  • 4,319
  • 6
  • 36
  • 60
0

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???

0

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

Nicolas Janel
  • 3,025
  • 1
  • 28
  • 31
0

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: enter image description here

0
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'
Suraj Rao
  • 29,388
  • 11
  • 94
  • 103
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community May 06 '22 at 05:06
0

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