Question

I have a somewhat convoluted scenario where a test table I had created with a primary key won't return the primary key. pgAdmin III reports there are no constraints. I have the entire PostgreSQL query log and below have the query I used to create the test table. I then dropped the primary key on a different test table and used the query generated (it's not a query I have manually run, yet) to help me search for pgAdmin III dropping the primary key on the table in question and found nothing searching for:

ALTER TABLE public.delete_key_bigserial DROP CONSTRAINT

The string 'DROP CONSTRAINT' only appears once in the query log dating back to 2014-12-02 which is weeks before I even created the test tables. I now understand that a primary key may or may not be set to bigserial or serial and have even created a table without a primary key set id to integer and then set id to be the primary key (another can of worms for whole 'nother day).

In an earlier question I inquired about how to fetch the data_type including if it was bigserial or serial to which Erwin Brandstetter had an excellent answer. He provided two queries in particular, one to fetch the data_types for all the columns and one to fetch the data_type for the primary key. Unfortunately one of the test tables I have been testing with isn't returning any results.

SELECT a.attrelid::regclass::text, a.attname,
CASE a.atttypid
 WHEN 'int'::regtype  THEN 'serial'
 WHEN 'int8'::regtype THEN 'bigserial'
 WHEN 'int2'::regtype THEN 'smallserial'
 END AS serial_type
FROM   pg_attribute  a
JOIN   pg_constraint c ON c.conrelid  = a.attrelid AND c.conkey[1] = a.attnum 
JOIN   pg_attrdef   ad ON ad.adrelid  = a.attrelid
                       AND ad.adnum   = a.attnum
WHERE  a.attrelid = 'delete_key_bigserial'::regclass
AND    a.attnum > 0
AND    NOT a.attisdropped
AND    a.atttypid = ANY('{int,int8,int2}'::regtype[]) -- integer type
AND    c.contype = 'p'                                -- PK
AND    array_length(c.conkey, 1) = 1                  -- single column
AND    ad.adsrc = 'nextval('''
            || (pg_get_serial_sequence (a.attrelid::regclass::text, a.attname))::regclass
            || '''::regclass)';

The query works perfect on all the other tables.

I've only been working with PostgreSQL since November 2014 and MySQL since circa 2011 so the best thing I can do AFAIK is to fetch as much relevant data as I can. Here is the query used to create the delete_key_bigserial table from the query log:

CREATE TABLE public.delete_key_bigserial (id bigserial PRIMARY KEY NOT NULL)
WITH (OIDS = FALSE);

I simplified Erwin's query and used it on the table to compare the results in my query tool to different test tables that the query works perfectly fine on (on all four data_types):

SELECT * FROM pg_attribute a 
WHERE a.attrelid = 'delete_key_bigserial'::regclass
AND a.attnum > 0
AND NOT a.attisdropped
AND attname='id'
ORDER BY a.attnum;

+----------+---------+----------+---------------+--------+--------+----------+-------------+
| attrelid | attname | atttypid | attstattarget | attlen | attnum | attndims | attcacheoff |
+----------+---------+----------+---------------+--------+--------+----------+-------------+
| 46390    | id      | 20       | -1            | 8      | 20     | 0        | -1          |
+----------+---------+----------+---------------+--------+--------+----------+-------------+

+-----------+----------+------------+----------+------------+-----------+--------------+
| atttypmod | attbyval | attstorage | attalign | attnotnull | atthasdef | attisdropped |
+-----------+----------+------------+----------+------------+-----------+--------------+
| -1        | f        | p          | d        | t          | t         | f            |
+-----------+----------+------------+----------+------------+-----------+--------------+

+------------+-------------+--------------+--------+------------+---------------+
| attislocal | attinhcount | attcollation | attacl | attoptions | attfdwoptions |
+------------+-------------+--------------+--------+------------+---------------+
| t          | 0           |              |        |            |               |
+------------+-------------+--------------+--------+------------+---------------+

Erwin is deriving the type via the atttypid column when other conditions are met however the resulting column/row is identical to other tables that work. There is another catalog table I've used in my attempts to determine what the data_type of the primary key is so I decided to compare the results from that table as well via the following query:

SELECT * FROM information_schema.columns
WHERE table_schema NOT IN ('pg_catalog', 'information_schema')
AND table_name='delete_key_bigserial'
AND is_nullable='NO';

The only difference for any returned column/row returned (besides the table name in the table_name and column_default columns) was the dtd_identifier column. The table delete_key_bigserial returns the column dtd_identifier with the value 20, for a working table the query returns 1. The (bottom of the) PostgreSQL element_types documentation describes the column as:

An identifier of the data type descriptor of the element. This is currently not useful.

I am guessing this is a deprecated/older fashion that is kept for legacy purposes though it could simply refer to the description itself? I'm not sure but this is where I am and I'm not even certain I'm on the right path.

I'd rather deal with the issue and learn from the scenario then disregard it simply because it's a test table as one day I'm certain I'll have to deal with this issue when it's not a test table. I'll be happy to update my question with relevant information that may help track down what the issue is.

Was it helpful?

Solution

For a table created like this:

CREATE TABLE public.delete_key_bigserial (id bigserial PRIMARY KEY NOT NULL);

... both my queries in the previous answer (as well as pgAdmin, psql or any other decent client) would find the PK constraint. If it's not there, you removed it somehow.
Note that my first query only returns the column if it is the PK and a serial type - which is the case for the example.

Another possible cause for the confusion: Maybe you have more than one table named delete_key_bigserial in your database? Table names are only unique inside a single schema. Test with:

SELECT * FROM pg_class WHERE relname = 'delete_key_bigserial';

To make your query unambiguous, schema-qualify the table name:

WHERE  a.attrelid = 'public.delete_key_bigserial'::regclass

There are ways to make the constraint "disappear" without leaving a DROP CONSTRAINT in your logs.

  • Drop and recreate the table.
  • Drop and recreate the schema or database.
  • (Temporarily) set log_statement or other relevant settings so the statement is not logged.
  • Manipulate the system catalogs directly (as superuser) Internally, the primary key is set with contype = 'p' in the table pg_constraint.
  • Edit the log files.

  • etc.

Licensed under: CC-BY-SA with attribution
Not affiliated with dba.stackexchange
scroll top