On 10.04.2017 14:20, Robert Haas wrote:
On Tue, Apr 4, 2017 at 12:10 PM, Maksim Milyutin
<m.milyu...@postgrespro.ru> wrote:
1. I have added a new relkind for local indexes named RELKIND_LOCAL_INDEX
(literal 'l').
Seems like it should maybe be RELKIND_PARTITIONED_INDEX. There's
nothing particularly "local" about it. I suppose what you're going
for is that it's not global, but in a way it *is* global to the
partitioning hierarchy. That's the point. It's just that it's
partitioned.
Ok, thanks for the note.
But I want to discuss the relevancy of introduction of a new relkind for
partitioned index. I could to change the control flow in partitioned
index creation (specify conditional statement in the 'index_create'
routine in attached patch) and not enter to the 'heap_create' routine.
This case releases us from integrating new relkind into different places
of Postgres code. But we have to copy-paste some specific code from
'heap_create' function, e.g., definition of relfilenode and tablespaceid
for the new index and perhaps something more when 'heap_create' routine
will be extended.
What do you think about this way?
--
Maksim Milyutin
Postgres Professional: http://www.postgrespro.com
Russian Postgres Company
diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c
index 2328b92..9c15bc9 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"
@@ -849,17 +850,33 @@ index_create(Relation heapRelation,
* we fail further down, it's the smgr's responsibility to remove the disk
* file again.)
*/
- indexRelation = heap_create(indexRelationName,
- namespaceId,
- tableSpaceId,
- indexRelationId,
- relFileNode,
- indexTupDesc,
- RELKIND_INDEX,
- relpersistence,
- shared_relation,
- mapped_relation,
- allow_system_table_mods);
+ if (heapRelation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
+ indexRelation = heap_create(indexRelationName,
+ namespaceId,
+ tableSpaceId,
+ indexRelationId,
+ relFileNode,
+ indexTupDesc,
+ RELKIND_INDEX,
+ relpersistence,
+ shared_relation,
+ mapped_relation,
+ allow_system_table_mods);
+ else
+ indexRelation =
+ RelationBuildLocalRelation(indexRelationName,
+ namespaceId,
+ indexTupDesc,
+ indexRelationId,
+ (OidIsValid(relFileNode)) ?
+ relFileNode : indexRelationId,
+ (tableSpaceId == MyDatabaseTableSpace) ?
+ InvalidOid : tableSpaceId,
+ shared_relation,
+ mapped_relation,
+ relpersistence,
+ RELKIND_INDEX);
+
Assert(indexRelationId == RelationGetRelid(indexRelation));
@@ -1549,10 +1566,14 @@ index_drop(Oid indexId, bool concurrent)
TransferPredicateLocksToHeapRelation(userIndexRelation);
}
- /*
- * Schedule physical removal of the files
- */
- RelationDropStorage(userIndexRelation);
+ if (userHeapRelation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
+ {
+
+ /*
+ * Schedule physical removal of the files
+ */
+ RelationDropStorage(userIndexRelation);
+ }
/*
* Close and flush the index's relcache entry, to ensure relcache doesn't
@@ -3294,6 +3315,111 @@ 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,
+ heap;
+
+ if (foundDep->classid != RelationRelationId)
+ continue;
+
+ /* Open and lock child index */
+ index = relation_open(foundDep->objid, indexLockmode);
+
+ /* Open and lock relation */
+ heap = relation_open(IndexGetRelation(index->rd_id, false), heapLockmode);
+
+ if (index->rd_rel->relkind == RELKIND_INDEX)
+ *result = lappend_oid(*result, index->rd_id);
+ else if (heap->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
+ localSubIndexIds = lappend_oid(localSubIndexIds, index->rd_id);
+
+ relation_close(heap, NoLock);
+ 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
@@ -3332,6 +3458,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 4861799..af96bdb 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;
--
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers