From 479b317df0d6096670490eda0a0a13c7810dc3c6 Mon Sep 17 00:00:00 2001
From: Amul Sul <sulamul@gmail.com>
Date: Wed, 1 Nov 2017 11:44:11 +0530
Subject: [PATCH 5/5] satisfies_hash_partition signature change WIP.

In this satisfies_hash_partition accepts parent relation id,
modulus, remainder and partition key value.

For the batch insert operation we'll open parent relation
and get the extended hash function info from the partition
key and stored in fn_extra.

TODO:
 1. Do we need to hold a lock on parent until commit?
 2. What is input type if different from the partition key?
 3. What is parent relation is change when we are using store function
 info?
---
 src/backend/catalog/partition.c            | 106 ++++++++++++++++++++---------
 src/include/catalog/pg_proc.h              |   2 +-
 src/test/regress/expected/create_table.out |   8 ---
 src/test/regress/sql/create_table.sql      |   1 -
 4 files changed, 73 insertions(+), 44 deletions(-)

diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c
index a900ede3cb..b8e800db7e 100644
--- a/src/backend/catalog/partition.c
+++ b/src/backend/catalog/partition.c
@@ -1811,17 +1811,22 @@ get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
 {
 	PartitionKey key = RelationGetPartitionKey(parent);
 	FuncExpr   *fexpr;
+	Node	   *relidConst;
 	Node	   *modulusConst;
 	Node	   *remainderConst;
-	Node	   *hashSeedConst;
 	List	   *args;
 	ListCell   *partexprs_item;
 	int			i;
 
-	/* Default hash partition is not supported */
-	Assert(!spec->is_default);
-
 	/* Fixed arguments. */
+	relidConst = (Node *) makeConst(OIDOID,
+									-1,
+									InvalidOid,
+									sizeof(Oid),
+									ObjectIdGetDatum(RelationGetRelid(parent)),
+									false,
+									true);
+
 	modulusConst = (Node *) makeConst(INT4OID,
 									  -1,
 									  InvalidOid,
@@ -1838,15 +1843,7 @@ get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
 										false,
 										true);
 
-	hashSeedConst = (Node *) makeConst(INT8OID,
-									   -1,
-									   InvalidOid,
-									   sizeof(uint64),
-									   UInt64GetDatum(HASH_PARTITION_SEED),
-									   false,
-									   FLOAT8PASSBYVAL);
-
-	args = list_make2(modulusConst, remainderConst);
+	args = list_make3(relidConst, modulusConst, remainderConst);
 	partexprs_item = list_head(key->partexprs);
 
 	/* Add an argument for each key column. */
@@ -1856,30 +1853,21 @@ get_qual_for_hash(Relation parent, PartitionBoundSpec *spec)
 
 		/* Left operand */
 		if (key->partattrs[i] != 0)
+		{
 			keyCol = (Node *) makeVar(1,
 									  key->partattrs[i],
 									  key->parttypid[i],
 									  key->parttypmod[i],
 									  key->parttypcoll[i],
 									  0);
+		}
 		else
 		{
-			if (partexprs_item == NULL)
-				elog(ERROR, "wrong number of partition key expressions");
-
 			keyCol = (Node *) copyObject(lfirst(partexprs_item));
 			partexprs_item = lnext(partexprs_item);
 		}
 
-		/* Form hash_fn(keyCol) expression */
-		fexpr = makeFuncExpr(key->partsupfunc[i].fn_oid,
-							 get_func_rettype(key->partsupfunc[i].fn_oid),
-							 list_make2(keyCol, hashSeedConst),
-							 InvalidOid,
-							 InvalidOid,
-							 COERCE_EXPLICIT_CALL);
-
-		args = lappend(args, fexpr);
+		args = lappend(args, keyCol);
 	}
 
 	fexpr = makeFuncExpr(F_SATISFIES_HASH_PARTITION,
@@ -3405,20 +3393,70 @@ compute_hash_value(PartitionKey key, Datum *values, bool *isnull)
 Datum
 satisfies_hash_partition(PG_FUNCTION_ARGS)
 {
-	int			modulus = PG_GETARG_INT32(0);
-	int			remainder = PG_GETARG_INT32(1);
-	short		nkeys = PG_NARGS() - 2;
+	typedef struct ColumnsHashData
+	{
+		Oid			relid;
+		int16		nkeys;
+		FmgrInfo   partsupfunc[PARTITION_MAX_KEYS];
+	}			ColumnsHashData;
+	Oid			parentId = PG_GETARG_OID(0);
+	int			modulus = PG_GETARG_INT32(1);
+	int			remainder = PG_GETARG_INT32(2);
+	short		nkeys = PG_NARGS() - 3;
 	int			i;
+	Datum		seed = UInt64GetDatum(HASH_PARTITION_SEED);
+	ColumnsHashData *my_extra;
 	uint64		rowHash = 0;
 
+	/*
+	 * Cache hash function information.
+	 */
+	my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
+	if (my_extra == NULL || my_extra->nkeys != nkeys ||
+		my_extra->relid != parentId)
+	{
+		Relation	parent;
+		PartitionKey key;
+
+		fcinfo->flinfo->fn_extra =
+			MemoryContextAllocZero(fcinfo->flinfo->fn_mcxt,
+								   offsetof(ColumnsHashData, partsupfunc) +
+								   sizeof(FmgrInfo) * nkeys);
+		my_extra = (ColumnsHashData *) fcinfo->flinfo->fn_extra;
+		my_extra->nkeys = nkeys;
+		my_extra->relid = parentId;
+
+		/* Open parent relation and fetch partition keyinfo */
+		parent = heap_open(parentId, AccessShareLock);
+		key = RelationGetPartitionKey(parent);
+
+		Assert(key->partnatts == nkeys);
+		memcpy(my_extra->partsupfunc, key->partsupfunc, nkeys * sizeof(FmgrInfo));
+
+		/* TODO: Should we hold lock until commit? */
+		heap_close(parent, AccessShareLock);
+	}
+
+	/* Get TypeCacheEntry for each partition column. */
 	for (i = 0; i < nkeys; i++)
+	{
+		/* keys start from fourth argument of function. */
+		int argno = i + 3;
 
-		/*
-		 * Partition key attribute's hash values start from third argument of
-		 * function.
-		 */
-		if (!PG_ARGISNULL(i + 2))
-			rowHash = hash_combine64(rowHash, PG_GETARG_UINT64(i + 2));
+		if (!PG_ARGISNULL(argno))
+		{
+			Datum		hash;
+
+			Assert(OidIsValid(my_extra->partsupfunc[i].fn_oid));
+
+			hash = FunctionCall2(&my_extra->partsupfunc[i],
+								 PG_GETARG_DATUM(argno),
+								 seed);
+
+			/* Form a single 64-bit hash value */
+			rowHash = hash_combine64(rowHash, DatumGetUInt64(hash));
+		}
+	}
 
 	PG_RETURN_BOOL(rowHash % modulus == remainder);
 }
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index 87acdc0ab3..be9eede93f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -5523,7 +5523,7 @@ DATA(insert OID = 3354 (  pg_ls_waldir				 PGNSP PGUID 12 10 20 0 0 f f f f t t
 DESCR("list of files in the WAL directory");
 
 /* hash partitioning constraint function */
-DATA(insert OID = 5028 ( satisfies_hash_partition PGNSP PGUID 12 1 0 20 0 f f f f f f i s 3 0 16 "23 23 1016" _null_ _null_ _null_ _null_ _null_ satisfies_hash_partition _null_ _null_ _null_ ));
+DATA(insert OID = 5028 ( satisfies_hash_partition PGNSP PGUID 12 1 0 2276 0 f f f f f f i s 4 0 16 "26 23 23 2276" _null_ _null_ _null_ _null_ _null_ satisfies_hash_partition _null_ _null_ _null_ ));
 DESCR("hash partition CHECK constraint");
 
 /*
diff --git a/src/test/regress/expected/create_table.out b/src/test/regress/expected/create_table.out
index 5019d1caaf..87aac1ae98 100644
--- a/src/test/regress/expected/create_table.out
+++ b/src/test/regress/expected/create_table.out
@@ -749,14 +749,6 @@ Check constraints:
     "check_a" CHECK (length(a) > 0)
 Partitions: part_c_1_10 FOR VALUES FROM (1) TO (10)
 
-\d+ hpart_1
-                                  Table "public.hpart_1"
- Column |  Type   | Collation | Nullable | Default | Storage | Stats target | Description 
---------+---------+-----------+----------+---------+---------+--------------+-------------
- a      | integer |           |          |         | plain   |              | 
-Partition of: hash_parted FOR VALUES WITH (modulus 10, remainder 0)
-Partition constraint: satisfies_hash_partition(10, 0, hashint4extended(a, '8816678312871386365'::bigint))
-
 -- a level-2 partition's constraint will include the parent's expressions
 \d+ part_c_1_10
                                 Table "public.part_c_1_10"
diff --git a/src/test/regress/sql/create_table.sql b/src/test/regress/sql/create_table.sql
index b08b33d515..901d66c63b 100644
--- a/src/test/regress/sql/create_table.sql
+++ b/src/test/regress/sql/create_table.sql
@@ -666,7 +666,6 @@ CREATE TABLE part_c_1_10 PARTITION OF part_c FOR VALUES FROM (1) TO (10);
 
 -- Both partition bound and partition key in describe output
 \d+ part_c
-\d+ hpart_1
 
 -- a level-2 partition's constraint will include the parent's expressions
 \d+ part_c_1_10
-- 
2.14.1

