BUG #13126: table constraint loses its comment
The following bug has been logged on the website:
Bug reference: 13126
Logged by: Kirill Simonov
Email address: xi@resolvent.net
PostgreSQL version: 9.4.1
Operating system: Ubuntu 15.04
Description:
In some circumstances, the comment on a table constraint disappears. Here
is an example:
-- Create a table with a primary key constraint.
CREATE TYPE enum1 AS ENUM ('foo', 'bar');
CREATE TYPE enum2 AS ENUM ('foo', 'bar', 'baz');
CREATE TABLE t (x enum1 NOT NULL);
ALTER TABLE t ADD CONSTRAINT t_pk PRIMARY KEY (x);
COMMENT ON CONSTRAINT t_pk ON t IS 'the primary key of table "t"';
-- Find the constraint:
--
-- oid
-- ---------
-- 3400853
-- (1 row)
SELECT c.oid
FROM pg_constraint c
WHERE c.conname = 't_pk';
-- Find the comment on the constraint:
--
-- description
-- ------------------------------
-- the primary key of table "t"
-- (1 row)
SELECT d.description
FROM
pg_description d,
pg_constraint c
WHERE
d.classoid = 'pg_constraint'::regclass AND
c.conname = 't_pk' AND
d.objoid = c.oid;
-- Change the type of the primary key column.
ALTER TABLE t ALTER COLUMN x SET DATA TYPE enum2 USING x::text::enum2;
-- The constraint now has a different OID:
--
-- oid
-- ---------
-- 3400855
-- (1 row)
SELECT c.oid
FROM pg_constraint c
WHERE c.conname = 't_pk';
-- The constraint comment is lost:
--
-- description
-- -------------
-- (0 rows)
SELECT d.description
FROM
pg_description d,
pg_constraint c
WHERE
d.classoid = 'pg_constraint'::regclass AND
c.conname = 't_pk' AND
d.objoid = c.oid;
-- Cleanup.
DROP TABLE t;
DROP TYPE enum1;
DROP TYPE enum2;
--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs
xi@resolvent.net writes:
In some circumstances, the comment on a table constraint disappears. Here
is an example:
Hm, yeah. The problem is that ATExecAlterColumnType() rebuilds all the
affected indexes from scratch, and it isn't doing anything about copying
their comments to the new objects (either comments on the constraints, or
comments directly on the indexes).
The least painful way to fix it might be to charter ATPostAlterTypeCleanup
to create COMMENT commands and add those to the appropriate work queue,
rather than complicating the data structure initially emitted by
ATExecAlterColumnType. But it'd still be a fair amount of new code I'm
afraid.
Not planning to fix this personally, but maybe someone else would like to
take it up.
regards, tom lane
--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs
On Sun, Apr 26, 2015 at 6:05 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
xi@resolvent.net writes:
In some circumstances, the comment on a table constraint disappears. Here
is an example:Hm, yeah. The problem is that ATExecAlterColumnType() rebuilds all the
affected indexes from scratch, and it isn't doing anything about copying
their comments to the new objects (either comments on the constraints, or
comments directly on the indexes).The least painful way to fix it might be to charter ATPostAlterTypeCleanup
to create COMMENT commands and add those to the appropriate work queue,
rather than complicating the data structure initially emitted by
ATExecAlterColumnType. But it'd still be a fair amount of new code I'm
afraid.Not planning to fix this personally, but maybe someone else would like to
take it up.
In order to fix this, an idea would be to add a new routine in
ruleutils.c that generates the COMMENT query string, and then call it
directly from tablecmds.c. On master, I imagine that we could even add
some SQL interface if there is some need.
Thoughts?
--
Michael
--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs
On Wed, Apr 29, 2015 at 1:30 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:
On Sun, Apr 26, 2015 at 6:05 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
xi@resolvent.net writes:
In some circumstances, the comment on a table constraint disappears. Here
is an example:Hm, yeah. The problem is that ATExecAlterColumnType() rebuilds all the
affected indexes from scratch, and it isn't doing anything about copying
their comments to the new objects (either comments on the constraints, or
comments directly on the indexes).The least painful way to fix it might be to charter ATPostAlterTypeCleanup
to create COMMENT commands and add those to the appropriate work queue,
rather than complicating the data structure initially emitted by
ATExecAlterColumnType. But it'd still be a fair amount of new code I'm
afraid.Not planning to fix this personally, but maybe someone else would like to
take it up.In order to fix this, an idea would be to add a new routine in
ruleutils.c that generates the COMMENT query string, and then call it
directly from tablecmds.c. On master, I imagine that we could even add
some SQL interface if there is some need.
Thoughts?
After looking at this problem, I noticed that the test case given
above does not cover everything: primary key indexes, constraints
(CHECK for example) and indexes alone have also their comments removed
after ALTER TABLE when those objects are re-created. I finished with
the patch attached to fix everything, patch that includes a set of
regression tests covering all the code paths added.
--
Michael
Attachments:
0001-Ensure-COMMENT-persistency-of-indexes-and-constraint.patchtext/x-diff; charset=US-ASCII; name=0001-Ensure-COMMENT-persistency-of-indexes-and-constraint.patchDownload
From 5953d4008d23d6e75ec8b55545b0a707e06b349e Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Wed, 27 May 2015 22:01:24 +0900
Subject: [PATCH] Ensure COMMENT persistency of indexes and constraint with
ALTER TABLE
When rewriting a table, in some cases indexes and constraints present
on it need to be recreated from scratch, making any existing comment
entry, as known as a description in pg_description, disappear after
ALTER TABLE.
This commit fixes this issue by tracking the existing constraint,
indexes, and combinations of both when running ALTER TABLE and recreate
COMMENT entries when appropriate. A set of regression tests is added
to test all the new code paths added.
---
src/backend/commands/tablecmds.c | 149 ++++++++++++++++++++++++++----
src/include/nodes/parsenodes.h | 1 +
src/test/regress/expected/alter_table.out | 66 +++++++++++++
src/test/regress/sql/alter_table.sql | 27 ++++++
4 files changed, 223 insertions(+), 20 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 84dbee0..0dd909d 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -386,7 +386,13 @@ static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
char *cmd, List **wqueue, LOCKMODE lockmode,
bool rewrite);
-static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
+static void RebuildObjectComment(AlteredTableInfo *tab,
+ int cmdidx,
+ ObjectType objtype,
+ Oid objid,
+ Oid classoid,
+ List *objname);
+static bool TryReuseIndex(Oid oldId, IndexStmt *stmt);
static void TryReuseForeignKey(Oid oldId, Constraint *con);
static void change_owner_fix_column_acls(Oid relationOid,
Oid oldOwnerId, Oid newOwnerId);
@@ -3498,6 +3504,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
lockmode);
break;
+ case AT_ReAddComment: /* Re-add existing comment */
+ CommentObject((CommentStmt *) cmd->def);
+ break;
case AT_AddConstraint: /* ADD CONSTRAINT */
address =
ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
@@ -8636,9 +8645,10 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
{
IndexStmt *stmt = (IndexStmt *) stm;
AlterTableCmd *newcmd;
+ bool reused = false;
if (!rewrite)
- TryReuseIndex(oldId, stmt);
+ reused = TryReuseIndex(oldId, stmt);
tab = ATGetQueueEntry(wqueue, rel);
newcmd = makeNode(AlterTableCmd);
@@ -8646,6 +8656,14 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
newcmd->def = (Node *) stmt;
tab->subcmds[AT_PASS_OLD_INDEX] =
lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
+
+ /* create COMMENT entry for new index */
+ if (!reused)
+ RebuildObjectComment(tab,
+ AT_PASS_OLD_INDEX,
+ OBJECT_INDEX,
+ oldId, RelationRelationId,
+ list_make1(makeString(stmt->idxname)));
break;
}
case T_AlterTableStmt:
@@ -8662,25 +8680,72 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
switch (cmd->subtype)
{
case AT_AddIndex:
- Assert(IsA(cmd->def, IndexStmt));
- if (!rewrite)
- TryReuseIndex(get_constraint_index(oldId),
- (IndexStmt *) cmd->def);
- cmd->subtype = AT_ReAddIndex;
- tab->subcmds[AT_PASS_OLD_INDEX] =
- lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
+ {
+ bool reused = false;
+ IndexStmt *stmt = (IndexStmt *) cmd->def;
+ Oid indoid = get_constraint_index(oldId);
+
+ Assert(IsA(cmd->def, IndexStmt));
+ if (!rewrite)
+ reused = TryReuseIndex(indoid,
+ stmt);
+ cmd->subtype = AT_ReAddIndex;
+ tab->subcmds[AT_PASS_OLD_INDEX] =
+ lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
+
+ /* create identical COMMENT entry for new index */
+ if (!reused)
+ {
+ List *objnames = list_make2(makeString(get_rel_name(tab->relid)),
+ makeString(stmt->idxname));
+
+ /* Check the constraint comment... */
+ RebuildObjectComment(tab,
+ AT_PASS_OLD_INDEX,
+ OBJECT_TABCONSTRAINT,
+ oldId,
+ ConstraintRelationId,
+ objnames);
+
+ /* ... And the comment on the index itself */
+ RebuildObjectComment(tab,
+ AT_PASS_OLD_INDEX,
+ OBJECT_INDEX,
+ indoid,
+ RelationRelationId,
+ list_make1(makeString(get_rel_name(indoid))));
+ }
+ }
break;
case AT_AddConstraint:
- Assert(IsA(cmd->def, Constraint));
- con = (Constraint *) cmd->def;
- con->old_pktable_oid = refRelId;
- /* rewriting neither side of a FK */
- if (con->contype == CONSTR_FOREIGN &&
- !rewrite && tab->rewrite == 0)
- TryReuseForeignKey(oldId, con);
- cmd->subtype = AT_ReAddConstraint;
- tab->subcmds[AT_PASS_OLD_CONSTR] =
- lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
+ {
+ bool reuse = false;
+ Assert(IsA(cmd->def, Constraint));
+ con = (Constraint *) cmd->def;
+ con->old_pktable_oid = refRelId;
+ /* rewriting neither side of a FK */
+ if (con->contype == CONSTR_FOREIGN &&
+ !rewrite && tab->rewrite == 0)
+ {
+ TryReuseForeignKey(oldId, con);
+ reuse = true;
+ }
+ cmd->subtype = AT_ReAddConstraint;
+ tab->subcmds[AT_PASS_OLD_CONSTR] =
+ lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
+
+ /* create COMMENT entry for new constraint */
+ if (!reuse)
+ {
+ List *objnames = list_make2(makeString(get_rel_name(tab->relid)),
+ makeString(con->conname));
+ RebuildObjectComment(tab,
+ AT_PASS_OLD_CONSTR,
+ OBJECT_TABCONSTRAINT,
+ oldId, ConstraintRelationId,
+ objnames);
+ }
+ }
break;
default:
elog(ERROR, "unexpected statement type: %d",
@@ -8699,12 +8764,54 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
/*
+ * Subroutine for ATPostAlterTypeParse() to recreate a comment entry for
+ * an index or a constraint that is being re-added.
+ */
+static void
+RebuildObjectComment(AlteredTableInfo *tab,
+ int cmdidx,
+ ObjectType objtype,
+ Oid objid,
+ Oid classoid,
+ List *objname)
+{
+ CommentStmt *cmd;
+ char *comment_str;
+ AlterTableCmd *newcmd;
+
+ Assert(objtype == OBJECT_INDEX ||
+ objtype == OBJECT_TABCONSTRAINT);
+
+ /* Look for comment for object wanted, and leave if none */
+ comment_str = GetComment(objid, classoid, 0);
+ if (comment_str == NULL)
+ return;
+
+ /* Build node CommentStmt */
+ cmd = makeNode(CommentStmt);
+ cmd->objtype = objtype;
+ cmd->objname = objname;
+ cmd->objargs = NIL;
+ cmd->comment = comment_str;
+
+ /* Append it to list of commands */
+ newcmd = makeNode(AlterTableCmd);
+ newcmd->subtype = AT_ReAddComment;
+ newcmd->def = (Node *) cmd;
+ tab->subcmds[cmdidx] = lappend(tab->subcmds[cmdidx], newcmd);
+}
+
+/*
* Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
* for the real analysis, then mutates the IndexStmt based on that verdict.
+ *
+ * Returns true if the index is reused, and false otherwise.
*/
-static void
+static bool
TryReuseIndex(Oid oldId, IndexStmt *stmt)
{
+ bool res = false;
+
if (CheckIndexCompatible(oldId,
stmt->accessMethod,
stmt->indexParams,
@@ -8714,7 +8821,9 @@ TryReuseIndex(Oid oldId, IndexStmt *stmt)
stmt->oldNode = irel->rd_node.relNode;
index_close(irel, NoLock);
+ res = true;
}
+ return res;
}
/*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 868905b..0ec4c96 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1471,6 +1471,7 @@ typedef enum AlterTableType
AT_DropColumnRecurse, /* internal to commands/tablecmds.c */
AT_AddIndex, /* add index */
AT_ReAddIndex, /* internal to commands/tablecmds.c */
+ AT_ReAddComment, /* internal to commands/tablecmds.c */
AT_AddConstraint, /* add constraint */
AT_AddConstraintRecurse, /* internal to commands/tablecmds.c */
AT_ReAddConstraint, /* internal to commands/tablecmds.c */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 65274bc..8fb3fe5 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2397,6 +2397,72 @@ Check constraints:
DROP TABLE alter2.tt8;
DROP SCHEMA alter2;
+-- Check comment persistency across ALTER TABLE commands for constraints
+-- and indexes
+CREATE TABLE comment_table_1 (id1 int, id2 int);
+-- Simple index
+CREATE INDEX comment_table_1_index ON comment_table_1(id2);
+COMMENT ON INDEX comment_table_1_index IS 'Simple index on comment_table_1';
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+ description
+---------------------------------
+ Simple index on comment_table_1
+(1 row)
+
+ALTER TABLE comment_table_1 ALTER COLUMN id2 SET DATA TYPE text USING id2::int::text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+ description
+---------------------------------
+ Simple index on comment_table_1
+(1 row)
+
+-- Index with constraint
+ALTER TABLE comment_table_1 ADD CONSTRAINT comment_table_1_pk PRIMARY KEY (id1);
+COMMENT ON INDEX comment_table_1_pk IS 'Index of PRIMARY KEY of comment_table_1';
+COMMENT ON CONSTRAINT comment_table_1_pk ON comment_table_1 IS 'PRIMARY KEY constraint of comment_table_1';
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+ description
+-----------------------------------------
+ Index of PRIMARY KEY of comment_table_1
+(1 row)
+
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+ description
+-------------------------------------------
+ PRIMARY KEY constraint of comment_table_1
+(1 row)
+
+ALTER TABLE comment_table_1 ALTER COLUMN id1 SET DATA TYPE text USING id1::int::text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+ description
+-----------------------------------------
+ Index of PRIMARY KEY of comment_table_1
+(1 row)
+
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+ description
+-------------------------------------------
+ PRIMARY KEY constraint of comment_table_1
+(1 row)
+
+DROP TABLE comment_table_1;
+-- independent constraint
+CREATE TABLE comment_table_2 (id1 int CONSTRAINT positive_id1 CHECK (id1 > 0));
+COMMENT ON CONSTRAINT positive_id1 ON comment_table_2 IS 'CHECK constraint of comment_table_2';
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+ description
+-------------------------------------
+ CHECK constraint of comment_table_2
+(1 row)
+
+ALTER TABLE comment_table_2 ALTER COLUMN id1 SET DATA TYPE bigint USING id1::int::bigint;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+ description
+-------------------------------------
+ CHECK constraint of comment_table_2
+(1 row)
+
+DROP TABLE comment_table_2;
-- Check that we map relation oids to filenodes and back correctly. Only
-- display bad mappings so the test output doesn't change all the time. A
-- filenode function call can return NULL for a relation dropped concurrently
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index b5ee7b0..448eebe 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1593,6 +1593,33 @@ ALTER TABLE IF EXISTS tt8 SET SCHEMA alter2;
DROP TABLE alter2.tt8;
DROP SCHEMA alter2;
+-- Check comment persistency across ALTER TABLE commands for constraints
+-- and indexes
+CREATE TABLE comment_table_1 (id1 int, id2 int);
+-- Simple index
+CREATE INDEX comment_table_1_index ON comment_table_1(id2);
+COMMENT ON INDEX comment_table_1_index IS 'Simple index on comment_table_1';
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+ALTER TABLE comment_table_1 ALTER COLUMN id2 SET DATA TYPE text USING id2::int::text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+-- Index with constraint
+ALTER TABLE comment_table_1 ADD CONSTRAINT comment_table_1_pk PRIMARY KEY (id1);
+COMMENT ON INDEX comment_table_1_pk IS 'Index of PRIMARY KEY of comment_table_1';
+COMMENT ON CONSTRAINT comment_table_1_pk ON comment_table_1 IS 'PRIMARY KEY constraint of comment_table_1';
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+ALTER TABLE comment_table_1 ALTER COLUMN id1 SET DATA TYPE text USING id1::int::text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+DROP TABLE comment_table_1;
+-- independent constraint
+CREATE TABLE comment_table_2 (id1 int CONSTRAINT positive_id1 CHECK (id1 > 0));
+COMMENT ON CONSTRAINT positive_id1 ON comment_table_2 IS 'CHECK constraint of comment_table_2';
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+ALTER TABLE comment_table_2 ALTER COLUMN id1 SET DATA TYPE bigint USING id1::int::bigint;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+DROP TABLE comment_table_2;
+
-- Check that we map relation oids to filenodes and back correctly. Only
-- display bad mappings so the test output doesn't change all the time. A
-- filenode function call can return NULL for a relation dropped concurrently
--
2.4.1
On 05/27/2015 04:10 PM, Michael Paquier wrote:
On Wed, Apr 29, 2015 at 1:30 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:On Sun, Apr 26, 2015 at 6:05 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
xi@resolvent.net writes:
In some circumstances, the comment on a table constraint disappears. Here
is an example:Hm, yeah. The problem is that ATExecAlterColumnType() rebuilds all the
affected indexes from scratch, and it isn't doing anything about copying
their comments to the new objects (either comments on the constraints, or
comments directly on the indexes).The least painful way to fix it might be to charter ATPostAlterTypeCleanup
to create COMMENT commands and add those to the appropriate work queue,
rather than complicating the data structure initially emitted by
ATExecAlterColumnType. But it'd still be a fair amount of new code I'm
afraid.Not planning to fix this personally, but maybe someone else would like to
take it up.In order to fix this, an idea would be to add a new routine in
ruleutils.c that generates the COMMENT query string, and then call it
directly from tablecmds.c. On master, I imagine that we could even add
some SQL interface if there is some need.
Thoughts?After looking at this problem, I noticed that the test case given
above does not cover everything: primary key indexes, constraints
(CHECK for example) and indexes alone have also their comments removed
after ALTER TABLE when those objects are re-created. I finished with
the patch attached to fix everything, patch that includes a set of
regression tests covering all the code paths added.
The problem isn't limited to the cases where the old index is not
reused. This still fails with your patch:
postgres=# create table t(id text primary key);CREATE TABLE
postgres=# comment on constraint t_pkey on t is 'the primary key of
table "t"';COMMENT
postgres=# select d.description from pg_description d, pg_constraint c
where d.classoid = 'pg_constraint'::regclass and c.conname = 't_pkey'
and d.objoid = c.oid;
description
------------------------------
the primary key of table "t"
(1 row)
-- perform an ALTER TABLE that doesn't really do anything
postgres=# ALTER TABLE t ALTER COLUMN id SET DATA TYPE text;
ALTER TABLE
-- The comment is gone:
postgres=# select d.description from pg_description d, pg_constraint c
where d.classoid = 'pg_constraint'::regclass and c.conname = 't_pkey'
and d.objoid = c.oid;
description
-------------
(0 rows)
- Heikki
--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs
On 2015-05-27 15:10, Michael Paquier wrote:
On Wed, Apr 29, 2015 at 1:30 PM, Michael Paquier
<michael.paquier@gmail.com> wrote:On Sun, Apr 26, 2015 at 6:05 AM, Tom Lane <tgl@sss.pgh.pa.us> wrote:
xi@resolvent.net writes:
In some circumstances, the comment on a table constraint disappears. Here
is an example:Hm, yeah. The problem is that ATExecAlterColumnType() rebuilds all the
affected indexes from scratch, and it isn't doing anything about copying
their comments to the new objects (either comments on the constraints, or
comments directly on the indexes).The least painful way to fix it might be to charter ATPostAlterTypeCleanup
to create COMMENT commands and add those to the appropriate work queue,
rather than complicating the data structure initially emitted by
ATExecAlterColumnType. But it'd still be a fair amount of new code I'm
afraid.Not planning to fix this personally, but maybe someone else would like to
take it up.In order to fix this, an idea would be to add a new routine in
ruleutils.c that generates the COMMENT query string, and then call it
directly from tablecmds.c. On master, I imagine that we could even add
some SQL interface if there is some need.
Thoughts?After looking at this problem, I noticed that the test case given
above does not cover everything: primary key indexes, constraints
(CHECK for example) and indexes alone have also their comments removed
after ALTER TABLE when those objects are re-created. I finished with
the patch attached to fix everything, patch that includes a set of
regression tests covering all the code paths added.
I was going through the code and have few comments:
- Why do you change the return value of TryReuseIndex? Can't we use
reuse the same OidIsValid(stmt->oldNode) check that ATExecAddIndex is
doing instead?
- I think the changes to ATPostAlterTypeParse should follow more closely
the coding of transformTableLikeClause - namely use the idxcomment
- Also the changes to ATPostAlterTypeParse move the code somewhat
uncomfortably over the 80 char width, I don't really see a way to fix
that except for moving some of the nesting out to another function.
--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs
On Thu, Jul 2, 2015 at 9:28 PM, Heikki Linnakangas <hlinnaka@iki.fi> wrote:
The problem isn't limited to the cases where the old index is not reused.
This still fails with your patch:
[test case]
Interesting, so my patch is over-complicated. I will fix in next
version with more test cases to check as well reused indexes.
--
Michael
--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs
On Thu, Jul 2, 2015 at 11:16 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:
I was going through the code and have few comments:
- Why do you change the return value of TryReuseIndex? Can't we use reuse
the same OidIsValid(stmt->oldNode) check that ATExecAddIndex is doing
instead?
As pointed out by Heikki previously, that is actually unnecessary,
comments are still lost even if the index is reused for constraints.
So perhaps for simplicity we could just unconditionally recreate the
comments all the time if they are available.
- I think the changes to ATPostAlterTypeParse should follow more closely the
coding of transformTableLikeClause - namely use the idxcomment
I am not sure I follow here. Could you elaborate?
- Also the changes to ATPostAlterTypeParse move the code somewhat
uncomfortably over the 80 char width, I don't really see a way to fix that
except for moving some of the nesting out to another function.
Yes, I did some refactoring here by adding a set of new routines
dedicated to attach generated commands to the correct queue. This way
the code is empty of large switch/case blocks.
Update patch is attached, with the issue reported by Heikki upthread
fixed as well.
Regards,
--
Michael
Attachments:
0001-Ensure-COMMENT-persistency-of-indexes-and-constraint.patchapplication/x-patch; name=0001-Ensure-COMMENT-persistency-of-indexes-and-constraint.patchDownload
From 2244608d3d06b89202b506f31b00a7d58a57f9c5 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Fri, 3 Jul 2015 22:47:22 +0900
Subject: [PATCH] Ensure COMMENT persistency of indexes and constraint with
ALTER TABLE
When rewriting a table, in some cases indexes and constraints present
on it need to be recreated from scratch, making any existing comment
entry, as known as a description in pg_description, disappear after
ALTER TABLE.
This commit fixes this issue by tracking the existing constraint,
indexes, and combinations of both when running ALTER TABLE and recreate
COMMENT entries when appropriate. A set of regression tests is added
to test all the new code paths added.
---
src/backend/commands/tablecmds.c | 287 ++++++++++++++++++++++--------
src/include/nodes/parsenodes.h | 1 +
src/test/regress/expected/alter_table.out | 95 ++++++++++
src/test/regress/sql/alter_table.sql | 37 ++++
4 files changed, 346 insertions(+), 74 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d394713..78e6b5c 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -316,6 +316,13 @@ static void ATRewriteTables(AlterTableStmt *parsetree,
List **wqueue, LOCKMODE lockmode);
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
+static void ATAttachQueueCommand(Oid oldId, Oid refRelId, List **wqueue,
+ Node *stm, Relation rel, bool rewrite);
+static void ATAttachQueueIndexStmt(Oid oldId, List **wqueue,
+ IndexStmt *stmt, Relation rel, bool rewrite);
+static void ATAttachQueueAlterTableStmt(Oid oldId, Oid refRelId,
+ List **wqueue, AlterTableStmt *stmt,
+ Relation rel, bool rewrite);
static void ATSimplePermissions(Relation rel, int allowed_targets);
static void ATWrongRelkindError(Relation rel, int allowed_targets);
static void ATSimpleRecursion(List **wqueue, Relation rel,
@@ -386,6 +393,12 @@ static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
char *cmd, List **wqueue, LOCKMODE lockmode,
bool rewrite);
+static void RebuildObjectComment(AlteredTableInfo *tab,
+ int cmdidx,
+ ObjectType objtype,
+ Oid objid,
+ Oid classoid,
+ List *objname);
static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
static void TryReuseForeignKey(Oid oldId, Constraint *con);
static void change_owner_fix_column_acls(Oid relationOid,
@@ -3498,6 +3511,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
lockmode);
break;
+ case AT_ReAddComment: /* Re-add existing comment */
+ CommentObject((CommentStmt *) cmd->def);
+ break;
case AT_AddConstraint: /* ADD CONSTRAINT */
address =
ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
@@ -4251,6 +4267,162 @@ ATGetQueueEntry(List **wqueue, Relation rel)
return tab;
}
+
+/*
+ * ATAttachQueueCommand
+ *
+ * Attach each generated command to the proper place in the work queue.
+ * Note this could result in creation of entirely new work-queue entries.
+ *
+ * Also note that the command subtypes have to be tweaked, because it
+ * turns out that re-creation of indexes and constraints has to act a bit
+ * differently from initial creation.
+ */
+static void
+ATAttachQueueCommand(Oid oldId, Oid refRelId, List **wqueue,
+ Node *stm, Relation rel, bool rewrite)
+{
+ switch (nodeTag(stm))
+ {
+ case T_IndexStmt:
+ ATAttachQueueIndexStmt(oldId, wqueue,
+ (IndexStmt *) stm, rel, rewrite);
+ break;
+ case T_AlterTableStmt:
+ ATAttachQueueAlterTableStmt(oldId, refRelId, wqueue,
+ (AlterTableStmt *) stm,
+ rel, rewrite);
+ break;
+ default:
+ elog(ERROR, "unexpected statement type: %d",
+ (int) nodeTag(stm));
+ }
+}
+
+
+/*
+ * ATAttachQueueIndexStmt
+ *
+ * Attach to the correct queue the given IndexStmt, re-creating at the same
+ * time a comment for it if necessary.
+ */
+static void
+ATAttachQueueIndexStmt(Oid oldId, List **wqueue,
+ IndexStmt *stmt, Relation rel, bool rewrite)
+{
+ AlterTableCmd *newcmd;
+ AlteredTableInfo *tab;
+
+ if (!rewrite)
+ TryReuseIndex(oldId, stmt);
+
+ tab = ATGetQueueEntry(wqueue, rel);
+ newcmd = makeNode(AlterTableCmd);
+ newcmd->subtype = AT_ReAddIndex;
+ newcmd->def = (Node *) stmt;
+ tab->subcmds[AT_PASS_OLD_INDEX] =
+ lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
+
+ /* create COMMENT entry for new index */
+ RebuildObjectComment(tab,
+ AT_PASS_OLD_INDEX,
+ OBJECT_INDEX,
+ oldId, RelationRelationId,
+ list_make1(makeString(stmt->idxname)));
+}
+
+/*
+ * ATAttachQueueAlterTableStmt
+ *
+ * Attach each generated command to the correct queue for the given
+ * AlterTableStmt, re-creating any existing COMMENT object for the
+ * commands generating new objects.
+ */
+static void
+ATAttachQueueAlterTableStmt(Oid oldId, Oid refRelId, List **wqueue,
+ AlterTableStmt *stmt, Relation rel,
+ bool rewrite)
+{
+ ListCell *lcmd;
+ AlteredTableInfo *tab;
+
+ tab = ATGetQueueEntry(wqueue, rel);
+ foreach(lcmd, stmt->cmds)
+ {
+ AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
+ Constraint *con;
+
+ switch (cmd->subtype)
+ {
+ case AT_AddIndex:
+ {
+ IndexStmt *stmt = (IndexStmt *) cmd->def;
+ Oid indoid = get_constraint_index(oldId);
+ List *objnames;
+
+ Assert(IsA(cmd->def, IndexStmt));
+ if (!rewrite)
+ TryReuseIndex(indoid, stmt);
+ cmd->subtype = AT_ReAddIndex;
+ tab->subcmds[AT_PASS_OLD_INDEX] =
+ lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
+
+ /* Create identical COMMENT entry for new index */
+ objnames = list_make2(
+ makeString(get_rel_name(tab->relid)),
+ makeString(stmt->idxname));
+
+ /* Check the constraint comment... */
+ RebuildObjectComment(tab,
+ AT_PASS_OLD_INDEX,
+ OBJECT_TABCONSTRAINT,
+ oldId,
+ ConstraintRelationId,
+ objnames);
+
+ /* ... And the comment on the index itself */
+ RebuildObjectComment(tab,
+ AT_PASS_OLD_INDEX,
+ OBJECT_INDEX,
+ indoid,
+ RelationRelationId,
+ list_make1(makeString(get_rel_name(indoid))));
+ }
+ break;
+ case AT_AddConstraint:
+ {
+ List *objnames;
+
+ Assert(IsA(cmd->def, Constraint));
+ con = (Constraint *) cmd->def;
+ con->old_pktable_oid = refRelId;
+ /* rewriting neither side of a FK */
+ if (con->contype == CONSTR_FOREIGN &&
+ !rewrite && tab->rewrite == 0)
+ TryReuseForeignKey(oldId, con);
+ cmd->subtype = AT_ReAddConstraint;
+ tab->subcmds[AT_PASS_OLD_CONSTR] =
+ lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
+
+ /* create COMMENT entry for new constraint */
+ objnames = list_make2(
+ makeString(get_rel_name(tab->relid)),
+ makeString(con->conname));
+ RebuildObjectComment(tab,
+ AT_PASS_OLD_CONSTR,
+ OBJECT_TABCONSTRAINT,
+ oldId,
+ ConstraintRelationId,
+ objnames);
+ }
+ break;
+ default:
+ elog(ERROR, "unexpected statement type: %d",
+ (int) cmd->subtype);
+ }
+ }
+}
+
/*
* ATSimplePermissions
*
@@ -8633,84 +8805,51 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
rel = relation_open(oldRelId, NoLock);
/*
- * Attach each generated command to the proper place in the work queue.
- * Note this could result in creation of entirely new work-queue entries.
- *
- * Also note that we have to tweak the command subtypes, because it turns
- * out that re-creation of indexes and constraints has to act a bit
- * differently from initial creation.
+ * Attach each generated command to the correct queue.
*/
foreach(list_item, querytree_list)
- {
- Node *stm = (Node *) lfirst(list_item);
- AlteredTableInfo *tab;
-
- switch (nodeTag(stm))
- {
- case T_IndexStmt:
- {
- IndexStmt *stmt = (IndexStmt *) stm;
- AlterTableCmd *newcmd;
-
- if (!rewrite)
- TryReuseIndex(oldId, stmt);
-
- tab = ATGetQueueEntry(wqueue, rel);
- newcmd = makeNode(AlterTableCmd);
- newcmd->subtype = AT_ReAddIndex;
- newcmd->def = (Node *) stmt;
- tab->subcmds[AT_PASS_OLD_INDEX] =
- lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
- break;
- }
- case T_AlterTableStmt:
- {
- AlterTableStmt *stmt = (AlterTableStmt *) stm;
- ListCell *lcmd;
-
- tab = ATGetQueueEntry(wqueue, rel);
- foreach(lcmd, stmt->cmds)
- {
- AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
- Constraint *con;
+ ATAttachQueueCommand(oldId, refRelId, wqueue,
+ (Node *) lfirst(list_item),
+ rel, rewrite);
+ relation_close(rel, NoLock);
+}
- switch (cmd->subtype)
- {
- case AT_AddIndex:
- Assert(IsA(cmd->def, IndexStmt));
- if (!rewrite)
- TryReuseIndex(get_constraint_index(oldId),
- (IndexStmt *) cmd->def);
- cmd->subtype = AT_ReAddIndex;
- tab->subcmds[AT_PASS_OLD_INDEX] =
- lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
- break;
- case AT_AddConstraint:
- Assert(IsA(cmd->def, Constraint));
- con = (Constraint *) cmd->def;
- con->old_pktable_oid = refRelId;
- /* rewriting neither side of a FK */
- if (con->contype == CONSTR_FOREIGN &&
- !rewrite && tab->rewrite == 0)
- TryReuseForeignKey(oldId, con);
- cmd->subtype = AT_ReAddConstraint;
- tab->subcmds[AT_PASS_OLD_CONSTR] =
- lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
- break;
- default:
- elog(ERROR, "unexpected statement type: %d",
- (int) cmd->subtype);
- }
- }
- break;
- }
- default:
- elog(ERROR, "unexpected statement type: %d",
- (int) nodeTag(stm));
- }
- }
+/*
+ * Subroutine for ATPostAlterTypeParse() to recreate a comment entry for
+ * an index or a constraint that is being re-added.
+ */
+static void
+RebuildObjectComment(AlteredTableInfo *tab,
+ int cmdidx,
+ ObjectType objtype,
+ Oid objid,
+ Oid classoid,
+ List *objname)
+{
+ CommentStmt *cmd;
+ char *comment_str;
+ AlterTableCmd *newcmd;
+
+ Assert(objtype == OBJECT_INDEX ||
+ objtype == OBJECT_TABCONSTRAINT);
+
+ /* Look for comment for object wanted, and leave if none */
+ comment_str = GetComment(objid, classoid, 0);
+ if (comment_str == NULL)
+ return;
- relation_close(rel, NoLock);
+ /* Build node CommentStmt */
+ cmd = makeNode(CommentStmt);
+ cmd->objtype = objtype;
+ cmd->objname = objname;
+ cmd->objargs = NIL;
+ cmd->comment = comment_str;
+
+ /* Append it to list of commands */
+ newcmd = makeNode(AlterTableCmd);
+ newcmd->subtype = AT_ReAddComment;
+ newcmd->def = (Node *) cmd;
+ tab->subcmds[cmdidx] = lappend(tab->subcmds[cmdidx], newcmd);
}
/*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 868905b..0ec4c96 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1471,6 +1471,7 @@ typedef enum AlterTableType
AT_DropColumnRecurse, /* internal to commands/tablecmds.c */
AT_AddIndex, /* add index */
AT_ReAddIndex, /* internal to commands/tablecmds.c */
+ AT_ReAddComment, /* internal to commands/tablecmds.c */
AT_AddConstraint, /* add constraint */
AT_AddConstraintRecurse, /* internal to commands/tablecmds.c */
AT_ReAddConstraint, /* internal to commands/tablecmds.c */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 3ad2c55..f4d7207 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2400,6 +2400,101 @@ Check constraints:
DROP TABLE alter2.tt8;
DROP SCHEMA alter2;
+-- Check comment persistency across ALTER TABLE commands for constraints
+-- and indexes. ALTER TABLE is optimized to check when an index can be
+-- reused or not depending on the data type changed, hence check that as
+-- well with some dummy commands.
+CREATE TABLE comment_table_1 (id1 int, id2 int);
+-- Simple index
+CREATE INDEX comment_table_1_index ON comment_table_1(id2);
+COMMENT ON INDEX comment_table_1_index IS 'Simple index on comment_table_1';
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+ description
+---------------------------------
+ Simple index on comment_table_1
+(1 row)
+
+ALTER TABLE comment_table_1 ALTER COLUMN id2 SET DATA TYPE text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+ description
+---------------------------------
+ Simple index on comment_table_1
+(1 row)
+
+ALTER TABLE comment_table_1 ALTER COLUMN id2 SET DATA TYPE text USING id2::int::text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+ description
+---------------------------------
+ Simple index on comment_table_1
+(1 row)
+
+-- Index with constraint
+ALTER TABLE comment_table_1 ADD CONSTRAINT comment_table_1_pk PRIMARY KEY (id1);
+COMMENT ON INDEX comment_table_1_pk IS 'Index of PRIMARY KEY of comment_table_1';
+COMMENT ON CONSTRAINT comment_table_1_pk ON comment_table_1 IS 'PRIMARY KEY constraint of comment_table_1';
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+ description
+-----------------------------------------
+ Index of PRIMARY KEY of comment_table_1
+(1 row)
+
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+ description
+-------------------------------------------
+ PRIMARY KEY constraint of comment_table_1
+(1 row)
+
+ALTER TABLE comment_table_1 ALTER COLUMN id1 SET DATA TYPE int;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+ description
+-----------------------------------------
+ Index of PRIMARY KEY of comment_table_1
+(1 row)
+
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+ description
+-------------------------------------------
+ PRIMARY KEY constraint of comment_table_1
+(1 row)
+
+ALTER TABLE comment_table_1 ALTER COLUMN id1 SET DATA TYPE text USING id1::int::text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+ description
+-----------------------------------------
+ Index of PRIMARY KEY of comment_table_1
+(1 row)
+
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+ description
+-------------------------------------------
+ PRIMARY KEY constraint of comment_table_1
+(1 row)
+
+DROP TABLE comment_table_1;
+-- Independent constraint
+CREATE TABLE comment_table_2 (id1 int CONSTRAINT positive_id1 CHECK (id1 > 0));
+COMMENT ON CONSTRAINT positive_id1 ON comment_table_2 IS 'CHECK constraint of comment_table_2';
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+ description
+-------------------------------------
+ CHECK constraint of comment_table_2
+(1 row)
+
+ALTER TABLE comment_table_2 ALTER COLUMN id1 SET DATA TYPE int;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+ description
+-------------------------------------
+ CHECK constraint of comment_table_2
+(1 row)
+
+ALTER TABLE comment_table_2 ALTER COLUMN id1 SET DATA TYPE bigint USING id1::int::bigint;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+ description
+-------------------------------------
+ CHECK constraint of comment_table_2
+(1 row)
+
+DROP TABLE comment_table_2;
-- Check that we map relation oids to filenodes and back correctly. Only
-- display bad mappings so the test output doesn't change all the time. A
-- filenode function call can return NULL for a relation dropped concurrently
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 29c1875..0f5347f 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1594,6 +1594,43 @@ ALTER TABLE IF EXISTS tt8 SET SCHEMA alter2;
DROP TABLE alter2.tt8;
DROP SCHEMA alter2;
+-- Check comment persistency across ALTER TABLE commands for constraints
+-- and indexes. ALTER TABLE is optimized to check when an index can be
+-- reused or not depending on the data type changed, hence check that as
+-- well with some dummy commands.
+CREATE TABLE comment_table_1 (id1 int, id2 int);
+-- Simple index
+CREATE INDEX comment_table_1_index ON comment_table_1(id2);
+COMMENT ON INDEX comment_table_1_index IS 'Simple index on comment_table_1';
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+ALTER TABLE comment_table_1 ALTER COLUMN id2 SET DATA TYPE text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+ALTER TABLE comment_table_1 ALTER COLUMN id2 SET DATA TYPE text USING id2::int::text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+-- Index with constraint
+ALTER TABLE comment_table_1 ADD CONSTRAINT comment_table_1_pk PRIMARY KEY (id1);
+COMMENT ON INDEX comment_table_1_pk IS 'Index of PRIMARY KEY of comment_table_1';
+COMMENT ON CONSTRAINT comment_table_1_pk ON comment_table_1 IS 'PRIMARY KEY constraint of comment_table_1';
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+ALTER TABLE comment_table_1 ALTER COLUMN id1 SET DATA TYPE int;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+ALTER TABLE comment_table_1 ALTER COLUMN id1 SET DATA TYPE text USING id1::int::text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+DROP TABLE comment_table_1;
+-- Independent constraint
+CREATE TABLE comment_table_2 (id1 int CONSTRAINT positive_id1 CHECK (id1 > 0));
+COMMENT ON CONSTRAINT positive_id1 ON comment_table_2 IS 'CHECK constraint of comment_table_2';
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+ALTER TABLE comment_table_2 ALTER COLUMN id1 SET DATA TYPE int;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+ALTER TABLE comment_table_2 ALTER COLUMN id1 SET DATA TYPE bigint USING id1::int::bigint;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+DROP TABLE comment_table_2;
+
+
-- Check that we map relation oids to filenodes and back correctly. Only
-- display bad mappings so the test output doesn't change all the time. A
-- filenode function call can return NULL for a relation dropped concurrently
--
2.4.5
On 2015-07-03 15:50, Michael Paquier wrote:
On Thu, Jul 2, 2015 at 11:16 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:
I was going through the code and have few comments:
- Why do you change the return value of TryReuseIndex? Can't we use reuse
the same OidIsValid(stmt->oldNode) check that ATExecAddIndex is doing
instead?As pointed out by Heikki previously, that is actually unnecessary,
comments are still lost even if the index is reused for constraints.
So perhaps for simplicity we could just unconditionally recreate the
comments all the time if they are available.
Ah ok, I missed Heikki's email.
- I think the changes to ATPostAlterTypeParse should follow more closely the
coding of transformTableLikeClause - namely use the idxcommentI am not sure I follow here. Could you elaborate?
Well for indexes you don't really need to add the new AT command, as
IndexStmt has char *idxcomment which it will automatically uses as
comment if not NULL. While I am not huge fan of the idxcomment it
doesn't seem to be easy to remove it in the future and it's what
transformTableLikeClause uses so it might be good to be consistent with
that.
--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs
On Fri, Jul 3, 2015 at 11:59 PM, Petr Jelinek wrote:
Well for indexes you don't really need to add the new AT command, as
IndexStmt has char *idxcomment which it will automatically uses as comment
if not NULL. While I am not huge fan of the idxcomment it doesn't seem to
be easy to remove it in the future and it's what transformTableLikeClause
uses so it might be good to be consistent with that.
Oh, right, I completely missed your point and this field in IndexStmt.
Yes it is better to be consistent in this case and to use it. It makes
as well the code easier to follow.
Btw, regarding the new AT routines, I am getting find of them, it
makes easier to follow which command is added where in the command
queues.
Updated patch attached.
--
Michael
Attachments:
0001-Ensure-COMMENT-persistency-of-indexes-and-constraint.patchapplication/x-patch; name=0001-Ensure-COMMENT-persistency-of-indexes-and-constraint.patchDownload
From 6d98dfd7f191dfe99f24c3022f30af8fc6a624dd Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@otacoo.com>
Date: Sat, 4 Jul 2015 20:40:42 +0900
Subject: [PATCH] Ensure COMMENT persistency of indexes and constraint with
ALTER TABLE
When rewriting a table, in some cases indexes and constraints present
on it need to be recreated from scratch, making any existing comment
entry, as known as a description in pg_description, disappear after
ALTER TABLE.
This commit fixes this issue by tracking the existing constraint,
indexes, and combinations of both when running ALTER TABLE and recreate
COMMENT entries when appropriate. A set of regression tests is added
to test all the new code paths added.
---
src/backend/commands/tablecmds.c | 268 +++++++++++++++++++++---------
src/include/nodes/parsenodes.h | 1 +
src/test/regress/expected/alter_table.out | 95 +++++++++++
src/test/regress/sql/alter_table.sql | 37 +++++
4 files changed, 327 insertions(+), 74 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d394713..79de187 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -316,6 +316,13 @@ static void ATRewriteTables(AlterTableStmt *parsetree,
List **wqueue, LOCKMODE lockmode);
static void ATRewriteTable(AlteredTableInfo *tab, Oid OIDNewHeap, LOCKMODE lockmode);
static AlteredTableInfo *ATGetQueueEntry(List **wqueue, Relation rel);
+static void ATAttachQueueCommand(Oid oldId, Oid refRelId, List **wqueue,
+ Node *stm, Relation rel, bool rewrite);
+static void ATAttachQueueIndexStmt(Oid oldId, List **wqueue,
+ IndexStmt *stmt, Relation rel, bool rewrite);
+static void ATAttachQueueAlterTableStmt(Oid oldId, Oid refRelId,
+ List **wqueue, AlterTableStmt *stmt,
+ Relation rel, bool rewrite);
static void ATSimplePermissions(Relation rel, int allowed_targets);
static void ATWrongRelkindError(Relation rel, int allowed_targets);
static void ATSimpleRecursion(List **wqueue, Relation rel,
@@ -386,6 +393,10 @@ static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
char *cmd, List **wqueue, LOCKMODE lockmode,
bool rewrite);
+static void RebuildConstraintComment(AlteredTableInfo *tab,
+ int cmdidx,
+ Oid objid,
+ List *objname);
static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
static void TryReuseForeignKey(Oid oldId, Constraint *con);
static void change_owner_fix_column_acls(Oid relationOid,
@@ -3498,6 +3509,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
address = ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, true,
lockmode);
break;
+ case AT_ReAddComment: /* Re-add existing comment */
+ CommentObject((CommentStmt *) cmd->def);
+ break;
case AT_AddConstraint: /* ADD CONSTRAINT */
address =
ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
@@ -4251,6 +4265,150 @@ ATGetQueueEntry(List **wqueue, Relation rel)
return tab;
}
+
+/*
+ * ATAttachQueueCommand
+ *
+ * Attach each generated command to the proper place in the work queue.
+ * Note this could result in creation of entirely new work-queue entries.
+ *
+ * Also note that the command subtypes have to be tweaked, because it
+ * turns out that re-creation of indexes and constraints has to act a bit
+ * differently from initial creation.
+ */
+static void
+ATAttachQueueCommand(Oid oldId, Oid refRelId, List **wqueue,
+ Node *stm, Relation rel, bool rewrite)
+{
+ switch (nodeTag(stm))
+ {
+ case T_IndexStmt:
+ ATAttachQueueIndexStmt(oldId, wqueue,
+ (IndexStmt *) stm, rel, rewrite);
+ break;
+ case T_AlterTableStmt:
+ ATAttachQueueAlterTableStmt(oldId, refRelId, wqueue,
+ (AlterTableStmt *) stm,
+ rel, rewrite);
+ break;
+ default:
+ elog(ERROR, "unexpected statement type: %d",
+ (int) nodeTag(stm));
+ }
+}
+
+
+/*
+ * ATAttachQueueIndexStmt
+ *
+ * Attach to the correct queue the given IndexStmt, re-creating at the same
+ * time a comment for it if necessary.
+ */
+static void
+ATAttachQueueIndexStmt(Oid oldId, List **wqueue,
+ IndexStmt *stmt, Relation rel, bool rewrite)
+{
+ AlterTableCmd *newcmd;
+ AlteredTableInfo *tab;
+
+ if (!rewrite)
+ TryReuseIndex(oldId, stmt);
+
+ tab = ATGetQueueEntry(wqueue, rel);
+ newcmd = makeNode(AlterTableCmd);
+ newcmd->subtype = AT_ReAddIndex;
+ newcmd->def = (Node *) stmt;
+ tab->subcmds[AT_PASS_OLD_INDEX] =
+ lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
+
+ /* Add COMMENT entry for new index */
+ stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
+}
+
+/*
+ * ATAttachQueueAlterTableStmt
+ *
+ * Attach each generated command to the correct queue for the given
+ * AlterTableStmt, re-creating any existing COMMENT object for the
+ * commands generating new objects.
+ */
+static void
+ATAttachQueueAlterTableStmt(Oid oldId, Oid refRelId, List **wqueue,
+ AlterTableStmt *stmt, Relation rel,
+ bool rewrite)
+{
+ ListCell *lcmd;
+ AlteredTableInfo *tab;
+
+ tab = ATGetQueueEntry(wqueue, rel);
+ foreach(lcmd, stmt->cmds)
+ {
+ AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
+ Constraint *con;
+
+ switch (cmd->subtype)
+ {
+ case AT_AddIndex:
+ {
+ IndexStmt *stmt = (IndexStmt *) cmd->def;
+ Oid indoid = get_constraint_index(oldId);
+ List *objnames;
+
+ Assert(IsA(cmd->def, IndexStmt));
+ if (!rewrite)
+ TryReuseIndex(indoid, stmt);
+ cmd->subtype = AT_ReAddIndex;
+ tab->subcmds[AT_PASS_OLD_INDEX] =
+ lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
+
+ /* Create identical COMMENT entry for new index */
+ objnames = list_make2(
+ makeString(get_rel_name(tab->relid)),
+ makeString(stmt->idxname));
+
+ /* Check the COMMENT of this constraint ... */
+ RebuildConstraintComment(tab,
+ AT_PASS_OLD_INDEX,
+ oldId,
+ objnames);
+
+ /* ... And the comment on the index itself */
+ stmt->idxcomment = GetComment(indoid,
+ RelationRelationId, 0);
+ }
+ break;
+ case AT_AddConstraint:
+ {
+ List *objnames;
+
+ Assert(IsA(cmd->def, Constraint));
+ con = (Constraint *) cmd->def;
+ con->old_pktable_oid = refRelId;
+ /* rewriting neither side of a FK */
+ if (con->contype == CONSTR_FOREIGN &&
+ !rewrite && tab->rewrite == 0)
+ TryReuseForeignKey(oldId, con);
+ cmd->subtype = AT_ReAddConstraint;
+ tab->subcmds[AT_PASS_OLD_CONSTR] =
+ lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
+
+ /* create COMMENT entry for the new constraint */
+ objnames = list_make2(
+ makeString(get_rel_name(tab->relid)),
+ makeString(con->conname));
+ RebuildConstraintComment(tab,
+ AT_PASS_OLD_CONSTR,
+ oldId,
+ objnames);
+ }
+ break;
+ default:
+ elog(ERROR, "unexpected statement type: %d",
+ (int) cmd->subtype);
+ }
+ }
+}
+
/*
* ATSimplePermissions
*
@@ -8633,84 +8791,46 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
rel = relation_open(oldRelId, NoLock);
/*
- * Attach each generated command to the proper place in the work queue.
- * Note this could result in creation of entirely new work-queue entries.
- *
- * Also note that we have to tweak the command subtypes, because it turns
- * out that re-creation of indexes and constraints has to act a bit
- * differently from initial creation.
+ * Attach each generated command to the correct queue.
*/
foreach(list_item, querytree_list)
- {
- Node *stm = (Node *) lfirst(list_item);
- AlteredTableInfo *tab;
-
- switch (nodeTag(stm))
- {
- case T_IndexStmt:
- {
- IndexStmt *stmt = (IndexStmt *) stm;
- AlterTableCmd *newcmd;
-
- if (!rewrite)
- TryReuseIndex(oldId, stmt);
-
- tab = ATGetQueueEntry(wqueue, rel);
- newcmd = makeNode(AlterTableCmd);
- newcmd->subtype = AT_ReAddIndex;
- newcmd->def = (Node *) stmt;
- tab->subcmds[AT_PASS_OLD_INDEX] =
- lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
- break;
- }
- case T_AlterTableStmt:
- {
- AlterTableStmt *stmt = (AlterTableStmt *) stm;
- ListCell *lcmd;
-
- tab = ATGetQueueEntry(wqueue, rel);
- foreach(lcmd, stmt->cmds)
- {
- AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
- Constraint *con;
+ ATAttachQueueCommand(oldId, refRelId, wqueue,
+ (Node *) lfirst(list_item),
+ rel, rewrite);
+ relation_close(rel, NoLock);
+}
- switch (cmd->subtype)
- {
- case AT_AddIndex:
- Assert(IsA(cmd->def, IndexStmt));
- if (!rewrite)
- TryReuseIndex(get_constraint_index(oldId),
- (IndexStmt *) cmd->def);
- cmd->subtype = AT_ReAddIndex;
- tab->subcmds[AT_PASS_OLD_INDEX] =
- lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
- break;
- case AT_AddConstraint:
- Assert(IsA(cmd->def, Constraint));
- con = (Constraint *) cmd->def;
- con->old_pktable_oid = refRelId;
- /* rewriting neither side of a FK */
- if (con->contype == CONSTR_FOREIGN &&
- !rewrite && tab->rewrite == 0)
- TryReuseForeignKey(oldId, con);
- cmd->subtype = AT_ReAddConstraint;
- tab->subcmds[AT_PASS_OLD_CONSTR] =
- lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
- break;
- default:
- elog(ERROR, "unexpected statement type: %d",
- (int) cmd->subtype);
- }
- }
- break;
- }
- default:
- elog(ERROR, "unexpected statement type: %d",
- (int) nodeTag(stm));
- }
- }
+/*
+ * Subroutine for ATPostAlterTypeParse() to recreate a comment entry for
+ * a constraint that is being re-added.
+ */
+static void
+RebuildConstraintComment(AlteredTableInfo *tab,
+ int cmdidx,
+ Oid objid,
+ List *objname)
+{
+ CommentStmt *cmd;
+ char *comment_str;
+ AlterTableCmd *newcmd;
+
+ /* Look for comment for object wanted, and leave if none */
+ comment_str = GetComment(objid, ConstraintRelationId, 0);
+ if (comment_str == NULL)
+ return;
- relation_close(rel, NoLock);
+ /* Build node CommentStmt */
+ cmd = makeNode(CommentStmt);
+ cmd->objtype = OBJECT_TABCONSTRAINT;
+ cmd->objname = objname;
+ cmd->objargs = NIL;
+ cmd->comment = comment_str;
+
+ /* Append it to list of commands */
+ newcmd = makeNode(AlterTableCmd);
+ newcmd->subtype = AT_ReAddComment;
+ newcmd->def = (Node *) cmd;
+ tab->subcmds[cmdidx] = lappend(tab->subcmds[cmdidx], newcmd);
}
/*
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 868905b..0ec4c96 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1471,6 +1471,7 @@ typedef enum AlterTableType
AT_DropColumnRecurse, /* internal to commands/tablecmds.c */
AT_AddIndex, /* add index */
AT_ReAddIndex, /* internal to commands/tablecmds.c */
+ AT_ReAddComment, /* internal to commands/tablecmds.c */
AT_AddConstraint, /* add constraint */
AT_AddConstraintRecurse, /* internal to commands/tablecmds.c */
AT_ReAddConstraint, /* internal to commands/tablecmds.c */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 3ad2c55..f4d7207 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2400,6 +2400,101 @@ Check constraints:
DROP TABLE alter2.tt8;
DROP SCHEMA alter2;
+-- Check comment persistency across ALTER TABLE commands for constraints
+-- and indexes. ALTER TABLE is optimized to check when an index can be
+-- reused or not depending on the data type changed, hence check that as
+-- well with some dummy commands.
+CREATE TABLE comment_table_1 (id1 int, id2 int);
+-- Simple index
+CREATE INDEX comment_table_1_index ON comment_table_1(id2);
+COMMENT ON INDEX comment_table_1_index IS 'Simple index on comment_table_1';
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+ description
+---------------------------------
+ Simple index on comment_table_1
+(1 row)
+
+ALTER TABLE comment_table_1 ALTER COLUMN id2 SET DATA TYPE text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+ description
+---------------------------------
+ Simple index on comment_table_1
+(1 row)
+
+ALTER TABLE comment_table_1 ALTER COLUMN id2 SET DATA TYPE text USING id2::int::text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+ description
+---------------------------------
+ Simple index on comment_table_1
+(1 row)
+
+-- Index with constraint
+ALTER TABLE comment_table_1 ADD CONSTRAINT comment_table_1_pk PRIMARY KEY (id1);
+COMMENT ON INDEX comment_table_1_pk IS 'Index of PRIMARY KEY of comment_table_1';
+COMMENT ON CONSTRAINT comment_table_1_pk ON comment_table_1 IS 'PRIMARY KEY constraint of comment_table_1';
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+ description
+-----------------------------------------
+ Index of PRIMARY KEY of comment_table_1
+(1 row)
+
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+ description
+-------------------------------------------
+ PRIMARY KEY constraint of comment_table_1
+(1 row)
+
+ALTER TABLE comment_table_1 ALTER COLUMN id1 SET DATA TYPE int;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+ description
+-----------------------------------------
+ Index of PRIMARY KEY of comment_table_1
+(1 row)
+
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+ description
+-------------------------------------------
+ PRIMARY KEY constraint of comment_table_1
+(1 row)
+
+ALTER TABLE comment_table_1 ALTER COLUMN id1 SET DATA TYPE text USING id1::int::text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+ description
+-----------------------------------------
+ Index of PRIMARY KEY of comment_table_1
+(1 row)
+
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+ description
+-------------------------------------------
+ PRIMARY KEY constraint of comment_table_1
+(1 row)
+
+DROP TABLE comment_table_1;
+-- Independent constraint
+CREATE TABLE comment_table_2 (id1 int CONSTRAINT positive_id1 CHECK (id1 > 0));
+COMMENT ON CONSTRAINT positive_id1 ON comment_table_2 IS 'CHECK constraint of comment_table_2';
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+ description
+-------------------------------------
+ CHECK constraint of comment_table_2
+(1 row)
+
+ALTER TABLE comment_table_2 ALTER COLUMN id1 SET DATA TYPE int;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+ description
+-------------------------------------
+ CHECK constraint of comment_table_2
+(1 row)
+
+ALTER TABLE comment_table_2 ALTER COLUMN id1 SET DATA TYPE bigint USING id1::int::bigint;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+ description
+-------------------------------------
+ CHECK constraint of comment_table_2
+(1 row)
+
+DROP TABLE comment_table_2;
-- Check that we map relation oids to filenodes and back correctly. Only
-- display bad mappings so the test output doesn't change all the time. A
-- filenode function call can return NULL for a relation dropped concurrently
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 29c1875..0f5347f 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1594,6 +1594,43 @@ ALTER TABLE IF EXISTS tt8 SET SCHEMA alter2;
DROP TABLE alter2.tt8;
DROP SCHEMA alter2;
+-- Check comment persistency across ALTER TABLE commands for constraints
+-- and indexes. ALTER TABLE is optimized to check when an index can be
+-- reused or not depending on the data type changed, hence check that as
+-- well with some dummy commands.
+CREATE TABLE comment_table_1 (id1 int, id2 int);
+-- Simple index
+CREATE INDEX comment_table_1_index ON comment_table_1(id2);
+COMMENT ON INDEX comment_table_1_index IS 'Simple index on comment_table_1';
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+ALTER TABLE comment_table_1 ALTER COLUMN id2 SET DATA TYPE text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+ALTER TABLE comment_table_1 ALTER COLUMN id2 SET DATA TYPE text USING id2::int::text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_index'::regclass;
+-- Index with constraint
+ALTER TABLE comment_table_1 ADD CONSTRAINT comment_table_1_pk PRIMARY KEY (id1);
+COMMENT ON INDEX comment_table_1_pk IS 'Index of PRIMARY KEY of comment_table_1';
+COMMENT ON CONSTRAINT comment_table_1_pk ON comment_table_1 IS 'PRIMARY KEY constraint of comment_table_1';
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+ALTER TABLE comment_table_1 ALTER COLUMN id1 SET DATA TYPE int;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+ALTER TABLE comment_table_1 ALTER COLUMN id1 SET DATA TYPE text USING id1::int::text;
+SELECT description FROM pg_description WHERE objoid = 'comment_table_1_pk'::regclass;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'comment_table_1_pk';
+DROP TABLE comment_table_1;
+-- Independent constraint
+CREATE TABLE comment_table_2 (id1 int CONSTRAINT positive_id1 CHECK (id1 > 0));
+COMMENT ON CONSTRAINT positive_id1 ON comment_table_2 IS 'CHECK constraint of comment_table_2';
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+ALTER TABLE comment_table_2 ALTER COLUMN id1 SET DATA TYPE int;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+ALTER TABLE comment_table_2 ALTER COLUMN id1 SET DATA TYPE bigint USING id1::int::bigint;
+SELECT description FROM pg_description, pg_constraint WHERE objoid = oid AND conname = 'positive_id1';
+DROP TABLE comment_table_2;
+
+
-- Check that we map relation oids to filenodes and back correctly. Only
-- display bad mappings so the test output doesn't change all the time. A
-- filenode function call can return NULL for a relation dropped concurrently
--
2.4.5
On 2015-07-04 13:45, Michael Paquier wrote:
On Fri, Jul 3, 2015 at 11:59 PM, Petr Jelinek wrote:
Well for indexes you don't really need to add the new AT command, as
IndexStmt has char *idxcomment which it will automatically uses as comment
if not NULL. While I am not huge fan of the idxcomment it doesn't seem to
be easy to remove it in the future and it's what transformTableLikeClause
uses so it might be good to be consistent with that.Oh, right, I completely missed your point and this field in IndexStmt.
Yes it is better to be consistent in this case and to use it. It makes
as well the code easier to follow.
Btw, regarding the new AT routines, I am getting find of them, it
makes easier to follow which command is added where in the command
queues.Updated patch attached.
Cool, I am happy with the patch now. Marking as ready for committer.
--
Petr Jelinek http://www.2ndQuadrant.com/
PostgreSQL Development, 24x7 Support, Training & Services
--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs
On Tue, Jul 7, 2015 at 6:24 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:
On 2015-07-04 13:45, Michael Paquier wrote:
On Fri, Jul 3, 2015 at 11:59 PM, Petr Jelinek wrote:
Well for indexes you don't really need to add the new AT command, as
IndexStmt has char *idxcomment which it will automatically uses as
comment
if not NULL. While I am not huge fan of the idxcomment it doesn't seem
to
be easy to remove it in the future and it's what transformTableLikeClause
uses so it might be good to be consistent with that.Oh, right, I completely missed your point and this field in IndexStmt.
Yes it is better to be consistent in this case and to use it. It makes
as well the code easier to follow.
Btw, regarding the new AT routines, I am getting find of them, it
makes easier to follow which command is added where in the command
queues.Updated patch attached.
Cool, I am happy with the patch now. Marking as ready for committer.
Thanks for the review.
--
Michael
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers
On 07/08/2015 08:12 AM, Michael Paquier wrote:
On Tue, Jul 7, 2015 at 6:24 PM, Petr Jelinek <petr@2ndquadrant.com> wrote:
On 2015-07-04 13:45, Michael Paquier wrote:
On Fri, Jul 3, 2015 at 11:59 PM, Petr Jelinek wrote:
Well for indexes you don't really need to add the new AT command, as
IndexStmt has char *idxcomment which it will automatically uses as
comment
if not NULL. While I am not huge fan of the idxcomment it doesn't seem
to
be easy to remove it in the future and it's what transformTableLikeClause
uses so it might be good to be consistent with that.Oh, right, I completely missed your point and this field in IndexStmt.
Yes it is better to be consistent in this case and to use it. It makes
as well the code easier to follow.
Btw, regarding the new AT routines, I am getting find of them, it
makes easier to follow which command is added where in the command
queues.Updated patch attached.
Cool, I am happy with the patch now. Marking as ready for committer.
Thanks for the review.
I don't much like splitting the code across multiple helper functions,
it makes the overall logic more difficult to follow, although I agree
that the indentation has made the pretty hard to read as it is. I'm
planning to commit the attached two patches. The first one is just
reformatting changes to ATExecAlterColumnType(), turning the switch-case
statements into if-else blocks. If-else doesn't cause so much
indentation, and allows defining variables local to the "cases" more
naturally, so it alleviates the indentation problem somewhat. The second
patch is the actual bug fix.
There was one bug in this patch: the COMMENT statement that was
constructed didn't schema-qualify the relation, so if the ALTERed table
was not in search_path, the operation would fail with a "relation not
found" error (or add the comment to wrong object). Fixed that.
I plan to commit the attached patches later today or tomorrow. But how
do you feel about back-patching this? It should be possible to
backpatch, although at a quick test it seems that there have been small
changes to the affected code in many versions, so it would require some
work. Also, in back-branches we'd need to put the new AT_ReAddComment
type to the end of the list, like we've done when we added
AT_ReAddConstraint in 9.3. I'm inclined to backpatch this to 9.5 only,
even though this is clearly a bug fix, on the grounds that it's not a
very serious bug and there's always some risk in backpatching.
- Heikki
Attachments:
0001-Reformat-code-in-ATPostAlterTypeParse.patchtext/x-diff; name=0001-Reformat-code-in-ATPostAlterTypeParse.patchDownload
>From c4865eb873a9cafb7e247cc69b7030243b74f3be Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Mon, 13 Jul 2015 19:22:31 +0300
Subject: [PATCH 1/2] Reformat code in ATPostAlterTypeParse.
The code in ATPostAlterTypeParse was very deeply indented, mostly because
there were two nested switch-case statements, which add a lot of
indentation. Use if-else blocks instead, to make the code less indented
and more readable.
This is in preparation for next patch that makes some actualy changes to
the function. These cosmetic parts have been separated to make it easier
to see the real changes in the other patch.
---
src/backend/commands/tablecmds.c | 104 +++++++++++++++++++--------------------
1 file changed, 51 insertions(+), 53 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index d394713..e7b23f1 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8645,69 +8645,67 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
Node *stm = (Node *) lfirst(list_item);
AlteredTableInfo *tab;
- switch (nodeTag(stm))
+ tab = ATGetQueueEntry(wqueue, rel);
+
+ if (IsA(stm, IndexStmt))
+ {
+ IndexStmt *stmt = (IndexStmt *) stm;
+ AlterTableCmd *newcmd;
+
+ if (!rewrite)
+ TryReuseIndex(oldId, stmt);
+
+ newcmd = makeNode(AlterTableCmd);
+ newcmd->subtype = AT_ReAddIndex;
+ newcmd->def = (Node *) stmt;
+ tab->subcmds[AT_PASS_OLD_INDEX] =
+ lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
+ }
+ else if (IsA(stm, AlterTableStmt))
{
- case T_IndexStmt:
+ AlterTableStmt *stmt = (AlterTableStmt *) stm;
+ ListCell *lcmd;
+
+ foreach(lcmd, stmt->cmds)
+ {
+ AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
+
+ if (cmd->subtype == AT_AddIndex)
{
- IndexStmt *stmt = (IndexStmt *) stm;
- AlterTableCmd *newcmd;
+ Assert(IsA(cmd->def, IndexStmt));
if (!rewrite)
- TryReuseIndex(oldId, stmt);
+ TryReuseIndex(get_constraint_index(oldId),
+ (IndexStmt *) cmd->def);
- tab = ATGetQueueEntry(wqueue, rel);
- newcmd = makeNode(AlterTableCmd);
- newcmd->subtype = AT_ReAddIndex;
- newcmd->def = (Node *) stmt;
+ cmd->subtype = AT_ReAddIndex;
tab->subcmds[AT_PASS_OLD_INDEX] =
- lappend(tab->subcmds[AT_PASS_OLD_INDEX], newcmd);
- break;
+ lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
}
- case T_AlterTableStmt:
+ else if (cmd->subtype == AT_AddConstraint)
{
- AlterTableStmt *stmt = (AlterTableStmt *) stm;
- ListCell *lcmd;
-
- tab = ATGetQueueEntry(wqueue, rel);
- foreach(lcmd, stmt->cmds)
- {
- AlterTableCmd *cmd = (AlterTableCmd *) lfirst(lcmd);
- Constraint *con;
-
- switch (cmd->subtype)
- {
- case AT_AddIndex:
- Assert(IsA(cmd->def, IndexStmt));
- if (!rewrite)
- TryReuseIndex(get_constraint_index(oldId),
- (IndexStmt *) cmd->def);
- cmd->subtype = AT_ReAddIndex;
- tab->subcmds[AT_PASS_OLD_INDEX] =
- lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
- break;
- case AT_AddConstraint:
- Assert(IsA(cmd->def, Constraint));
- con = (Constraint *) cmd->def;
- con->old_pktable_oid = refRelId;
- /* rewriting neither side of a FK */
- if (con->contype == CONSTR_FOREIGN &&
- !rewrite && tab->rewrite == 0)
- TryReuseForeignKey(oldId, con);
- cmd->subtype = AT_ReAddConstraint;
- tab->subcmds[AT_PASS_OLD_CONSTR] =
- lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
- break;
- default:
- elog(ERROR, "unexpected statement type: %d",
- (int) cmd->subtype);
- }
- }
- break;
+ Constraint *con;
+
+ Assert(IsA(cmd->def, Constraint));
+
+ con = (Constraint *) cmd->def;
+ con->old_pktable_oid = refRelId;
+ /* rewriting neither side of a FK */
+ if (con->contype == CONSTR_FOREIGN &&
+ !rewrite && tab->rewrite == 0)
+ TryReuseForeignKey(oldId, con);
+ cmd->subtype = AT_ReAddConstraint;
+ tab->subcmds[AT_PASS_OLD_CONSTR] =
+ lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
}
- default:
- elog(ERROR, "unexpected statement type: %d",
- (int) nodeTag(stm));
+ else
+ elog(ERROR, "unexpected statement type: %d",
+ (int) cmd->subtype);
+ }
}
+ else
+ elog(ERROR, "unexpected statement type: %d",
+ (int) nodeTag(stm));
}
relation_close(rel, NoLock);
--
2.1.4
0002-Retain-comments-on-indexes-and-constraints-at-ALTER-.patchtext/x-diff; name=0002-Retain-comments-on-indexes-and-constraints-at-ALTER-.patchDownload
>From 9a50ee7f08107c7e8499c0a07034dc8104de55d6 Mon Sep 17 00:00:00 2001
From: Heikki Linnakangas <heikki.linnakangas@iki.fi>
Date: Mon, 13 Jul 2015 19:22:35 +0300
Subject: [PATCH 2/2] Retain comments on indexes and constraints at ALTER TABLE
... TYPE ...
When a column's datatype is changed, ATExecAlterColumnType() rebuilds all
the affected indexes and constraints, and the comments from the old
indexes/constraints were not carried over.
To fix, create a synthetic COMMENT ON command in the work queue, to re-add
any comments on constraints. For indexes, there's a comment field in
IndexStmt that is used.
Original patch by Michael Paquier, reviewed by Petr Jelinek and me. This
fixes bug #13126, reported by Kirill Simonov.
---
src/backend/commands/tablecmds.c | 65 ++++++++++++++++++++++++++++++-
src/include/nodes/parsenodes.h | 1 +
src/test/regress/expected/alter_table.out | 63 ++++++++++++++++++++++++++++++
src/test/regress/sql/alter_table.sql | 36 +++++++++++++++++
4 files changed, 163 insertions(+), 2 deletions(-)
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index e7b23f1..1c7eded 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -386,6 +386,8 @@ static void ATPostAlterTypeCleanup(List **wqueue, AlteredTableInfo *tab,
static void ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId,
char *cmd, List **wqueue, LOCKMODE lockmode,
bool rewrite);
+static void RebuildConstraintComment(AlteredTableInfo *tab, int pass,
+ Oid objid, Relation rel, char *conname);
static void TryReuseIndex(Oid oldId, IndexStmt *stmt);
static void TryReuseForeignKey(Oid oldId, Constraint *con);
static void change_owner_fix_column_acls(Oid relationOid,
@@ -3514,6 +3516,9 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
ATExecAddConstraint(wqueue, tab, rel, (Constraint *) cmd->def,
false, true, lockmode);
break;
+ case AT_ReAddComment: /* Re-add existing comment */
+ address = CommentObject((CommentStmt *) cmd->def);
+ break;
case AT_AddIndexConstraint: /* ADD CONSTRAINT USING INDEX */
address = ATExecAddIndexConstraint(tab, rel, (IndexStmt *) cmd->def,
lockmode);
@@ -8654,6 +8659,8 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
if (!rewrite)
TryReuseIndex(oldId, stmt);
+ /* keep the index's comment */
+ stmt->idxcomment = GetComment(oldId, RelationRelationId, 0);
newcmd = makeNode(AlterTableCmd);
newcmd->subtype = AT_ReAddIndex;
@@ -8672,15 +8679,29 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
if (cmd->subtype == AT_AddIndex)
{
+ IndexStmt *indstmt;
+ Oid indoid;
+
Assert(IsA(cmd->def, IndexStmt));
+ indstmt = (IndexStmt *) cmd->def;
+ indoid = get_constraint_index(oldId);
+
if (!rewrite)
- TryReuseIndex(get_constraint_index(oldId),
- (IndexStmt *) cmd->def);
+ TryReuseIndex(indoid, indstmt);
+ /* keep any comment on the index */
+ indstmt->idxcomment = GetComment(indoid,
+ RelationRelationId, 0);
cmd->subtype = AT_ReAddIndex;
tab->subcmds[AT_PASS_OLD_INDEX] =
lappend(tab->subcmds[AT_PASS_OLD_INDEX], cmd);
+
+ /* recreate any comment on the constraint */
+ RebuildConstraintComment(tab,
+ AT_PASS_OLD_INDEX,
+ oldId,
+ rel, indstmt->idxname);
}
else if (cmd->subtype == AT_AddConstraint)
{
@@ -8697,6 +8718,12 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
cmd->subtype = AT_ReAddConstraint;
tab->subcmds[AT_PASS_OLD_CONSTR] =
lappend(tab->subcmds[AT_PASS_OLD_CONSTR], cmd);
+
+ /* recreate any comment on the constraint */
+ RebuildConstraintComment(tab,
+ AT_PASS_OLD_CONSTR,
+ oldId,
+ rel, con->conname);
}
else
elog(ERROR, "unexpected statement type: %d",
@@ -8712,6 +8739,40 @@ ATPostAlterTypeParse(Oid oldId, Oid oldRelId, Oid refRelId, char *cmd,
}
/*
+ * Subroutine for ATPostAlterTypeParse() to recreate a comment entry for
+ * a constraint that is being re-added.
+ */
+static void
+RebuildConstraintComment(AlteredTableInfo *tab, int pass, Oid objid,
+ Relation rel, char *conname)
+{
+ CommentStmt *cmd;
+ char *comment_str;
+ AlterTableCmd *newcmd;
+
+ /* Look for comment for object wanted, and leave if none */
+ comment_str = GetComment(objid, ConstraintRelationId, 0);
+ if (comment_str == NULL)
+ return;
+
+ /* Build node CommentStmt */
+ cmd = makeNode(CommentStmt);
+ cmd->objtype = OBJECT_TABCONSTRAINT;
+ cmd->objname = list_make3(
+ makeString(get_namespace_name(RelationGetNamespace(rel))),
+ makeString(RelationGetRelationName(rel)),
+ makeString(conname));
+ cmd->objargs = NIL;
+ cmd->comment = comment_str;
+
+ /* Append it to list of commands */
+ newcmd = makeNode(AlterTableCmd);
+ newcmd->subtype = AT_ReAddComment;
+ newcmd->def = (Node *) cmd;
+ tab->subcmds[pass] = lappend(tab->subcmds[pass], newcmd);
+}
+
+/*
* Subroutine for ATPostAlterTypeParse(). Calls out to CheckIndexCompatible()
* for the real analysis, then mutates the IndexStmt based on that verdict.
*/
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 868905b..a567c50 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -1474,6 +1474,7 @@ typedef enum AlterTableType
AT_AddConstraint, /* add constraint */
AT_AddConstraintRecurse, /* internal to commands/tablecmds.c */
AT_ReAddConstraint, /* internal to commands/tablecmds.c */
+ AT_ReAddComment, /* internal to commands/tablecmds.c */
AT_AlterConstraint, /* alter constraint */
AT_ValidateConstraint, /* validate constraint */
AT_ValidateConstraintRecurse, /* internal to commands/tablecmds.c */
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 3ad2c55..6b9291b 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2400,6 +2400,69 @@ Check constraints:
DROP TABLE alter2.tt8;
DROP SCHEMA alter2;
+-- Check that comments on constraints and indexes are not lost at ALTER TABLE.
+CREATE TABLE comment_test (
+ id int,
+ positive_col int CHECK (positive_col > 0),
+ indexed_col int,
+ CONSTRAINT comment_test_pk PRIMARY KEY (id));
+CREATE INDEX comment_test_index ON comment_test(indexed_col);
+COMMENT ON COLUMN comment_test.id IS 'Column ''id'' on comment_test';
+COMMENT ON INDEX comment_test_index IS 'Simple index on comment_test';
+COMMENT ON CONSTRAINT comment_test_positive_col_check ON comment_test IS 'CHECK constraint on comment_test.positive_col';
+COMMENT ON CONSTRAINT comment_test_pk ON comment_test IS 'PRIMARY KEY constraint of comment_test';
+COMMENT ON INDEX comment_test_pk IS 'Index backing the PRIMARY KEY of comment_test';
+SELECT col_description('comment_test'::regclass, 1) as comment;
+ comment
+-----------------------------
+ Column 'id' on comment_test
+(1 row)
+
+SELECT indexrelid::regclass as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass;
+ index | comment
+--------------------+-----------------------------------------------
+ comment_test_index | Simple index on comment_test
+ comment_test_pk | Index backing the PRIMARY KEY of comment_test
+(2 rows)
+
+SELECT conname as constraint, obj_description(oid, 'pg_constraint') as comment FROM pg_constraint where conrelid = 'comment_test'::regclass;
+ constraint | comment
+---------------------------------+-----------------------------------------------
+ comment_test_pk | PRIMARY KEY constraint of comment_test
+ comment_test_positive_col_check | CHECK constraint on comment_test.positive_col
+(2 rows)
+
+-- Change the datatype of all the columns. ALTER TABLE is optimized to not
+-- rebuild an index if the new data type is binary compatible with the old
+-- one. Check do a dummy ALTER TABLE that doesn't change the datatype
+-- first, to test that no-op codepath, and another one that does.
+ALTER TABLE comment_test ALTER COLUMN indexed_col SET DATA TYPE int;
+ALTER TABLE comment_test ALTER COLUMN indexed_col SET DATA TYPE text;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
+ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
+ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
+-- Check that the comments are intact.
+SELECT col_description('comment_test'::regclass, 1) as comment;
+ comment
+-----------------------------
+ Column 'id' on comment_test
+(1 row)
+
+SELECT indexrelid::regclass as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass;
+ index | comment
+--------------------+-----------------------------------------------
+ comment_test_pk | Index backing the PRIMARY KEY of comment_test
+ comment_test_index | Simple index on comment_test
+(2 rows)
+
+SELECT conname as constraint, obj_description(oid, 'pg_constraint') as comment FROM pg_constraint where conrelid = 'comment_test'::regclass;
+ constraint | comment
+---------------------------------+-----------------------------------------------
+ comment_test_positive_col_check | CHECK constraint on comment_test.positive_col
+ comment_test_pk | PRIMARY KEY constraint of comment_test
+(2 rows)
+
-- Check that we map relation oids to filenodes and back correctly. Only
-- display bad mappings so the test output doesn't change all the time. A
-- filenode function call can return NULL for a relation dropped concurrently
diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql
index 29c1875..9f755fa 100644
--- a/src/test/regress/sql/alter_table.sql
+++ b/src/test/regress/sql/alter_table.sql
@@ -1594,6 +1594,42 @@ ALTER TABLE IF EXISTS tt8 SET SCHEMA alter2;
DROP TABLE alter2.tt8;
DROP SCHEMA alter2;
+
+-- Check that comments on constraints and indexes are not lost at ALTER TABLE.
+CREATE TABLE comment_test (
+ id int,
+ positive_col int CHECK (positive_col > 0),
+ indexed_col int,
+ CONSTRAINT comment_test_pk PRIMARY KEY (id));
+CREATE INDEX comment_test_index ON comment_test(indexed_col);
+
+COMMENT ON COLUMN comment_test.id IS 'Column ''id'' on comment_test';
+COMMENT ON INDEX comment_test_index IS 'Simple index on comment_test';
+COMMENT ON CONSTRAINT comment_test_positive_col_check ON comment_test IS 'CHECK constraint on comment_test.positive_col';
+COMMENT ON CONSTRAINT comment_test_pk ON comment_test IS 'PRIMARY KEY constraint of comment_test';
+COMMENT ON INDEX comment_test_pk IS 'Index backing the PRIMARY KEY of comment_test';
+
+SELECT col_description('comment_test'::regclass, 1) as comment;
+SELECT indexrelid::regclass as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass;
+SELECT conname as constraint, obj_description(oid, 'pg_constraint') as comment FROM pg_constraint where conrelid = 'comment_test'::regclass;
+
+-- Change the datatype of all the columns. ALTER TABLE is optimized to not
+-- rebuild an index if the new data type is binary compatible with the old
+-- one. Check do a dummy ALTER TABLE that doesn't change the datatype
+-- first, to test that no-op codepath, and another one that does.
+ALTER TABLE comment_test ALTER COLUMN indexed_col SET DATA TYPE int;
+ALTER TABLE comment_test ALTER COLUMN indexed_col SET DATA TYPE text;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE int;
+ALTER TABLE comment_test ALTER COLUMN id SET DATA TYPE text;
+ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE int;
+ALTER TABLE comment_test ALTER COLUMN positive_col SET DATA TYPE bigint;
+
+-- Check that the comments are intact.
+SELECT col_description('comment_test'::regclass, 1) as comment;
+SELECT indexrelid::regclass as index, obj_description(indexrelid, 'pg_class') as comment FROM pg_index where indrelid = 'comment_test'::regclass;
+SELECT conname as constraint, obj_description(oid, 'pg_constraint') as comment FROM pg_constraint where conrelid = 'comment_test'::regclass;
+
+
-- Check that we map relation oids to filenodes and back correctly. Only
-- display bad mappings so the test output doesn't change all the time. A
-- filenode function call can return NULL for a relation dropped concurrently
--
2.1.4
On Tue, Jul 14, 2015 at 1:42 AM, Heikki Linnakangas wrote:
There was one bug in this patch: the COMMENT statement that was constructed
didn't schema-qualify the relation, so if the ALTERed table was not in
search_path, the operation would fail with a "relation not found" error (or
add the comment to wrong object). Fixed that.
Ouch. I had a look at the patches and they look neater than what I
drafted. Thanks!
I plan to commit the attached patches later today or tomorrow. But how do
you feel about back-patching this? It should be possible to backpatch,
although at a quick test it seems that there have been small changes to the
affected code in many versions, so it would require some work. Also, in
back-branches we'd need to put the new AT_ReAddComment type to the end of
the list, like we've done when we added AT_ReAddConstraint in 9.3. I'm
inclined to backpatch this to 9.5 only, even though this is clearly a bug
fix, on the grounds that it's not a very serious bug and there's always some
risk in backpatching.
Well, while it's clearly a bug I don't think that it is worth the risk
to destabilize back branches older than 9.5 in this code path. So +1
for doing it the way you are suggesting. We could still revisit that
later on if there are more complaints, but I doubt there will be much.
--
Michael
--
Sent via pgsql-bugs mailing list (pgsql-bugs@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-bugs
On 07/14/2015 10:29 AM, Michael Paquier wrote:
On Tue, Jul 14, 2015 at 1:42 AM, Heikki Linnakangas wrote:
I plan to commit the attached patches later today or tomorrow. But how do
you feel about back-patching this? It should be possible to backpatch,
although at a quick test it seems that there have been small changes to the
affected code in many versions, so it would require some work. Also, in
back-branches we'd need to put the new AT_ReAddComment type to the end of
the list, like we've done when we added AT_ReAddConstraint in 9.3. I'm
inclined to backpatch this to 9.5 only, even though this is clearly a bug
fix, on the grounds that it's not a very serious bug and there's always some
risk in backpatching.Well, while it's clearly a bug I don't think that it is worth the risk
to destabilize back branches older than 9.5 in this code path. So +1
for doing it the way you are suggesting. We could still revisit that
later on if there are more complaints, but I doubt there will be much.
Ok, committed to master and 9.5. Thanks!
- Heikki
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers