Thread: Dynamic constraint names in ALTER TABLE

Dynamic constraint names in ALTER TABLE

From
patrick keshishian
Date:
Hi,

Where I work, we have a large deployment of software using PostgreSQL
database. We have been stuck on version 7.4.16 for a while now. I am
about to switch us to a 9.0.x.

One problem I'm running into, and I am hoping you can help me with,
given the constraints I have to work with, is our conversion
"scripts".

As our DB schema changes, we have a series of convert SQL scripts that
are executed through psql to alter tables, constraints, etc. This is
done at upgrade time. This has worked pretty well over the years for
us. However, what I notice is that, while in PG 7.4.x the constraints
(foreign keys) seem to take of the form "$1", "$2", etc., PG 9.x seems
to give them a more descriptive names. This behavior change breaks our
SQL convert scripts.

Is there anyway I can change our .sql files to make the the ALTER
TABLE <ADD|DROP> CONSTRAINT statements determine the constraint name
based on system catalog?

Here a simple example demonstrating my issue:

foo=# CREATE TABLE employee (id INTEGER PRIMARY KEY, name TEXT);
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index
"employee_pkey" for table "employee"
CREATE TABLE

foo=# CREATE TABLE sales (seller INTEGER PRIMARY KEY, amount INTEGER,
FOREIGN KEY (seller) REFERENCES employee (id));
NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index
"sales_pkey" for table "sales"
CREATE TABLE

In PostgreSQL 7.4.17:
foo=# \d sales
     Table "public.sales"
 Column |  Type   | Modifiers
--------+---------+-----------
 seller | integer | not null
 amount | integer |
Indexes:
    "sales_pkey" PRIMARY KEY, btree (seller)
Foreign-key constraints:
    "$1" FOREIGN KEY (seller) REFERENCES employee(id)


In PostgreSQL 9.0.3:
foo=# \d sales
     Table "public.sales"
 Column |  Type   | Modifiers
--------+---------+-----------
 seller | integer | not null
 amount | integer |
Indexes:
    "sales_pkey" PRIMARY KEY, btree (seller)
Foreign-key constraints:
    "sales_seller_fkey" FOREIGN KEY (seller) REFERENCES employee(id)


# If the conversion wanted to change the constraint to
# add ON DELETE CASCADE (simple example).

In PostgreSQL 7.4.17 our .sql convert script would say:
foo=# ALTER TABLE sales DROP CONSTRAINT "$1" ;
ALTER TABLE
foo=# ALTER TABLE sales ADD CONSTRAINT "$1" FOREIGN KEY (seller)
REFERENCES employee (id) ON DELETE CASCADE ;
ALTER TABLE


In PostgreSQL 9.0.3 our .sql convert script would need to say:
foo=# ALTER TABLE sales DROP CONSTRAINT "sales_seller_fkey" ;
ALTER TABLE
foo=# ALTER TABLE sales ADD CONSTRAINT "sales_seller_fkey" FOREIGN KEY
(seller) REFERENCES employee (id) ON DELETE CASCADE ;
ALTER TABLE


Is there any way the .sql scripts could make use of this query to get
the foreign key name from pg_constraint table, regardless of PG
version (7.4.x or 9.x)?

foo=# SELECT conname FROM pg_constraint JOIN pg_class ON
(conrelid=pg_class.oid) WHERE pg_class.relname='sales' AND conkey[1] =
1 AND contype='f';

In PostgreSQL 7.4.17:
 conname
---------
 $1
(1 row)


In PostgreSQL 9.0.3:
      conname
-------------------
 sales_seller_fkey
(1 row)


Thanks for reading,
--patrick

Re: Dynamic constraint names in ALTER TABLE

From
Adrian Klaver
Date:
On Monday, September 19, 2011 5:10:45 pm patrick keshishian wrote:
> Hi,
>

>
>
> Is there any way the .sql scripts could make use of this query to get
> the foreign key name from pg_constraint table, regardless of PG
> version (7.4.x or 9.x)?

Use the information schema?  As example:
http://www.postgresql.org/docs/7.4/static/infoschema-table-constraints.html
http://www.postgresql.org/docs/9.0/interactive/infoschema-table-constraints.html

>
> foo=# SELECT conname FROM pg_constraint JOIN pg_class ON
> (conrelid=pg_class.oid) WHERE pg_class.relname='sales' AND conkey[1] =
> 1 AND contype='f';
>
> In PostgreSQL 7.4.17:
>  conname
> ---------
>  $1
> (1 row)
>
>
> In PostgreSQL 9.0.3:
>       conname
> -------------------
>  sales_seller_fkey
> (1 row)
>
>
> Thanks for reading,
> --patrick

--
Adrian Klaver
adrian.klaver@gmail.com

Re: Dynamic constraint names in ALTER TABLE

From
patrick keshishian
Date:
On Mon, Sep 19, 2011 at 6:08 PM, Adrian Klaver <adrian.klaver@gmail.com> wrote:
> On Monday, September 19, 2011 5:10:45 pm patrick keshishian wrote:
>> Hi,
>>
>> Is there any way the .sql scripts could make use of this query to get
>> the foreign key name from pg_constraint table, regardless of PG
>> version (7.4.x or 9.x)?
>
> Use the information schema?  As example:
> http://www.postgresql.org/docs/7.4/static/infoschema-table-constraints.html
> http://www.postgresql.org/docs/9.0/interactive/infoschema-table-constraints.html

I think you you missed the intent of my question; unless I've missed
depth of your answer.

The question wasn't where does one find the name of the constraint. My
example demonstrated that I knew how to get that value. The question,
however, is how do you get that in an ALTER TABLE statement? A
sub-select doesn't seem to work.

e.g., ALTER TABLE sales DROP CONSTRAINT (SELECT conname FROM
pg_constraint JOIN pg_class ON (conrelid=pg_class.oid) WHERE
pg_class.relname='sales' AND conkey[1] = 1 AND contype='f') ;

That does not work.

I can generate the SQL statements using SELECTs, output (\o) them to a
/tmp/really-hacky-way-to-do-this.sql files, then read (\i) them into
psql, but as the file name says, this is getting perverse.

--patrick


>> foo=# SELECT conname FROM pg_constraint JOIN pg_class ON
>> (conrelid=pg_class.oid) WHERE pg_class.relname='sales' AND conkey[1] =
>> 1 AND contype='f';
>>
>> In PostgreSQL 7.4.17:
>>  conname
>> ---------
>>  $1
>> (1 row)
>>
>>
>> In PostgreSQL 9.0.3:
>>       conname
>> -------------------
>>  sales_seller_fkey
>> (1 row)
>>
>>
>> Thanks for reading,
>> --patrick
>
> --
> Adrian Klaver
> adrian.klaver@gmail.com
>

Re: Dynamic constraint names in ALTER TABLE

From
Ondrej Ivanič
Date:
Hi,

On 20 September 2011 13:09, patrick keshishian <pkeshish@gmail.com> wrote:
> e.g., ALTER TABLE sales DROP CONSTRAINT (SELECT conname FROM
> pg_constraint JOIN pg_class ON (conrelid=pg_class.oid) WHERE
> pg_class.relname='sales' AND conkey[1] = 1 AND contype='f') ;

You have to build query in different way:

psql ... -A -t -c "SELECT 'ALTER TABLE sales DROP CONSTRAINT ' ||
conname || ';' FROM pg_constraint JOIN pg_class ON
(conrelid=pg_class.oid) WHERE pg_class.relname='sales' AND conkey[1] =
1 AND contype='f'"

Finally, you can save it in to file or pipe it to another psql:
psql ... -A -t -c '....' | psql ...

--
Ondrej Ivanic
(ondrej.ivanic@gmail.com)

Re: Dynamic constraint names in ALTER TABLE

From
Tom Lane
Date:
patrick keshishian <pkeshish@gmail.com> writes:
> The question wasn't where does one find the name of the constraint. My
> example demonstrated that I knew how to get that value. The question,
> however, is how do you get that in an ALTER TABLE statement?

You'd need to construct the ALTER statement as a string value and then
EXECUTE it, using a plpgsql function.  (In 9.0 you could use a DO
command, but if you want this to also work in 7.4, it'll have to be a
plain old function.)

            regards, tom lane

Re: Dynamic constraint names in ALTER TABLE

From
patrick keshishian
Date:
2011/9/19 Ondrej Ivanič <ondrej.ivanic@gmail.com>:
> Hi,
>
> On 20 September 2011 13:09, patrick keshishian <pkeshish@gmail.com> wrote:
>> e.g., ALTER TABLE sales DROP CONSTRAINT (SELECT conname FROM
>> pg_constraint JOIN pg_class ON (conrelid=pg_class.oid) WHERE
>> pg_class.relname='sales' AND conkey[1] = 1 AND contype='f') ;
>
> You have to build query in different way:
>
> psql ... -A -t -c "SELECT 'ALTER TABLE sales DROP CONSTRAINT ' ||
> conname || ';' FROM pg_constraint JOIN pg_class ON
> (conrelid=pg_class.oid) WHERE pg_class.relname='sales' AND conkey[1] =
> 1 AND contype='f'"
>
> Finally, you can save it in to file or pipe it to another psql:
> psql ... -A -t -c '....' | psql ...

this is no different than my save to a file (\o) and read it back (\i) way.

--patrick

Re: Dynamic constraint names in ALTER TABLE

From
patrick keshishian
Date:
On Mon, Sep 19, 2011 at 8:34 PM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
> patrick keshishian <pkeshish@gmail.com> writes:
>> The question wasn't where does one find the name of the constraint. My
>> example demonstrated that I knew how to get that value. The question,
>> however, is how do you get that in an ALTER TABLE statement?
>
> You'd need to construct the ALTER statement as a string value and then
> EXECUTE it, using a plpgsql function.  (In 9.0 you could use a DO
> command, but if you want this to also work in 7.4, it'll have to be a
> plain old function.)

OK. Thanks all.

--patrick

Re: Dynamic constraint names in ALTER TABLE

From
Adrian Klaver
Date:
On Monday, September 19, 2011 8:09:04 pm patrick keshishian wrote:
> On Mon, Sep 19, 2011 at 6:08 PM, Adrian Klaver <adrian.klaver@gmail.com>
wrote:
> > On Monday, September 19, 2011 5:10:45 pm patrick keshishian wrote:
> >> Hi,
> >>
> >> Is there any way the .sql scripts could make use of this query to get
> >> the foreign key name from pg_constraint table, regardless of PG
> >> version (7.4.x or 9.x)?
> >
> > Use the information schema?  As example:
> > http://www.postgresql.org/docs/7.4/static/infoschema-table-constraints.ht
> > ml
> > http://www.postgresql.org/docs/9.0/interactive/infoschema-table-constrai
> > nts.html
>
> I think you you missed the intent of my question; unless I've missed
> depth of your answer.

My mistake. I misread the question and I thought you where looking for a way to
get the information without using the system catalogs.

>
> The question wasn't where does one find the name of the constraint. My
> example demonstrated that I knew how to get that value. The question,
> however, is how do you get that in an ALTER TABLE statement? A
> sub-select doesn't seem to work.
>
> e.g., ALTER TABLE sales DROP CONSTRAINT (SELECT conname FROM
> pg_constraint JOIN pg_class ON (conrelid=pg_class.oid) WHERE
> pg_class.relname='sales' AND conkey[1] = 1 AND contype='f') ;
>
> That does not work.
>
> I can generate the SQL statements using SELECTs, output (\o) them to a
> /tmp/really-hacky-way-to-do-this.sql files, then read (\i) them into
> psql, but as the file name says, this is getting perverse.
>


Just out of curiosity, what do you do if there is more than one constraint on a
table and you want to apply different changes?


--
Adrian Klaver
adrian.klaver@gmail.com

Re: Dynamic constraint names in ALTER TABLE

From
patrick keshishian
Date:
On Tue, Sep 20, 2011 at 7:36 AM, Adrian Klaver <adrian.klaver@gmail.com> wrote:
> On Monday, September 19, 2011 8:09:04 pm patrick keshishian wrote:
>> On Mon, Sep 19, 2011 at 6:08 PM, Adrian Klaver <adrian.klaver@gmail.com>
> wrote:
>> > On Monday, September 19, 2011 5:10:45 pm patrick keshishian wrote:
>> >> Hi,
>> >>
>> >> Is there any way the .sql scripts could make use of this query to get
>> >> the foreign key name from pg_constraint table, regardless of PG
>> >> version (7.4.x or 9.x)?
>> >
>> > Use the information schema?  As example:
>> > http://www.postgresql.org/docs/7.4/static/infoschema-table-constraints.ht
>> > ml
>> > http://www.postgresql.org/docs/9.0/interactive/infoschema-table-constrai
>> > nts.html
>>
>> I think you you missed the intent of my question; unless I've missed
>> depth of your answer.
>
> My mistake. I misread the question and I thought you where looking for a way to
> get the information without using the system catalogs.
>
>>
>> The question wasn't where does one find the name of the constraint. My
>> example demonstrated that I knew how to get that value. The question,
>> however, is how do you get that in an ALTER TABLE statement? A
>> sub-select doesn't seem to work.
>>
>> e.g., ALTER TABLE sales DROP CONSTRAINT (SELECT conname FROM
>> pg_constraint JOIN pg_class ON (conrelid=pg_class.oid) WHERE
>> pg_class.relname='sales' AND conkey[1] = 1 AND contype='f') ;
>>
>> That does not work.
>>
>> I can generate the SQL statements using SELECTs, output (\o) them to a
>> /tmp/really-hacky-way-to-do-this.sql files, then read (\i) them into
>> psql, but as the file name says, this is getting perverse.
>>
>
>
> Just out of curiosity, what do you do if there is more than one constraint on a
> table and you want to apply different changes?

You mean in a situation like this:

foo=# CREATE TABLE employee (id INTEGER PRIMARY KEY, name TEXT);
foo=# CREATE TABLE product (id INTEGER PRIMARY KEY, description TEXT) ;
foo=# CREATE TABLE sales (seller INTEGER, amount INTEGER, item
INTEGER, FOREIGN KEY (seller) REFERENCES employee (id), FOREIGN KEY
(item) REFERENCES product (id));
foo=# \d sales
     Table "public.sales"
 Column |  Type   | Modifiers
--------+---------+-----------
 seller | integer |
 amount | integer |
 item   | integer |
Foreign-key constraints:
    "sales_item_fkey" FOREIGN KEY (item) REFERENCES product(id)
    "sales_seller_fkey" FOREIGN KEY (seller) REFERENCES employee(id)


The conkey field in the pg_constraint table helps you choose the
specific constraint you are referring to.

i.e., when I am looking for the FOREIGN KEY referenced by seller
(column 1) conkey[1] = 1:

foo=# SELECT conname FROM pg_constraint JOIN pg_class ON
(conrelid=pg_class.oid) WHERE pg_class.relname='sales' AND conkey[1] =
1 AND contype='f';
      conname
-------------------
 sales_seller_fkey
(1 row)


FOREIGN KEY referenced by item (column 3) conkey[1] = 3:

foo=# SELECT conname FROM pg_constraint JOIN pg_class ON
(conrelid=pg_class.oid) WHERE pg_class.relname='sales' AND conkey[1] =
3 AND contype='f';
     conname
-----------------
 sales_item_fkey
(1 row)


Best,
--patrick