diff --git a/src/backend/access/index/indexam.c b/src/backend/access/index/indexam.c index cc5ac8b..bec3983 100644 --- a/src/backend/access/index/indexam.c +++ b/src/backend/access/index/indexam.c @@ -154,7 +154,8 @@ index_open(Oid relationId, LOCKMODE lockmode) r = relation_open(relationId, lockmode); - if (r->rd_rel->relkind != RELKIND_INDEX) + if (r->rd_rel->relkind != RELKIND_INDEX && + r->rd_rel->relkind != RELKIND_LOCAL_INDEX) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not an index", diff --git a/src/backend/catalog/dependency.c b/src/backend/catalog/dependency.c index fc088b2..26a10e9 100644 --- a/src/backend/catalog/dependency.c +++ b/src/backend/catalog/dependency.c @@ -1107,7 +1107,8 @@ doDeletion(const ObjectAddress *object, int flags) { char relKind = get_rel_relkind(object->objectId); - if (relKind == RELKIND_INDEX) + if (relKind == RELKIND_INDEX || + relKind == RELKIND_LOCAL_INDEX) { bool concurrent = ((flags & PERFORM_DELETION_CONCURRENTLY) != 0); diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 36917c8..91ac740 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -293,6 +293,7 @@ heap_create(const char *relname, case RELKIND_COMPOSITE_TYPE: case RELKIND_FOREIGN_TABLE: case RELKIND_PARTITIONED_TABLE: + case RELKIND_LOCAL_INDEX: create_storage = false; /* diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 7924c30..36452a9 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -41,6 +41,7 @@ #include "catalog/pg_collation.h" #include "catalog/pg_constraint.h" #include "catalog/pg_constraint_fn.h" +#include "catalog/pg_depend.h" #include "catalog/pg_operator.h" #include "catalog/pg_opclass.h" #include "catalog/pg_tablespace.h" @@ -726,6 +727,7 @@ index_create(Relation heapRelation, Oid namespaceId; int i; char relpersistence; + char index_relkind; is_exclusion = (indexInfo->ii_ExclusionOps != NULL); @@ -843,6 +845,10 @@ index_create(Relation heapRelation, } } + index_relkind = + (heapRelation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) ? + RELKIND_INDEX : RELKIND_LOCAL_INDEX; + /* * 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 @@ -854,7 +860,7 @@ index_create(Relation heapRelation, indexRelationId, relFileNode, indexTupDesc, - RELKIND_INDEX, + index_relkind, relpersistence, shared_relation, mapped_relation, @@ -1548,10 +1554,14 @@ index_drop(Oid indexId, bool concurrent) TransferPredicateLocksToHeapRelation(userIndexRelation); } - /* - * Schedule physical removal of the files - */ - RelationDropStorage(userIndexRelation); + if (userIndexRelation->rd_rel->relkind != RELKIND_LOCAL_INDEX) + { + + /* + * Schedule physical removal of the files + */ + RelationDropStorage(userIndexRelation); + } /* * Close and flush the index's relcache entry, to ensure relcache doesn't @@ -3300,6 +3310,109 @@ IndexGetRelation(Oid indexId, bool missing_ok) } /* + * Find all leaf indexes included into local index with 'indexId' oid and lock + * all dependent indexes and respective relations. + * + * Search is performed in pg_depend table since all indexes belonging to child + * tables depends on index from parent table. + * + * indexId: the oid of local index whose leaf indexes need to find + * result: list of result leaf indexes + * depRel: already opened pg_depend relation + * indexLockmode: lockmode for indexes' locks + * heapLockmode: lockmode for relations' locks + */ +static void +findDepedentLeafIndexes(Oid indexId, List **result, Relation depRel, + LOCKMODE indexLockmode, LOCKMODE heapLockmode) +{ + ScanKeyData key[3]; + int nkeys; + SysScanDesc scan; + HeapTuple tup; + List *localSubIndexIds = NIL; + ListCell *lc; + + ScanKeyInit(&key[0], + Anum_pg_depend_refclassid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(RelationRelationId)); + ScanKeyInit(&key[1], + Anum_pg_depend_refobjid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(indexId)); + nkeys = 2; + + scan = systable_beginscan(depRel, DependReferenceIndexId, true, + NULL, nkeys, key); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + Form_pg_depend foundDep = (Form_pg_depend) GETSTRUCT(tup); + Relation index; + + if (foundDep->classid != RelationRelationId) + continue; + + /* Open and lock child index */ + index = relation_open(foundDep->objid, indexLockmode); + + /* Lock relation */ + LockRelationOid(IndexGetRelation(index->rd_id, false), heapLockmode); + + if (index->rd_rel->relkind == RELKIND_INDEX) + *result = lappend_oid(*result, index->rd_id); + else if (index->rd_rel->relkind == RELKIND_LOCAL_INDEX) + localSubIndexIds = lappend_oid(localSubIndexIds, index->rd_id); + + relation_close(index, NoLock); + } + + systable_endscan(scan); + + /* Iterate thorugh local subindexes to extract their leaf indexes */ + foreach(lc, localSubIndexIds) + { + findDepedentLeafIndexes(lfirst_oid(lc), result, depRel, indexLockmode, + heapLockmode); + } +} + +/* + * Reindex all real indexes included into local index with 'parent_index_id' oid + */ +static void +reindex_local_index(Oid parent_index_id, bool skip_constraint_checks, + char persistence, int options) +{ + List *leaf_indexes = NIL; + ListCell *lc; + Relation deprel; + + /* + * We open pg_depend just once and passing the Relation pointer down to all + * the recursive searching of leaf indexes steps. + */ + deprel = heap_open(DependRelationId, AccessShareLock); + + /* + * Extract all leaf indexes, and lock all indexes belonging with parent + * local index using AccessExclusive lock and corresponding relations using + * Share lock + */ + findDepedentLeafIndexes(parent_index_id, &leaf_indexes, deprel, + AccessExclusiveLock, ShareLock); + + foreach(lc, leaf_indexes) + { + reindex_index(lfirst_oid(lc), skip_constraint_checks, persistence, + options); + } + + heap_close(deprel, AccessShareLock); +} + +/* * reindex_index - This routine is used to recreate a single index */ void @@ -3338,6 +3451,19 @@ reindex_index(Oid indexId, bool skip_constraint_checks, char persistence, errmsg("cannot reindex temporary tables of other sessions"))); /* + * Reindex local index belonging to partitioned table + */ + if (heapRelation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + index_close(iRel, NoLock); + heap_close(heapRelation, NoLock); + + reindex_local_index(indexId, skipped_constraint, persistence, options); + + return; + } + + /* * Also check for active uses of the index in the current transaction; we * don't want to reindex underneath an open indexscan. */ diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c index 9618032..8bbe3d0 100644 --- a/src/backend/commands/indexcmds.c +++ b/src/backend/commands/indexcmds.c @@ -23,7 +23,9 @@ #include "catalog/catalog.h" #include "catalog/index.h" #include "catalog/indexing.h" +#include "catalog/partition.h" #include "catalog/pg_am.h" +#include "catalog/pg_inherits_fn.h" #include "catalog/pg_opclass.h" #include "catalog/pg_opfamily.h" #include "catalog/pg_tablespace.h" @@ -283,6 +285,30 @@ CheckIndexCompatible(Oid oldId, return ret; } +#define PUSH_REL_PARTITION_OIDS(rel, part_oids, rel_index_oid, \ + parent_index_oids) \ + do\ + {\ + if (RelationGetPartitionDesc((rel)))\ + {\ + int i;\ + for (i = 0; i < (rel)->rd_partdesc->nparts; ++i)\ + {\ + (part_oids) = lcons_oid((rel)->rd_partdesc->oids[i],\ + (part_oids));\ + (parent_index_oids) = lcons_oid((rel_index_oid),\ + (parent_index_oids));\ + }\ + }\ + } while(0) + +#define POP_REL_PARTITION_OIDS(part_oids, parent_index_oids) \ + do\ + {\ + (part_oids) = list_delete_first((part_oids));\ + (parent_index_oids) = list_delete_first((parent_index_oids));\ + } while(0) + /* * DefineIndex * Creates a new index. @@ -372,7 +398,8 @@ DefineIndex(Oid relationId, namespaceId = RelationGetNamespace(rel); if (rel->rd_rel->relkind != RELKIND_RELATION && - rel->rd_rel->relkind != RELKIND_MATVIEW) + rel->rd_rel->relkind != RELKIND_MATVIEW && + rel->rd_rel->relkind != RELKIND_PARTITIONED_TABLE) { if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) @@ -384,11 +411,6 @@ DefineIndex(Oid relationId, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("cannot create index on foreign table \"%s\"", RelationGetRelationName(rel)))); - else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) - ereport(ERROR, - (errcode(ERRCODE_WRONG_OBJECT_TYPE), - errmsg("cannot create index on partitioned table \"%s\"", - RelationGetRelationName(rel)))); else ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -396,6 +418,12 @@ DefineIndex(Oid relationId, RelationGetRelationName(rel)))); } + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE && stmt->concurrent) + ereport(ERROR, + (errcode(ERRCODE_WRONG_OBJECT_TYPE), + errmsg("cannot create local index on partitioned table \"%s\" concurrently", + RelationGetRelationName(rel)))); + /* * Don't try to CREATE INDEX on temp tables of other backends. */ @@ -660,7 +688,8 @@ DefineIndex(Oid relationId, coloptions, reloptions, stmt->primary, stmt->isconstraint, stmt->deferrable, stmt->initdeferred, allowSystemTableMods, - skip_build || stmt->concurrent, + skip_build || stmt->concurrent || + rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE, stmt->concurrent, !check_rights, stmt->if_not_exists); @@ -677,8 +706,89 @@ DefineIndex(Oid relationId, CreateComments(indexRelationId, RelationRelationId, 0, stmt->idxcomment); + /* + * Create local index on partitioned table that comes down to creating of + * indexes on child relations using depth-first traversal + */ + if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) + { + + List *part_oids = NIL, /* stack for concerned partion oids */ + *parent_index_oids = NIL; /* stack for corresponding parent + index oids */ + + Assert(!stmt->concurrent); + + /* + * Initially push child oids of current relation and related + * parent index oids + */ + PUSH_REL_PARTITION_OIDS(rel, part_oids, indexRelationId, + parent_index_oids); + + while (list_length(part_oids) > 0) + { + Relation childrel; + Oid parent_index_oid, + child_index_oid; + ObjectAddress index_address, + parent_index_address; + char *child_index_name; + + /* Extract top child relation and related parent index oid from stacks */ + childrel = relation_open(linitial_oid(part_oids), lockmode); + parent_index_oid = linitial_oid(parent_index_oids); + + /* Choose name for child index */ + child_index_name = + ChooseIndexName(RelationGetRelationName(childrel), + namespaceId, indexColNames, + stmt->excludeOpNames, stmt->primary, + stmt->isconstraint); + + /* Create index for child node */ + child_index_oid = + index_create(childrel, child_index_name, InvalidOid, + InvalidOid, indexInfo, indexColNames, + accessMethodId, tablespaceId, + collationObjectId, classObjectId, + coloptions, reloptions, stmt->primary, + stmt->isconstraint, stmt->deferrable, + stmt->initdeferred, allowSystemTableMods, + skip_build || childrel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE, + stmt->concurrent, !check_rights, + stmt->if_not_exists); + + /* Pop current reloid and related parent index oid from stacks */ + POP_REL_PARTITION_OIDS(part_oids, parent_index_oids); + + /* + * Push new childs of current child relation and + * related parent indexes to stacks + */ + PUSH_REL_PARTITION_OIDS(childrel, part_oids, child_index_oid, + parent_index_oids); + + /* Release relcache entry from childrel */ + relation_close(childrel, NoLock); + + /* + * Add entry to pg_depend to specify dependancy child index + * from parent one + */ + ObjectAddressSet(index_address, RelationRelationId, + child_index_oid); + ObjectAddressSet(parent_index_address, RelationRelationId, + parent_index_oid); + recordDependencyOn(&index_address, &parent_index_address, + DEPENDENCY_NORMAL); + } + } + + if (!stmt->concurrent) { + /* Close the heap and we're done, in the non-concurrent case */ heap_close(rel, NoLock); return address; @@ -1800,7 +1910,7 @@ RangeVarCallbackForReindexIndex(const RangeVar *relation, relkind = get_rel_relkind(relId); if (!relkind) return; - if (relkind != RELKIND_INDEX) + if (relkind != RELKIND_INDEX && relkind != RELKIND_LOCAL_INDEX) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not an index", relation->relname))); diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 3b28e8c..da92211 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -1113,9 +1113,13 @@ RangeVarCallbackForDropRelation(const RangeVar *rel, Oid relOid, Oid oldRelOid, * It chooses RELKIND_RELATION for both regular and partitioned tables. * That means we must be careful before giving the wrong type error when * the relation is RELKIND_PARTITIONED_TABLE. + * + * Similar statements hold for RELKIND_LOCAL_INDEX and RELKIND_INDEX. */ if (classform->relkind == RELKIND_PARTITIONED_TABLE) expected_relkind = RELKIND_RELATION; + else if (classform->relkind == RELKIND_LOCAL_INDEX) + expected_relkind = RELKIND_INDEX; else expected_relkind = classform->relkind; diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h index d1d493e..21af6fa 100644 --- a/src/include/catalog/pg_class.h +++ b/src/include/catalog/pg_class.h @@ -159,6 +159,8 @@ DESCR(""); #define RELKIND_RELATION 'r' /* ordinary table */ #define RELKIND_INDEX 'i' /* secondary index */ +#define RELKIND_LOCAL_INDEX 'l' /* local index for + partitioned table */ #define RELKIND_SEQUENCE 'S' /* sequence object */ #define RELKIND_TOASTVALUE 't' /* for out-of-line values */ #define RELKIND_VIEW 'v' /* view */