Get the default values of table columns in Postgres?
Solution 1:
Use the information schema:
SELECT column_name, column_default
FROM information_schema.columns
WHERE (table_schema, table_name) = ('public', 'mytable')
ORDER BY ordinal_position;
column_name │ column_default
─────────────┼────────────────────────────────────────
integer │ 2
text │ 'I am default'::character varying
moretext │ 'I am also default'::character varying
unimportant │
(4 rows)
Up to the schema naming, this should work in any SQL database system.
Solution 2:
System catalogs are the source of truth in Postgres:
SELECT pg_get_expr(d.adbin, d.adrelid) AS default_value
FROM pg_catalog.pg_attribute a
LEFT JOIN pg_catalog.pg_attrdef d ON (a.attrelid, a.attnum) = (d.adrelid, d.adnum)
WHERE NOT a.attisdropped -- no dropped (dead) columns
AND a.attnum > 0 -- no system columns
AND a.attrelid = 'myschema.mytable'::regclass
AND a.attname = 'mycolumn';
- Related answer on dba.SE discussing information schema vs. system catalogs
LEFT JOIN
guarantees a result as long as the column exists. If there is no default you get NULL
- which happens to be the default default. And almost always correct. But see:
- How to use default value of data type as column default?
To exclude columns without default, use JOIN
instead and be prepared to get no row occasionally.
The special cast ::regclass
considers the current setting for search_path
. With names that aren't schema-qualified (which you should, to be sure!), you may or may not get the expected result. See rebuttal below. More in the manual. Related:
- How does the search_path influence identifier resolution and the "current schema"
No need to include pg_class
once we have the OID of the table. Faster without.
Why the first answer wouldn't do
@Zohaib's query is almost but not quite right. There are a couple of issues. I copied it here for future reference. Do not use this:
SELECT adsrc as default_value
FROM pg_attrdef pad, pg_atttribute pat, pg_class pc
WHERE pc.relname='your_table_name'
AND pc.oid=pat.attrelid AND pat.attname='your_column_name'
AND pat.attrelid=pad.adrelid AND pat.attnum=pad.adnum
-
Copied from some blog. Good that it's mentioned, but the source should be added. People reading that blog need to be warned.
-
Typo in
pg_atttribute
- fixed easily. -
Doesn't return any rows, if there is no default specified for the requested column. Better make that a
LEFT JOIN pg_attrdef ON ..
, so you always get a resulting row if the column exists. It will be NULL, if there is no default, which is actually the correct result becauseNULL
is the default then. -
If you remove
attname
from the WHERE clause, you only get values for columns that actually have a default value. Not for others. And you need to addattname
to the SELECT list or you will not know for which column. -
The query would also return the default of a column that is already dropped, which is wrong. Read about the details in the manual.
-
Most importantly: the query can give completely wrong results, as it does not take the schema name into account. There can be any number of
table1.col1
in a postgres database: in various schemas. If more than one have a default, you get multiple values. If the column you have in mind does not have a default, but another one in another schema does, you will be fooled and never know it. -
Last not least: in recent PostgreSQL releases, the
adsrc
column has been removed frompg_attrdef
by commit fe5038236c, as it was redundant, deprecated and unused in PostgreSQL.
To sum it up: C/P from some blog without insight went dangerously wrong.
Solution 3:
I liked Erwin's Answer but was having some difficulties:
- Sometimes I would get column names "........pg.dropped.30.......", etc. even with the
NOT a.attisdropped
qualification. It was not consistent, I wound up having to loop through the results and check the column names. - The returned default value sometimes had a cast attached, e.g.
'I am default'::character varying
which did not work when they are being stuffed into web form input values. I could not think of a good way to remove the cast suffix without doing something like.replace(/::.*/, '')
which is not robust enough. I bet Erwin can figure out some magical way toEVAL()
the returned value and use thea.atttypid
column to get the right data type.
So I went back to what I was doing before:
BEGIN;
INSERT INTO mytable DEFAULT VALUES RETURNING *;
ROLLBACK;
One thing to note here is that any SERIAL
columns will get incremented. It likely does not matter so long as it is unique if it is just a table index. Otherwise it is a deal breaker.
The other thing to watch out for is any TRIGGER AFTER/BEFORE INSERT
which will fire even though the INSERT
gets rolled back (I think any changes the trigger function makes will get rolled back though.)