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

Reply via email to