replace_pkey_index.patch
application/octet-stream
Filename: replace_pkey_index.patch
Type: application/octet-stream
Part: 0
Message:
Patch to reindex primary keys
Patch
Same data as JSON:
GET /api/v1/attachments/:id/patch
the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes.
API reference →
Format: unified
| File | + | − |
|---|---|---|
| src/backend/bootstrap/bootparse.y | 2 | 2 |
| src/backend/catalog/index.c | 22 | 1 |
| src/backend/catalog/toasting.c | 1 | 1 |
| src/backend/commands/indexcmds.c | 14 | 8 |
| src/backend/commands/tablecmds.c | 49 | 1 |
| src/backend/parser/parse_utilcmd.c | 187 | 0 |
| src/backend/tcop/utility.c | 1 | 0 |
| src/include/catalog/index.h | 1 | 0 |
| src/include/commands/defrem.h | 1 | 0 |
diff --git a/src/backend/bootstrap/bootparse.y b/src/backend/bootstrap/bootparse.y
index 387d43e..fed79ff 100644
--- a/src/backend/bootstrap/bootparse.y
+++ b/src/backend/bootstrap/bootparse.y
@@ -287,7 +287,7 @@ Boot_DeclareIndexStmt:
$10,
NULL, NIL, NIL,
false, false, false, false, false,
- false, false, true, false, false);
+ false, false, true, false, false, false);
do_end();
}
;
@@ -305,7 +305,7 @@ Boot_DeclareUniqueIndexStmt:
$11,
NULL, NIL, NIL,
true, false, false, false, false,
- false, false, true, false, false);
+ false, false, true, false, false, false);
do_end();
}
;
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 69946fe..0ab523e 100644
--- a/src/backend/catalog/index.c
+++ b/src/backend/catalog/index.c
@@ -512,6 +512,7 @@ UpdateIndexRelation(Oid indexoid,
* allow_system_table_mods: allow table to be a system catalog
* skip_build: true to skip the index_build() step for the moment; caller
* must do it later (typically via reindex_index())
+ * index_exists: the index already exists, we are here just for formalities.
* concurrent: if true, do not lock the table against writers. The index
* will be marked "invalid" and the caller must take additional steps
* to fix it up.
@@ -535,6 +536,7 @@ index_create(Oid heapRelationId,
bool initdeferred,
bool allow_system_table_mods,
bool skip_build,
+ bool index_exists,
bool concurrent)
{
Relation pg_class;
@@ -615,6 +617,7 @@ index_create(Oid heapRelationId,
if (shared_relation && tableSpaceId != GLOBALTABLESPACE_OID)
elog(ERROR, "shared relations must be placed in pg_global tablespace");
+ if (!index_exists)
if (get_relname_relid(indexRelationName, namespaceId))
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_TABLE),
@@ -648,6 +651,17 @@ index_create(Oid heapRelationId,
indexRelationId = GetNewRelFileNode(tableSpaceId, pg_class);
}
+ if (index_exists)
+ {
+ Assert( skip_build && !IsBootstrapProcessingMode() );
+
+ indexRelation = relation_open(indexRelationId, AccessExclusiveLock);
+
+ /* done with pg_class */
+ heap_close(pg_class, RowExclusiveLock);
+ }
+ else
+ {
/*
* create the index relation's relcache entry and physical disk file. (If
* we fail further down, it's the smgr's responsibility to remove the disk
@@ -720,6 +734,7 @@ index_create(Oid heapRelationId,
classObjectId, coloptions, isprimary,
!deferrable,
!concurrent);
+ }
/*
* Register constraint and dependencies for the index.
@@ -833,7 +848,7 @@ index_create(Oid heapRelationId,
true);
}
}
- else
+ else if(!index_exists)
{
bool have_simple_col = false;
@@ -876,6 +891,8 @@ index_create(Oid heapRelationId,
Assert(!initdeferred);
}
+ if (!index_exists)
+ {
/* Store dependency on operator classes */
for (i = 0; i < indexInfo->ii_NumIndexAttrs; i++)
{
@@ -905,6 +922,7 @@ index_create(Oid heapRelationId,
DEPENDENCY_NORMAL,
DEPENDENCY_AUTO);
}
+ }
}
else
{
@@ -945,6 +963,8 @@ index_create(Oid heapRelationId,
}
else if (skip_build)
{
+ if (!index_exists)
+ {
/*
* Caller is responsible for filling the index later on. However,
* we'd better make sure that the heap relation is correctly marked as
@@ -958,6 +978,7 @@ index_create(Oid heapRelationId,
heapRelation->rd_rel->reltuples);
/* Make the above update visible */
CommandCounterIncrement();
+ }
}
else
{
diff --git a/src/backend/catalog/toasting.c b/src/backend/catalog/toasting.c
index 435dfdd..96cbce2 100644
--- a/src/backend/catalog/toasting.c
+++ b/src/backend/catalog/toasting.c
@@ -269,7 +269,7 @@ create_toast_table(Relation rel, Oid toastOid, Oid toastIndexOid, Datum reloptio
rel->rd_rel->reltablespace,
classObjectId, coloptions, (Datum) 0,
true, false, false, false,
- true, false, false);
+ true, false, false, false);
/*
* Store the toast table's OID in the parent relation's pg_class row
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 2da1c86..11ba888 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -69,7 +69,9 @@ static void ComputeIndexAttrs(IndexInfo *indexInfo,
static Oid GetIndexOpClass(List *opclass, Oid attrType,
char *accessMethodName, Oid accessMethodId);
static char *ChooseIndexNameAddition(List *colnames);
-static bool relationHasPrimaryKey(Relation rel);
+
+/* TODO: Unable to figure out which header to declare this in */
+Oid relationHasPrimaryKey(Relation rel);
/*
@@ -122,6 +124,7 @@ DefineIndex(RangeVar *heapRelation,
bool is_alter_table,
bool check_rights,
bool skip_build,
+ bool index_exists,
bool quiet,
bool concurrent)
{
@@ -340,7 +343,7 @@ DefineIndex(RangeVar *heapRelation,
* it's no problem either.
*/
if (is_alter_table &&
- relationHasPrimaryKey(rel))
+ relationHasPrimaryKey(rel) != InvalidOid)
{
ereport(ERROR,
(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
@@ -488,6 +491,7 @@ DefineIndex(RangeVar *heapRelation,
isconstraint, deferrable, initdeferred,
allowSystemTableMods,
skip_build || concurrent,
+ index_exists,
concurrent);
if (!concurrent)
@@ -1541,10 +1545,11 @@ ChooseIndexColumnNames(List *indexElems)
*
* See whether an existing relation has a primary key.
*/
-static bool
+Oid
relationHasPrimaryKey(Relation rel)
{
- bool result = false;
+ Oid indexoid = InvalidOid;
+ bool isprimary = false;
List *indexoidlist;
ListCell *indexoidscan;
@@ -1557,21 +1562,22 @@ relationHasPrimaryKey(Relation rel)
foreach(indexoidscan, indexoidlist)
{
- Oid indexoid = lfirst_oid(indexoidscan);
HeapTuple indexTuple;
+ indexoid = lfirst_oid(indexoidscan);
+
indexTuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexoid));
if (!HeapTupleIsValid(indexTuple)) /* should not happen */
elog(ERROR, "cache lookup failed for index %u", indexoid);
- result = ((Form_pg_index) GETSTRUCT(indexTuple))->indisprimary;
+ isprimary = ((Form_pg_index) GETSTRUCT(indexTuple))->indisprimary;
ReleaseSysCache(indexTuple);
- if (result)
+ if (isprimary)
break;
}
list_free(indexoidlist);
- return result;
+ return isprimary ? indexoid : InvalidOid;
}
/*
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 2383112..68e916f 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -4559,7 +4559,10 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
{
bool check_rights;
bool skip_build;
+ bool index_exists = false;
bool quiet;
+ Oid index_oid = InvalidOid;
+ ListCell *l, *prev = NULL;
Assert(IsA(stmt, IndexStmt));
@@ -4570,11 +4573,55 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
/* suppress notices when rebuilding existing index */
quiet = is_rebuild;
+ /* TODO:
+ Do stmt->options have 'WITH INDEX' option set
+ Get OID from the Value* (represented as string)
+ strspn(opr_name_or_oid, "0123456789") == strlen(opr_name_or_oid))
+ result = DatumGetObjectId(DirectFunctionCall1(oidin,
+ CStringGetDatum(opr_name_or_oid)));
+ replace InvalidOid, below, with this OID
+ set 'primary' to on;
+ set skip_build to on
+ TODO Check with -hackers that this is the right thing to do even when tab->newvals is non-null?
+ */
+
+ foreach(l, stmt->options)
+ {
+ DefElem *def = (DefElem*)lfirst(l);
+
+ if (def->defnamespace == NULL && 0 == strcmp(def->defname, "index"))
+ {
+ if (!(def->defaction == DEFELEM_UNSPEC || def->defaction == DEFELEM_SET))
+ elog(ERROR, "index option for a primary key has a syntax error." );
+ }
+ else
+ {
+ prev = l;
+ continue;
+ }
+
+ Assert(strspn(strVal(def->arg), "0123456789") == strlen(strVal(def->arg)));
+ Assert(stmt->primary);
+
+ index_oid = DatumGetObjectId(DirectFunctionCall1(oidin,
+ CStringGetDatum(strVal(def->arg))));
+
+ /* We override the skip_build set above */
+ skip_build = true;
+
+ index_exists = true;
+
+ break;
+ }
+
+ if (l) /* unecessary check, but good for readability */
+ stmt->options = list_delete_cell(stmt->options, l, prev);
+
/* The IndexStmt has already been through transformIndexStmt */
DefineIndex(stmt->relation, /* relation */
stmt->idxname, /* index name */
- InvalidOid, /* no predefined OID */
+ index_oid, /* no predefined OID */
stmt->accessMethod, /* am name */
stmt->tableSpace,
stmt->indexParams, /* parameters */
@@ -4589,6 +4636,7 @@ ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
true, /* is_alter_table */
check_rights,
skip_build,
+ index_exists,
quiet,
false);
}
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 322e689..ad98928 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1931,6 +1931,7 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
List *newcmds = NIL;
bool skipValidation = true;
AlterTableCmd *newcmd;
+ Oid index_oid = InvalidOid;
/*
* We must not scribble on the passed-in AlterTableStmt, so copy it. (This
@@ -2047,6 +2048,192 @@ transformAlterTableStmt(AlterTableStmt *stmt, const char *queryString)
transformFKConstraints(pstate, &cxt, skipValidation, true);
+ /* TODO: look at cxt->pkey->options and see if it has 'WITH INDEX'; if yes
+ then cook-up an ALTER TABLE DROP CONSTRAINT for table's primary key,
+ and push into newcmds
+
+ Check if cxt->pkey->options has a 'WITH INDEX' element
+ take an exclusive lock on that index
+
+ Does this table have a primary key
+ check if index mentioned in cxt->pkey matches that PKEY definition,
+ Does column list match
+ Do the opclasses match
+ Does index type match (BTree for now)
+ Do they have the same owner
+
+ Append a new command to newcmds to drop the PKey constraint
+ use 'rel' variable to get primary key's OID ( modify and reuse relationHasPrimaryKey() )
+ use relation_open() to get pkey's relation
+ use the returned Relation->rd_rel->relname to build DROP CONSTRAINT command
+ set missingok member of the command so that this would work even if there was already a DROP CONSTRAINT for the PKey.
+ push this command to newcmds
+
+ Chenge the 'WITH INDEX' element, and replace index name in Value* to have decimal representation of index's OID.
+ This will be used by ATExecAddIndex().
+
+ BIG TODO: convert all elog() calls into ereport() and proper errcode()
+ calls and write better messages.
+ */
+
+ if (cxt.pkey)
+ foreach(l, cxt.pkey->options)
+ {
+ DefElem *def = (DefElem*)lfirst(l);
+ Oid pkey_oid;
+ char *index_name;
+ char *oid_string;
+ List *index_name_list;
+ RangeVar *index_rv;
+ Relation index_rel;
+
+ if (def->defnamespace == NULL && 0 == strcmp(def->defname, "index"))
+ {
+ if (!(def->defaction == DEFELEM_UNSPEC || def->defaction == DEFELEM_SET))
+ elog(ERROR, "index option for a primary key has a syntax error." );
+ }
+ else
+ continue;
+
+ /*
+ * If we don't do this, then this will get to DefineIndex(), and it will
+ * throw a fit.
+ */
+ if (index_oid != InvalidOid)
+ elog(ERROR, "only one WITH INDEX option can be specified for a primary key.");
+
+ if (!IsA(def->arg, String))
+ elog(ERROR, "WITH INDEX option for primary key should be a string value");
+
+ index_name = strVal(def->arg);
+
+ index_name_list = stringToQualifiedNameList(index_name);
+
+ /* TODO: Should we assert that this is a non-qualified name? */
+ index_rv = makeRangeVarFromNameList(index_name_list);
+
+ index_rel = relation_openrv(index_rv, AccessExclusiveLock);
+
+ index_oid = RelationGetRelid(index_rel);
+
+ oid_string = DatumGetCString(DirectFunctionCall1(oidout,
+ ObjectIdGetDatum(index_oid)));
+
+ /* replace index name with its Oid::cstring */
+ def->arg = (Node*)makeString(oid_string);
+
+ /* TODO: set index name in the statement, to affect the constraint name */
+ cxt.pkey->idxname = pstrdup(strVal(llast(index_name_list)));
+
+ /* TODO: make sure the index is in the same schema/namespace as the table */
+
+ pkey_oid = relationHasPrimaryKey(rel);
+
+ if (pkey_oid != InvalidOid)
+ {
+ HeapTuple pkey_tuple;
+ HeapTuple index_tuple;
+ Form_pg_index pkey_form;
+ Form_pg_index index_form;
+ AlterTableCmd *at_cmd;
+ int2 i;
+ Relation pkey_rel;
+
+ /*
+ * Check pg_class->relam to see if the index type match. As of now, only
+ * BTree indexes are used to implement primary keys, but it doesn't hurt
+ * to be future proof.
+ */
+ pkey_rel = relation_open(pkey_oid, AccessExclusiveLock);
+
+ if (pkey_rel->rd_rel->relam != index_rel->rd_rel->relam)
+ elog(ERROR, "index type of WITH INDEX argument and that of the primary key are not the same.");
+
+ if (pkey_rel->rd_rel->relowner != index_rel->rd_rel->relowner)
+ elog(ERROR, "owner of WITH INDEX argument and that of the primary key are not same.");
+
+ pkey_tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(pkey_oid));
+ if (!HeapTupleIsValid(pkey_tuple))
+ elog(ERROR, "cache lookup failed for index %u", pkey_oid);
+ pkey_form = (Form_pg_index) GETSTRUCT(pkey_tuple);
+
+ index_tuple = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(index_oid));
+ if (!HeapTupleIsValid(index_tuple))
+ elog(ERROR, "cache lookup failed for index %u", index_oid);
+ index_form = (Form_pg_index) GETSTRUCT(index_tuple);
+
+ if (!index_form->indisvalid)
+ elog(ERROR, "index mentioned in the WITH INDEX clause of primary key is not valid.");
+
+ if (!index_form->indisunique)
+ elog(ERROR, "index mentioned in the WITH INDEX clause of primary key is not a unique index.");
+
+ /* TODO: Should we check for indisready, indcheckxmin ?*/
+
+ if (index_form->indrelid != pkey_form->indrelid)
+ elog(ERROR, "index mentioned in the WITH INDEX clause of primary key is not on the same table.");
+
+ if (index_form->indnatts != pkey_form->indnatts)
+ elog(ERROR, "idefinition of index mentioned in the WITH INDEX clause does not match primary key.");
+
+ /*
+ * Loop over each attribute in the primary key and see if it
+ * matches the attributes of the index we are replacing it with.
+ */
+ for (i = 0; i < pkey_form->indnatts; i++)
+ {
+ /* We do not expect primary keys on expressions */
+ Assert(pkey_form->indkey.values[i] != 0);
+
+ if (pkey_form->indkey.values[i] != index_form->indkey.values[i])
+ elog(ERROR, "idefinition of index mentioned in the WITH INDEX clause does not match primary key.");
+
+ if (pkey_form->indclass.values[i] != index_form->indclass.values[i])
+ elog(ERROR, "operator classes of index mentioned in the WITH INDEX clause does not match those of primary key.");
+ }
+
+ ReleaseSysCache(pkey_tuple);
+ ReleaseSysCache(index_tuple);
+
+ at_cmd = makeNode(AlterTableCmd);
+ at_cmd->subtype = AT_DropConstraint;
+ at_cmd->name = pstrdup(NameStr(pkey_rel->rd_rel->relname));
+ at_cmd->behavior = DROP_CASCADE;;
+ at_cmd->missing_ok = true;
+
+ newcmds = lappend(newcmds, at_cmd);
+
+ /* Release the lock so that it can be dropped from cache. */
+ relation_close(pkey_rel, AccessExclusiveLock);
+ }
+
+ /* NOw update pg_index tuple to mark this index as indisprimary */
+ {
+ Relation pg_index;
+ HeapTuple indexTuple;
+ Form_pg_index indexForm;
+
+ pg_index = heap_open(IndexRelationId, RowExclusiveLock);
+
+ indexTuple = SearchSysCacheCopy1(INDEXRELID,
+ ObjectIdGetDatum(index_oid));
+ if (!HeapTupleIsValid(indexTuple))
+ elog(ERROR, "cache lookup failed for index %u", index_oid);
+ indexForm = (Form_pg_index) GETSTRUCT(indexTuple);
+
+ indexForm->indisprimary = true;
+ simple_heap_update(pg_index, &indexTuple->t_self, indexTuple);
+ CatalogUpdateIndexes(pg_index, indexTuple);
+
+ heap_freetuple(indexTuple);
+ heap_close(pg_index, RowExclusiveLock);
+ }
+
+ relation_close(index_rel, NoLock);
+
+ /* do not break; at the end of the loop. Use this opprtunity to catch multiple 'WITH INDEX' clauses*/
+ }
+
/*
* Push any index-creation commands into the ALTER, so that they can be
* scheduled nicely by tablecmds.c. Note that tablecmds.c assumes that
diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c
index b9be06f..adae7c6 100644
--- a/src/backend/tcop/utility.c
+++ b/src/backend/tcop/utility.c
@@ -890,6 +890,7 @@ standard_ProcessUtility(Node *parsetree,
false, /* is_alter_table */
true, /* check_rights */
false, /* skip_build */
+ false, /* index_exists */
false, /* quiet */
stmt->concurrent); /* concurrent */
}
diff --git a/src/include/catalog/index.h b/src/include/catalog/index.h
index d336576..3b019cb 100644
--- a/src/include/catalog/index.h
+++ b/src/include/catalog/index.h
@@ -44,6 +44,7 @@ extern Oid index_create(Oid heapRelationId,
bool initdeferred,
bool allow_system_table_mods,
bool skip_build,
+ bool index_exists,
bool concurrent);
extern void index_drop(Oid indexId);
diff --git a/src/include/commands/defrem.h b/src/include/commands/defrem.h
index 7edda97..c774710 100644
--- a/src/include/commands/defrem.h
+++ b/src/include/commands/defrem.h
@@ -35,6 +35,7 @@ extern void DefineIndex(RangeVar *heapRelation,
bool is_alter_table,
bool check_rights,
bool skip_build,
+ bool index_exists,
bool quiet,
bool concurrent);
extern void ReindexIndex(RangeVar *indexRelation);