]> granicus.if.org Git - postgresql/commitdiff
Code review for LIKE ... INCLUDING INDEXES patch. Fix failure to propagate
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 1 Dec 2007 23:44:44 +0000 (23:44 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 1 Dec 2007 23:44:44 +0000 (23:44 +0000)
constraint status of copied indexes (bug #3774), as well as various other
small bugs such as failure to pstrdup when needed.  Allow INCLUDING INDEXES
indexes to be merged with identical declared indexes (perhaps not real useful,
but the code is there and having it not apply to LIKE indexes seems pretty
unorthogonal).  Avoid useless work in generateClonedIndexStmt().  Undo some
poorly chosen API changes, and put a couple of routines in modules that seem
to be better places for them.

17 files changed:
src/backend/access/common/reloptions.c
src/backend/bootstrap/bootparse.y
src/backend/catalog/pg_depend.c
src/backend/commands/indexcmds.c
src/backend/commands/tablecmds.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/nodes/outfuncs.c
src/backend/parser/parse_utilcmd.c
src/backend/tcop/utility.c
src/backend/utils/adt/ruleutils.c
src/include/access/reloptions.h
src/include/catalog/dependency.h
src/include/commands/defrem.h
src/include/nodes/parsenodes.h
src/include/utils/builtins.h
src/test/regress/expected/inherit.out

index 7e4afd70bd520d797067c948d386b4bab0a92b96..8b3331702c5dce5ed47ae05a8c702bf1ee4394b5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.6 2007/11/15 21:14:31 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/access/common/reloptions.c,v 1.7 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -18,6 +18,7 @@
 #include "access/reloptions.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
+#include "nodes/makefuncs.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/rel.h"
@@ -149,6 +150,50 @@ transformRelOptions(Datum oldOptions, List *defList,
 }
 
 
+/*
+ * Convert the text-array format of reloptions into a List of DefElem.
+ * This is the inverse of transformRelOptions().
+ */
+List *
+untransformRelOptions(Datum options)
+{
+       List       *result = NIL;
+       ArrayType  *array;
+       Datum      *optiondatums;
+       int                     noptions;
+       int                     i;
+
+       /* Nothing to do if no options */
+       if (options == (Datum) 0)
+               return result;
+
+       array = DatumGetArrayTypeP(options);
+
+       Assert(ARR_ELEMTYPE(array) == TEXTOID);
+
+       deconstruct_array(array, TEXTOID, -1, false, 'i',
+                                         &optiondatums, NULL, &noptions);
+
+       for (i = 0; i < noptions; i++)
+       {
+               char       *s;
+               char       *p;
+               Node       *val = NULL;
+
+               s = DatumGetCString(DirectFunctionCall1(textout, optiondatums[i]));
+               p = strchr(s, '=');
+               if (p)
+               {
+                       *p++ = '\0';
+                       val = (Node *) makeString(pstrdup(p));
+               }
+               result = lappend(result, makeDefElem(pstrdup(s), val));
+       }
+
+       return result;
+}
+
+
 /*
  * Interpret reloptions that are given in text-array format.
  *
index 3fd29b7e97168894adf42eab40ba47fc28e37242..bd640804c461788e19780aeeabf66664d10deb01 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.89 2007/07/17 05:02:00 neilc Exp $
+ *       $PostgreSQL: pgsql/src/backend/bootstrap/bootparse.y,v 1.90 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -252,7 +252,7 @@ Boot_DeclareIndexStmt:
                                                                LexIDStr($8),
                                                                NULL,
                                                                $10,
-                                                               NULL, NIL, NULL,
+                                                               NULL, NIL,
                                                                false, false, false,
                                                                false, false, true, false, false);
                                        do_end();
@@ -270,7 +270,7 @@ Boot_DeclareUniqueIndexStmt:
                                                                LexIDStr($9),
                                                                NULL,
                                                                $11,
-                                                               NULL, NIL, NULL,
+                                                               NULL, NIL,
                                                                true, false, false,
                                                                false, false, true, false, false);
                                        do_end();
index 3574ab1ba3a3ed3e36153e82e73f4590d5b0d8a7..a35b376cc10db94a051031dff4b88f2b1eb42088 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.24 2007/01/05 22:19:25 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/catalog/pg_depend.c,v 1.25 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #include "access/heapam.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
+#include "catalog/pg_constraint.h"
 #include "catalog/pg_depend.h"
 #include "miscadmin.h"
 #include "utils/fmgroids.h"
+#include "utils/lsyscache.h"
 
 
 static bool isObjectPinned(const ObjectAddress *object, Relation rel);
@@ -260,6 +262,62 @@ changeDependencyFor(Oid classId, Oid objectId,
        return count;
 }
 
+/*
+ * isObjectPinned()
+ *
+ * Test if an object is required for basic database functionality.
+ * Caller must already have opened pg_depend.
+ *
+ * The passed subId, if any, is ignored; we assume that only whole objects
+ * are pinned (and that this implies pinning their components).
+ */
+static bool
+isObjectPinned(const ObjectAddress *object, Relation rel)
+{
+       bool            ret = false;
+       SysScanDesc scan;
+       HeapTuple       tup;
+       ScanKeyData key[2];
+
+       ScanKeyInit(&key[0],
+                               Anum_pg_depend_refclassid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(object->classId));
+
+       ScanKeyInit(&key[1],
+                               Anum_pg_depend_refobjid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(object->objectId));
+
+       scan = systable_beginscan(rel, DependReferenceIndexId, true,
+                                                         SnapshotNow, 2, key);
+
+       /*
+        * Since we won't generate additional pg_depend entries for pinned
+        * objects, there can be at most one entry referencing a pinned object.
+        * Hence, it's sufficient to look at the first returned tuple; we don't
+        * need to loop.
+        */
+       tup = systable_getnext(scan);
+       if (HeapTupleIsValid(tup))
+       {
+               Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
+
+               if (foundDep->deptype == DEPENDENCY_PIN)
+                       ret = true;
+       }
+
+       systable_endscan(scan);
+
+       return ret;
+}
+
+
+/*
+ * Various special-purpose lookups and manipulations of pg_depend.
+ */
+
+
 /*
  * Detect whether a sequence is marked as "owned" by a column
  *
@@ -359,52 +417,120 @@ markSequenceUnowned(Oid seqId)
        heap_close(depRel, RowExclusiveLock);
 }
 
+
 /*
- * isObjectPinned()
- *
- * Test if an object is required for basic database functionality.
- * Caller must already have opened pg_depend.
+ * get_constraint_index
+ *             Given the OID of a unique or primary-key constraint, return the
+ *             OID of the underlying unique index.
  *
- * The passed subId, if any, is ignored; we assume that only whole objects
- * are pinned (and that this implies pinning their components).
+ * Return InvalidOid if the index couldn't be found; this suggests the
+ * given OID is bogus, but we leave it to caller to decide what to do.
  */
-static bool
-isObjectPinned(const ObjectAddress *object, Relation rel)
+Oid
+get_constraint_index(Oid constraintId)
 {
-       bool            ret = false;
+       Oid                     indexId = InvalidOid;
+       Relation        depRel;
+       ScanKeyData key[3];
        SysScanDesc scan;
        HeapTuple       tup;
-       ScanKeyData key[2];
+
+       /* Search the dependency table for the dependent index */
+       depRel = heap_open(DependRelationId, AccessShareLock);
 
        ScanKeyInit(&key[0],
                                Anum_pg_depend_refclassid,
                                BTEqualStrategyNumber, F_OIDEQ,
-                               ObjectIdGetDatum(object->classId));
-
+                               ObjectIdGetDatum(ConstraintRelationId));
        ScanKeyInit(&key[1],
                                Anum_pg_depend_refobjid,
                                BTEqualStrategyNumber, F_OIDEQ,
-                               ObjectIdGetDatum(object->objectId));
+                               ObjectIdGetDatum(constraintId));
+       ScanKeyInit(&key[2],
+                               Anum_pg_depend_refobjsubid,
+                               BTEqualStrategyNumber, F_INT4EQ,
+                               Int32GetDatum(0));
 
-       scan = systable_beginscan(rel, DependReferenceIndexId, true,
-                                                         SnapshotNow, 2, key);
+       scan = systable_beginscan(depRel, DependReferenceIndexId, true,
+                                                         SnapshotNow, 3, key);
 
-       /*
-        * Since we won't generate additional pg_depend entries for pinned
-        * objects, there can be at most one entry referencing a pinned object.
-        * Hence, it's sufficient to look at the first returned tuple; we don't
-        * need to loop.
-        */
-       tup = systable_getnext(scan);
-       if (HeapTupleIsValid(tup))
+       while (HeapTupleIsValid(tup = systable_getnext(scan)))
        {
-               Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup);
+               Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
 
-               if (foundDep->deptype == DEPENDENCY_PIN)
-                       ret = true;
+               /*
+                * We assume any internal dependency of an index on the constraint
+                * must be what we are looking for.  (The relkind test is just
+                * paranoia; there shouldn't be any such dependencies otherwise.)
+                */
+               if (deprec->classid == RelationRelationId &&
+                       deprec->objsubid == 0 &&
+                       deprec->deptype == DEPENDENCY_INTERNAL &&
+                       get_rel_relkind(deprec->objid) == RELKIND_INDEX)
+               {
+                       indexId = deprec->objid;
+                       break;
+               }
        }
 
        systable_endscan(scan);
+       heap_close(depRel, AccessShareLock);
 
-       return ret;
+       return indexId;
+}
+
+/*
+ * get_index_constraint
+ *             Given the OID of an index, return the OID of the owning unique or
+ *             primary-key constraint, or InvalidOid if no such constraint.
+ */
+Oid
+get_index_constraint(Oid indexId)
+{
+       Oid                     constraintId = InvalidOid;
+       Relation        depRel;
+       ScanKeyData key[3];
+       SysScanDesc scan;
+       HeapTuple       tup;
+
+       /* Search the dependency table for the index */
+       depRel = heap_open(DependRelationId, AccessShareLock);
+
+       ScanKeyInit(&key[0],
+                               Anum_pg_depend_classid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(RelationRelationId));
+       ScanKeyInit(&key[1],
+                               Anum_pg_depend_objid,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(indexId));
+       ScanKeyInit(&key[2],
+                               Anum_pg_depend_objsubid,
+                               BTEqualStrategyNumber, F_INT4EQ,
+                               Int32GetDatum(0));
+
+       scan = systable_beginscan(depRel, DependDependerIndexId, true,
+                                                         SnapshotNow, 3, key);
+
+       while (HeapTupleIsValid(tup = systable_getnext(scan)))
+       {
+               Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
+
+               /*
+                * We assume any internal dependency on a constraint
+                * must be what we are looking for.
+                */
+               if (deprec->refclassid == ConstraintRelationId &&
+                       deprec->refobjsubid == 0 &&
+                       deprec->deptype == DEPENDENCY_INTERNAL)
+               {
+                       constraintId = deprec->refobjid;
+                       break;
+               }
+       }
+
+       systable_endscan(scan);
+       heap_close(depRel, AccessShareLock);
+
+       return constraintId;
 }
index dc53546a05f3b2243750c724a958251359c4d3c6..1ae51246e795c770607cea92241621d907b23c1d 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.167 2007/11/15 21:14:33 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/indexcmds.c,v 1.168 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -80,8 +80,6 @@ static bool relationHasPrimaryKey(Relation rel);
  *             to index on.
  * 'predicate': the partial-index condition, or NULL if none.
  * 'options': reloptions from WITH (in list-of-DefElem form).
- * 'src_options': reloptions from the source index, if this is a cloned
- *             index produced by CREATE TABLE LIKE ... INCLUDING INDEXES
  * 'unique': make the index enforce uniqueness.
  * 'primary': mark the index as a primary key in the catalogs.
  * 'isconstraint': index is for a PRIMARY KEY or UNIQUE constraint,
@@ -103,7 +101,6 @@ DefineIndex(RangeVar *heapRelation,
                        List *attributeList,
                        Expr *predicate,
                        List *options,
-                       char *src_options,
                        bool unique,
                        bool primary,
                        bool isconstraint,
@@ -396,16 +393,9 @@ DefineIndex(RangeVar *heapRelation,
        }
 
        /*
-        * Parse AM-specific options, convert to text array form, validate.  The
-        * src_options introduced due to using indexes via the "CREATE LIKE
-        * INCLUDING INDEXES" statement also need to be merged here
+        * Parse AM-specific options, convert to text array form, validate.
         */
-       if (src_options)
-               reloptions = unflatten_reloptions(src_options);
-       else
-               reloptions = (Datum) 0;
-
-       reloptions = transformRelOptions(reloptions, options, false, false);
+       reloptions = transformRelOptions((Datum) 0, options, false, false);
 
        (void) index_reloptions(amoptions, reloptions, true);
 
index 285bc23496708da17f739d94dc021c8c051f415b..2310c821d03b7d710f798f182660fc82425798e7 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.236 2007/11/15 21:14:33 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.237 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3795,7 +3795,6 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
                                stmt->indexParams,              /* parameters */
                                (Expr *) stmt->whereClause,
                                stmt->options,
-                               stmt->src_options,
                                stmt->unique,
                                stmt->primary,
                                stmt->isconstraint,
index 17de2ff3b8807932799e199fd67b2a92ba2b423f..ebee72f80c7897bb7822b3e68df86a571b8f5247 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.385 2007/11/15 22:25:15 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.386 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2195,7 +2195,6 @@ _copyIndexStmt(IndexStmt *from)
        COPY_STRING_FIELD(tableSpace);
        COPY_NODE_FIELD(indexParams);
        COPY_NODE_FIELD(options);
-       COPY_STRING_FIELD(src_options);
        COPY_NODE_FIELD(whereClause);
        COPY_SCALAR_FIELD(unique);
        COPY_SCALAR_FIELD(primary);
index 115436237196a956db39f45ae1a05574b73500f3..ebca3370dfabed93e8043d3a1727cb86ac617b07 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.315 2007/11/15 22:25:15 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.316 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1046,7 +1046,6 @@ _equalIndexStmt(IndexStmt *a, IndexStmt *b)
        COMPARE_STRING_FIELD(tableSpace);
        COMPARE_NODE_FIELD(indexParams);
        COMPARE_NODE_FIELD(options);
-       COMPARE_STRING_FIELD(src_options);
        COMPARE_NODE_FIELD(whereClause);
        COMPARE_SCALAR_FIELD(unique);
        COMPARE_SCALAR_FIELD(primary);
index 7d3952c86bd5e501f8a9b21ec9eedba650922f7c..ee56af2bc84faed7848bb15bdcc4b535b80d0435 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.318 2007/11/15 22:25:15 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.319 2007/12/01 23:44:44 tgl Exp $
  *
  * NOTES
  *       Every node type that can appear in stored rules' parsetrees *must*
@@ -1546,7 +1546,6 @@ _outIndexStmt(StringInfo str, IndexStmt *node)
        WRITE_STRING_FIELD(tableSpace);
        WRITE_NODE_FIELD(indexParams);
        WRITE_NODE_FIELD(options);
-       WRITE_STRING_FIELD(src_options);
        WRITE_NODE_FIELD(whereClause);
        WRITE_BOOL_FIELD(unique);
        WRITE_BOOL_FIELD(primary);
index 2ff6f9274d717ba946debe202e35ef303da40449..a53f01c32abe797d7c1f582da4614df6f09de69b 100644 (file)
@@ -19,7 +19,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- *     $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.6 2007/11/15 21:14:37 momjian Exp $
+ *     $PostgreSQL: pgsql/src/backend/parser/parse_utilcmd.c,v 2.7 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -28,6 +28,8 @@
 
 #include "access/genam.h"
 #include "access/heapam.h"
+#include "access/reloptions.h"
+#include "catalog/dependency.h"
 #include "catalog/heap.h"
 #include "catalog/index.h"
 #include "catalog/namespace.h"
@@ -675,13 +677,15 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
                }
        }
 
+       /*
+        * Likewise, copy indexes if requested
+        */
        if (including_indexes && relation->rd_rel->relhasindex)
        {
-               AttrNumber *attmap;
+               AttrNumber *attmap = varattnos_map_schema(tupleDesc, cxt->columns);
                List       *parent_indexes;
                ListCell   *l;
 
-               attmap = varattnos_map_schema(tupleDesc, cxt->columns);
                parent_indexes = RelationGetIndexList(relation);
 
                foreach(l, parent_indexes)
@@ -693,14 +697,12 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
                        parent_index = index_open(parent_index_oid, AccessShareLock);
 
                        /* Build CREATE INDEX statement to recreate the parent_index */
-                       index_stmt = generateClonedIndexStmt(cxt, parent_index,
-                                                                                                attmap);
+                       index_stmt = generateClonedIndexStmt(cxt, parent_index, attmap);
 
-                       /* Add the new IndexStmt to the create context */
+                       /* Save it in the inh_indexes list for the time being */
                        cxt->inh_indexes = lappend(cxt->inh_indexes, index_stmt);
 
-                       /* Keep our lock on the index till xact commit */
-                       index_close(parent_index, NoLock);
+                       index_close(parent_index, AccessShareLock);
                }
        }
 
@@ -713,54 +715,62 @@ transformInhRelation(ParseState *pstate, CreateStmtContext *cxt,
 }
 
 /*
- * Generate an IndexStmt entry using information from an already
- * existing index "source_idx".
- *
- * Note: Much of this functionality is cribbed from pg_get_indexdef.
+ * Generate an IndexStmt node using information from an already existing index
+ * "source_idx".  Attribute numbers should be adjusted according to attmap.
  */
 static IndexStmt *
 generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
                                                AttrNumber *attmap)
 {
-       HeapTuple       ht_idx;
+       Oid                     source_relid = RelationGetRelid(source_idx);
        HeapTuple       ht_idxrel;
-       HeapTuple       ht_am;
-       Form_pg_index idxrec;
+       HeapTuple       ht_idx;
        Form_pg_class idxrelrec;
+       Form_pg_index idxrec;
        Form_pg_am      amrec;
-       List       *indexprs = NIL;
+       oidvector  *indclass;
+       IndexStmt  *index;
+       List       *indexprs;
        ListCell   *indexpr_item;
        Oid                     indrelid;
-       Oid                     source_relid;
        int                     keyno;
        Oid                     keycoltype;
-       Datum           indclassDatum;
-       Datum           indoptionDatum;
+       Datum           datum;
        bool            isnull;
-       oidvector  *indclass;
-       int2vector *indoption;
-       IndexStmt  *index;
-       Datum           reloptions;
 
-       source_relid = RelationGetRelid(source_idx);
+       /*
+        * Fetch pg_class tuple of source index.  We can't use the copy in the
+        * relcache entry because it doesn't include optional fields.
+        */
+       ht_idxrel = SearchSysCache(RELOID,
+                                                          ObjectIdGetDatum(source_relid),
+                                                          0, 0, 0);
+       if (!HeapTupleIsValid(ht_idxrel))
+               elog(ERROR, "cache lookup failed for relation %u", source_relid);
+       idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
 
-       /* Fetch pg_index tuple for source index */
-       ht_idx = SearchSysCache(INDEXRELID,
-                                                       ObjectIdGetDatum(source_relid),
-                                                       0, 0, 0);
-       if (!HeapTupleIsValid(ht_idx))
-               elog(ERROR, "cache lookup failed for index %u", source_relid);
+       /* Fetch pg_index tuple for source index from relcache entry */
+       ht_idx = source_idx->rd_indextuple;
        idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
-
-       Assert(source_relid == idxrec->indexrelid);
        indrelid = idxrec->indrelid;
 
+       /* Fetch pg_am tuple for source index from relcache entry */
+       amrec = source_idx->rd_am;
+
+       /* Must get indclass the hard way, since it's not stored in relcache */
+       datum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                                       Anum_pg_index_indclass, &isnull);
+       Assert(!isnull);
+       indclass = (oidvector *) DatumGetPointer(datum);
+
+       /* Begin building the IndexStmt */
        index = makeNode(IndexStmt);
+       index->relation = cxt->relation;
+       index->accessMethod = pstrdup(NameStr(amrec->amname));
+       index->tableSpace = get_tablespace_name(source_idx->rd_node.spcNode);
        index->unique = idxrec->indisunique;
-       index->concurrent = false;
        index->primary = idxrec->indisprimary;
-       index->relation = cxt->relation;
-       index->isconstraint = false;
+       index->concurrent = false;
 
        /*
         * We don't try to preserve the name of the source index; instead, just
@@ -768,65 +778,40 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
         */
        index->idxname = NULL;
 
-       /* Must get indclass and indoption the hard way */
-       indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
-                                                                       Anum_pg_index_indclass, &isnull);
-       Assert(!isnull);
-       indclass = (oidvector *) DatumGetPointer(indclassDatum);
-       indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
-                                                                        Anum_pg_index_indoption, &isnull);
-       Assert(!isnull);
-       indoption = (int2vector *) DatumGetPointer(indoptionDatum);
-
-       /* Fetch pg_class tuple of source index */
-       ht_idxrel = SearchSysCache(RELOID,
-                                                          ObjectIdGetDatum(source_relid),
-                                                          0, 0, 0);
-       if (!HeapTupleIsValid(ht_idxrel))
-               elog(ERROR, "cache lookup failed for relation %u", source_relid);
-
        /*
-        * Store the reloptions for later use by this new index
+        * If the index is marked PRIMARY, it's certainly from a constraint;
+        * else, if it's not marked UNIQUE, it certainly isn't; else, we have
+        * to search pg_depend to see if there's an associated unique constraint.
         */
-       reloptions = SysCacheGetAttr(RELOID, ht_idxrel,
-                                                                Anum_pg_class_reloptions, &isnull);
-       if (!isnull)
-               index->src_options = flatten_reloptions(source_relid);
-
-       idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
-
-       /* Fetch pg_am tuple for the index's access method */
-       ht_am = SearchSysCache(AMOID,
-                                                  ObjectIdGetDatum(idxrelrec->relam),
-                                                  0, 0, 0);
-       if (!HeapTupleIsValid(ht_am))
-               elog(ERROR, "cache lookup failed for access method %u",
-                        idxrelrec->relam);
-       amrec = (Form_pg_am) GETSTRUCT(ht_am);
-       index->accessMethod = pstrdup(NameStr(amrec->amname));
+       if (index->primary)
+               index->isconstraint = true;
+       else if (!index->unique)
+               index->isconstraint = false;
+       else
+               index->isconstraint = OidIsValid(get_index_constraint(source_relid));
 
        /* Get the index expressions, if any */
-       if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs))
+       datum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                                       Anum_pg_index_indexprs, &isnull);
+       if (!isnull)
        {
-               Datum           exprsDatum;
-               bool            isnull;
                char       *exprsString;
 
-               exprsDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
-                                                                        Anum_pg_index_indexprs, &isnull);
-               exprsString = DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                                 exprsDatum));
-               Assert(!isnull);
+               exprsString = DatumGetCString(DirectFunctionCall1(textout, datum));
                indexprs = (List *) stringToNode(exprsString);
        }
+       else
+               indexprs = NIL;
 
-       indexpr_item = list_head(indexprs);
+       /* Build the list of IndexElem */
+       index->indexParams = NIL;
 
+       indexpr_item = list_head(indexprs);
        for (keyno = 0; keyno < idxrec->indnatts; keyno++)
        {
                IndexElem  *iparam;
                AttrNumber      attnum = idxrec->indkey.values[keyno];
-               int16           opt = indoption->values[keyno];
+               int16           opt = source_idx->rd_indoption[keyno];
 
                iparam = makeNode(IndexElem);
 
@@ -849,11 +834,14 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
                        if (indexpr_item == NULL)
                                elog(ERROR, "too few entries in indexprs list");
                        indexkey = (Node *) lfirst(indexpr_item);
+                       indexpr_item = lnext(indexpr_item);
+
+                       /* OK to modify indexkey since we are working on a private copy */
                        change_varattnos_of_a_node(indexkey, attmap);
+
                        iparam->name = NULL;
                        iparam->expr = indexkey;
 
-                       indexpr_item = lnext(indexpr_item);
                        keycoltype = exprType(indexkey);
                }
 
@@ -866,40 +854,50 @@ generateClonedIndexStmt(CreateStmtContext *cxt, Relation source_idx,
                /* Adjust options if necessary */
                if (amrec->amcanorder)
                {
-                       /* If it supports sort ordering, report DESC and NULLS opts */
+                       /*
+                        * If it supports sort ordering, copy DESC and NULLS opts.
+                        * Don't set non-default settings unnecessarily, though,
+                        * so as to improve the chance of recognizing equivalence
+                        * to constraint indexes.
+                        */
                        if (opt & INDOPTION_DESC)
+                       {
                                iparam->ordering = SORTBY_DESC;
-                       if (opt & INDOPTION_NULLS_FIRST)
-                               iparam->nulls_ordering = SORTBY_NULLS_FIRST;
+                               if ((opt & INDOPTION_NULLS_FIRST) == 0)
+                                       iparam->nulls_ordering = SORTBY_NULLS_LAST;
+                       }
+                       else
+                       {
+                               if (opt & INDOPTION_NULLS_FIRST)
+                                       iparam->nulls_ordering = SORTBY_NULLS_FIRST;
+                       }
                }
 
                index->indexParams = lappend(index->indexParams, iparam);
        }
 
-       /* Use the same tablespace as the source index */
-       index->tableSpace = get_tablespace_name(source_idx->rd_node.spcNode);
+       /* Copy reloptions if any */
+       datum = SysCacheGetAttr(RELOID, ht_idxrel,
+                                                       Anum_pg_class_reloptions, &isnull);
+       if (!isnull)
+               index->options = untransformRelOptions(datum);
 
        /* If it's a partial index, decompile and append the predicate */
-       if (!heap_attisnull(ht_idx, Anum_pg_index_indpred))
+       datum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                                       Anum_pg_index_indpred, &isnull);
+       if (!isnull)
        {
-               Datum           pred_datum;
-               bool            isnull;
                char       *pred_str;
 
                /* Convert text string to node tree */
-               pred_datum = SysCacheGetAttr(INDEXRELID, ht_idx,
-                                                                        Anum_pg_index_indpred, &isnull);
-               Assert(!isnull);
-               pred_str = DatumGetCString(DirectFunctionCall1(textout,
-                                                                                                          pred_datum));
+               pred_str = DatumGetCString(DirectFunctionCall1(textout, datum));
                index->whereClause = (Node *) stringToNode(pred_str);
+               /* Adjust attribute numbers */
                change_varattnos_of_a_node(index->whereClause, attmap);
        }
 
        /* Clean up */
-       ReleaseSysCache(ht_idx);
        ReleaseSysCache(ht_idxrel);
-       ReleaseSysCache(ht_am);
 
        return index;
 }
@@ -924,11 +922,11 @@ get_opclass(Oid opclass, Oid actual_datatype)
                elog(ERROR, "cache lookup failed for opclass %u", opclass);
        opc_rec = (Form_pg_opclass) GETSTRUCT(ht_opc);
 
-       if (!OidIsValid(actual_datatype) ||
-               GetDefaultOpClass(actual_datatype, opc_rec->opcmethod) != opclass)
+       if (GetDefaultOpClass(actual_datatype, opc_rec->opcmethod) != opclass)
        {
+               /* For simplicity, we always schema-qualify the name */
                char       *nsp_name = get_namespace_name(opc_rec->opcnamespace);
-               char       *opc_name = NameStr(opc_rec->opcname);
+               char       *opc_name = pstrdup(NameStr(opc_rec->opcname));
 
                result = list_make2(makeString(nsp_name), makeString(opc_name));
        }
@@ -940,8 +938,8 @@ get_opclass(Oid opclass, Oid actual_datatype)
 
 /*
  * transformIndexConstraints
- *             Handle UNIQUE and PRIMARY KEY constraints, which create
- *             indexes. We also merge index definitions arising from
+ *             Handle UNIQUE and PRIMARY KEY constraints, which create indexes.
+ *             We also merge in any index definitions arising from
  *             LIKE ... INCLUDING INDEXES.
  */
 static void
@@ -960,7 +958,30 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
        {
                Constraint *constraint = (Constraint *) lfirst(lc);
 
+               Assert(IsA(constraint, Constraint));
+               Assert(constraint->contype == CONSTR_PRIMARY ||
+                          constraint->contype == CONSTR_UNIQUE);
+
                index = transformIndexConstraint(constraint, cxt);
+
+               indexlist = lappend(indexlist, index);
+       }
+
+       /* Add in any indexes defined by LIKE ... INCLUDING INDEXES */
+       foreach(lc, cxt->inh_indexes)
+       {
+               index = (IndexStmt *) lfirst(lc);
+
+               if (index->primary)
+               {
+                       if (cxt->pkey != NULL)
+                               ereport(ERROR,
+                                               (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
+                                                errmsg("multiple primary keys for table \"%s\" are not allowed",
+                                                               cxt->relation->relname)));
+                       cxt->pkey = index;
+               }
+
                indexlist = lappend(indexlist, index);
        }
 
@@ -995,8 +1016,11 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
                {
                        IndexStmt  *priorindex = lfirst(k);
 
-                       if (equal(index->indexParams, priorindex->indexParams))
+                       if (equal(index->indexParams, priorindex->indexParams) &&
+                               equal(index->whereClause, priorindex->whereClause) &&
+                               strcmp(index->accessMethod, priorindex->accessMethod) == 0)
                        {
+                               priorindex->unique |= index->unique;
                                /*
                                 * If the prior index is as yet unnamed, and this one is
                                 * named, then transfer the name to the prior index. This
@@ -1013,27 +1037,13 @@ transformIndexConstraints(ParseState *pstate, CreateStmtContext *cxt)
                if (keep)
                        cxt->alist = lappend(cxt->alist, index);
        }
-
-       /* Copy indexes defined by LIKE ... INCLUDING INDEXES */
-       foreach(lc, cxt->inh_indexes)
-       {
-               index = (IndexStmt *) lfirst(lc);
-
-               if (index->primary)
-               {
-                       if (cxt->pkey)
-                               ereport(ERROR,
-                                               (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
-                                                errmsg("multiple primary keys for table \"%s\" are not allowed",
-                                                               cxt->relation->relname)));
-
-                       cxt->pkey = index;
-               }
-
-               cxt->alist = lappend(cxt->alist, index);
-       }
 }
 
+/*
+ * transformIndexConstraint
+ *             Transform one UNIQUE or PRIMARY KEY constraint for
+ *             transformIndexConstraints.
+ */
 static IndexStmt *
 transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
 {
@@ -1041,13 +1051,10 @@ transformIndexConstraint(Constraint *constraint, CreateStmtContext *cxt)
        ListCell   *keys;
        IndexElem  *iparam;
 
-       Assert(constraint->contype == CONSTR_PRIMARY ||
-                  constraint->contype == CONSTR_UNIQUE);
-
        index = makeNode(IndexStmt);
+
        index->unique = true;
        index->primary = (constraint->contype == CONSTR_PRIMARY);
-
        if (index->primary)
        {
                if (cxt->pkey != NULL)
@@ -1771,7 +1778,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
 
        /*
         * transformIndexConstraints wants cxt.alist to contain only index
-        * statements, so transfer anything we already have into save_alist.
+        * statements, so transfer anything we already have into save_alist
         * immediately.
         */
        save_alist = cxt.alist;
index 9a1e877820dd42e3a0c92bb9332826d8c03ad3bb..bcbfe251b7f07e19a251c5720ebf9af7ebab8304 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.287 2007/11/15 21:14:38 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.288 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -924,7 +924,6 @@ ProcessUtility(Node *parsetree,
                                                        stmt->indexParams,      /* parameters */
                                                        (Expr *) stmt->whereClause,
                                                        stmt->options,
-                                                       stmt->src_options,
                                                        stmt->unique,
                                                        stmt->primary,
                                                        stmt->isconstraint,
index 168da20aa23d0d998774b3eb4752f91ad0108841..8e4d5c90cd3e76f5bc3f306dc01d249033fc9316 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.265 2007/11/15 21:14:39 momjian Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.266 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -133,7 +133,6 @@ static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
                                                        int prettyFlags);
 static char *pg_get_expr_worker(text *expr, Oid relid, char *relname,
                                   int prettyFlags);
-static Oid     get_constraint_index(Oid constraintId);
 static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
                         int prettyFlags);
 static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
@@ -195,6 +194,7 @@ static char *generate_relation_name(Oid relid);
 static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
 static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
 static text *string_to_text(char *str);
+static char *flatten_reloptions(Oid relid);
 
 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
 
@@ -1384,68 +1384,6 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS)
 }
 
 
-/*
- * get_constraint_index
- *             Given the OID of a unique or primary-key constraint, return the
- *             OID of the underlying unique index.
- *
- * Return InvalidOid if the index couldn't be found; this suggests the
- * given OID is bogus, but we leave it to caller to decide what to do.
- */
-static Oid
-get_constraint_index(Oid constraintId)
-{
-       Oid                     indexId = InvalidOid;
-       Relation        depRel;
-       ScanKeyData key[3];
-       SysScanDesc scan;
-       HeapTuple       tup;
-
-       /* Search the dependency table for the dependent index */
-       depRel = heap_open(DependRelationId, AccessShareLock);
-
-       ScanKeyInit(&key[0],
-                               Anum_pg_depend_refclassid,
-                               BTEqualStrategyNumber, F_OIDEQ,
-                               ObjectIdGetDatum(ConstraintRelationId));
-       ScanKeyInit(&key[1],
-                               Anum_pg_depend_refobjid,
-                               BTEqualStrategyNumber, F_OIDEQ,
-                               ObjectIdGetDatum(constraintId));
-       ScanKeyInit(&key[2],
-                               Anum_pg_depend_refobjsubid,
-                               BTEqualStrategyNumber, F_INT4EQ,
-                               Int32GetDatum(0));
-
-       scan = systable_beginscan(depRel, DependReferenceIndexId, true,
-                                                         SnapshotNow, 3, key);
-
-       while (HeapTupleIsValid(tup = systable_getnext(scan)))
-       {
-               Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
-
-               /*
-                * We assume any internal dependency of an index on the constraint
-                * must be what we are looking for.  (The relkind test is just
-                * paranoia; there shouldn't be any such dependencies otherwise.)
-                */
-               if (deprec->classid == RelationRelationId &&
-                       deprec->objsubid == 0 &&
-                       deprec->deptype == DEPENDENCY_INTERNAL &&
-                       get_rel_relkind(deprec->objid) == RELKIND_INDEX)
-               {
-                       indexId = deprec->objid;
-                       break;
-               }
-       }
-
-       systable_endscan(scan);
-       heap_close(depRel, AccessShareLock);
-
-       return indexId;
-}
-
-
 /*
  * deparse_expression                  - General utility for deparsing expressions
  *
@@ -5507,7 +5445,7 @@ string_to_text(char *str)
 /*
  * Generate a C string representing a relation's reloptions, or NULL if none.
  */
-char *
+static char *
 flatten_reloptions(Oid relid)
 {
        char       *result = NULL;
@@ -5543,32 +5481,3 @@ flatten_reloptions(Oid relid)
 
        return result;
 }
-
-/*
- * Generate an Array Datum representing a relation's reloptions using
- * a C string
- */
-Datum
-unflatten_reloptions(char *reloptstring)
-{
-       Datum           result = (Datum) 0;
-
-       if (reloptstring)
-       {
-               Datum           sep,
-                                       relopts;
-
-               /*
-                * We want to use text_to_array(reloptstring, ', ') --- but
-                * DirectFunctionCall2(text_to_array) does not work, because
-                * text_to_array() relies on fcinfo to be valid.  So use
-                * OidFunctionCall2.
-                */
-               sep = DirectFunctionCall1(textin, CStringGetDatum(", "));
-               relopts = DirectFunctionCall1(textin, CStringGetDatum(reloptstring));
-
-               result = OidFunctionCall2(F_TEXT_TO_ARRAY, relopts, sep);
-       }
-
-       return result;
-}
index 1c5aa3526cbc891ff6a9d928eb6dc81a56cd1e6e..f6a7c42c19db17d2ba1af93282587f0bda9682b6 100644 (file)
@@ -11,7 +11,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/reloptions.h,v 1.3 2007/01/05 22:19:51 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/access/reloptions.h,v 1.4 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -23,6 +23,8 @@
 extern Datum transformRelOptions(Datum oldOptions, List *defList,
                                        bool ignoreOids, bool isReset);
 
+extern List *untransformRelOptions(Datum options);
+
 extern void parseRelOptions(Datum options, int numkeywords,
                                const char *const * keywords,
                                char **values, bool validate);
index 1b238d8d8a93ce4bb31bee8d77ad7d8d5d256f14..637a7a93712f7a3a3ed3e3c8a500153d15a64e3f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.31 2007/11/15 21:14:42 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/dependency.h,v 1.32 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -207,6 +207,10 @@ extern bool sequenceIsOwned(Oid seqId, Oid *tableId, int32 *colId);
 
 extern void markSequenceUnowned(Oid seqId);
 
+extern Oid     get_constraint_index(Oid constraintId);
+
+extern Oid     get_index_constraint(Oid indexId);
+
 /* in pg_shdepend.c */
 
 extern void recordSharedDependencyOn(ObjectAddress *depender,
index 99621d3b26e38c3153a484f15970ceafd294ea43..152061239f503ed6b3acdbdaeef0975d669c9609 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.86 2007/11/15 22:25:17 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/defrem.h,v 1.87 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -26,7 +26,6 @@ extern void DefineIndex(RangeVar *heapRelation,
                        List *attributeList,
                        Expr *predicate,
                        List *options,
-                       char *src_options,
                        bool unique,
                        bool primary,
                        bool isconstraint,
index 51d760ef564cdb13afe7befe9eaf23cdd912ec9b..d7055140a5fe20050a76c5ad10f7238da9557561 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.356 2007/11/15 22:25:17 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.357 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1540,7 +1540,6 @@ typedef struct IndexStmt
        char       *tableSpace;         /* tablespace, or NULL to use parent's */
        List       *indexParams;        /* a list of IndexElem */
        List       *options;            /* options from WITH clause */
-       char       *src_options;        /* relopts inherited from source index */
        Node       *whereClause;        /* qualification (partial-index predicate) */
        bool            unique;                 /* is index unique? */
        bool            primary;                /* is index on primary key? */
index cf9e03fcf3d3e1faa68e53e96e7d0dfc01778435..d4fb1262cdf9ec0cb6b2938805e7af09d31e2fcc 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.306 2007/11/15 21:14:45 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.307 2007/12/01 23:44:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -566,8 +566,6 @@ extern List *deparse_context_for_plan(Node *outer_plan, Node *inner_plan,
 extern const char *quote_identifier(const char *ident);
 extern char *quote_qualified_identifier(const char *namespace,
                                                   const char *ident);
-extern char *flatten_reloptions(Oid relid);
-extern Datum unflatten_reloptions(char *reloptstring);
 
 /* tid.c */
 extern Datum tidin(PG_FUNCTION_ARGS);
index 40dfaeda902a0c75e0f51b8e66fc86c2190f5319..f81776fe80463dac11247b8cab439d3415f96030 100644 (file)
@@ -634,6 +634,7 @@ SELECT * FROM inhg; /* Two records with three columns in order x=x, xx=text, y=y
 
 DROP TABLE inhg;
 CREATE TABLE inhg (x text, LIKE inhx INCLUDING INDEXES, y text); /* copies indexes */
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "inhg_pkey" for table "inhg"
 INSERT INTO inhg VALUES (5, 10);
 INSERT INTO inhg VALUES (20, 10); -- should fail
 ERROR:  duplicate key value violates unique constraint "inhg_pkey"
@@ -647,6 +648,7 @@ CREATE UNIQUE INDEX inhz_xx_idx on inhz (xx) WHERE xx <> 'test';
 /* Ok to create multiple unique indexes */
 CREATE TABLE inhg (x text UNIQUE, LIKE inhz INCLUDING INDEXES);
 NOTICE:  CREATE TABLE / UNIQUE will create implicit index "inhg_x_key" for table "inhg"
+NOTICE:  CREATE TABLE / UNIQUE will create implicit index "inhg_yy_key" for table "inhg"
 INSERT INTO inhg (xx, yy, x) VALUES ('test', 5, 10);
 INSERT INTO inhg (xx, yy, x) VALUES ('test', 10, 15);
 INSERT INTO inhg (xx, yy, x) VALUES ('foo', 10, 15); -- should fail