From 7773ba29d73e2e5cdb4fa7fee68c4598b70bf22f Mon Sep 17 00:00:00 2001 From: amit Date: Thu, 28 Sep 2017 13:26:00 +0900 Subject: [PATCH 3/3] Make constraint exclusion work with hash partitions --- src/backend/catalog/partition.c | 104 +++++++++++++++++++++++++++++----------- src/include/catalog/pg_proc.h | 6 ++- src/include/fmgr.h | 1 + 3 files changed, 82 insertions(+), 29 deletions(-) diff --git a/src/backend/catalog/partition.c b/src/backend/catalog/partition.c index a085a7a0be..ba437c13c8 100644 --- a/src/backend/catalog/partition.c +++ b/src/backend/catalog/partition.c @@ -28,6 +28,7 @@ #include "catalog/pg_inherits.h" #include "catalog/pg_inherits_fn.h" #include "catalog/pg_opclass.h" +#include "catalog/pg_operator.h" #include "catalog/pg_partitioned_table.h" #include "catalog/pg_type.h" #include "commands/tablecmds.h" @@ -174,7 +175,8 @@ static void get_partition_dispatch_recurse(Relation rel, Relation parent, static uint64 compute_hash_value(PartitionKey key, Datum *values, bool *isnull); /* SQL-callable function for use in hash partition CHECK constraints */ -PG_FUNCTION_INFO_V1(satisfies_hash_partition); +PG_FUNCTION_INFO_V1(hash_partition_modulus); +PG_FUNCTION_INFO_V1(hash_partition_hash); /* * RelationBuildPartitionDesc @@ -1705,14 +1707,14 @@ make_partition_op_expr(PartitionKey key, int keynum, * This function will return one of the following in the form of an * expression: * - * for p_p1: satisfies_hash_partition(2, 1, hash_fn_1_extended(a, HASH_SEED), - * hash_fn_2_extended(b, HASH_SEED)) - * for p_p2: satisfies_hash_partition(4, 2, hash_fn_1_extended(a, HASH_SEED), - * hash_fn_2_extended(b, HASH_SEED)) - * for p_p3: satisfies_hash_partition(8, 0, hash_fn_1_extended(a, HASH_SEED), - * hash_fn_2_extended(b, HASH_SEED)) - * for p_p4: satisfies_hash_partition(8, 4, hash_fn_1_extended(a, HASH_SEED), - * hash_fn_2_extended(b, HASH_SEED)) + * for p_p1: hash_partition_hash(hash_fn_1_extended(a, HASH_SEED), + * hash_fn_2_extended(b, HASH_SEED)) % 4 = 1 + * for p_p2: hash_partition_hash(hash_fn_1_extended(a, HASH_SEED), + * hash_fn_2_extended(b, HASH_SEED)) % 4 = 2 + * for p_p3: hash_partition_hash(hash_fn_1_extended(a, HASH_SEED), + * hash_fn_2_extended(b, HASH_SEED)) % 8 = 0 + * for p_p4: hash_partition_hash(hash_fn_1_extended(a, HASH_SEED), + * hash_fn_2_extended(b, HASH_SEED)) % 8 = 4 * * where hash_fn_1_extended and hash_fn_2_extended are datatype-specific hash * functions(extended version) for columns a and b respectively and HASH_SEED @@ -1722,11 +1724,13 @@ static List * get_qual_for_hash(Relation parent, PartitionBoundSpec *spec) { PartitionKey key = RelationGetPartitionKey(parent); - FuncExpr *fexpr; + FuncExpr *hash_fexpr, + *mod_fexpr; + Expr *opexpr; Node *modulusConst; Node *remainderConst; Node *hashSeedConst; - List *args; + List *args = NIL; ListCell *partexprs_item; int i; @@ -1758,13 +1762,13 @@ get_qual_for_hash(Relation parent, PartitionBoundSpec *spec) false, FLOAT8PASSBYVAL); - args = list_make2(modulusConst, remainderConst); partexprs_item = list_head(key->partexprs); /* Add an argument for each key column. */ for (i = 0; i < key->partnatts; i++) { Node *keyCol; + FuncExpr *fexpr; /* Left operand */ if (key->partattrs[i] != 0) @@ -1794,14 +1798,26 @@ get_qual_for_hash(Relation parent, PartitionBoundSpec *spec) args = lappend(args, fexpr); } - fexpr = makeFuncExpr(F_SATISFIES_HASH_PARTITION, - BOOLOID, - args, - InvalidOid, - InvalidOid, - COERCE_EXPLICIT_CALL); + hash_fexpr = makeFuncExpr(F_HASH_PARTITION_HASH, + INT8OID, + args, + InvalidOid, + InvalidOid, + COERCE_EXPLICIT_CALL); + mod_fexpr = makeFuncExpr(F_HASH_PARTITION_MODULUS, + INT4OID, + list_make2(hash_fexpr, modulusConst), + InvalidOid, + InvalidOid, + COERCE_EXPLICIT_CALL); + + /* Make an fexpr = true expression */ + opexpr = make_opclause(Int4EqualOperator, BOOLOID, false, + (Expr *) mod_fexpr, + (Expr *) remainderConst, + InvalidOid, InvalidOid); - return list_make1(fexpr); + return list_make1(opexpr); } /* @@ -3273,7 +3289,43 @@ compute_hash_value(PartitionKey key, Datum *values, bool *isnull) } /* - * satisfies_hash_partition + * hash_partition_modulus + * + * This is a SQL-callable function for use in hash partition constraint that + * takes as arguments a 64-bit hash value and modulus and returns the + * remainder obtained by dividing the hash value with modulus. + */ +Datum +hash_partition_modulus(PG_FUNCTION_ARGS) +{ + uint64 arg1 = PG_GETARG_UINT64(0); + int32 arg2 = PG_GETARG_INT32(1); + + if (arg2 == 0) + { + ereport(ERROR, + (errcode(ERRCODE_DIVISION_BY_ZERO), + errmsg("division by zero"))); + /* ensure compiler realizes we mustn't reach the division (gcc bug) */ + PG_RETURN_NULL(); + } + + /* + * Some machines throw a floating-point exception for INT_MIN % -1, which + * is a bit silly since the correct answer is perfectly well-defined, + * namely zero. (It's not clear this ever happens when dealing with + * int16, but we might as well have the test for safety.) + */ + if (arg2 == -1) + PG_RETURN_INT32(0); + + /* No overflow is possible */ + + PG_RETURN_INT32(arg1 % arg2); +} + +/* + * hash_partition_hash * * This is a SQL-callable function for use in hash partition constraints takes * an already computed hash values of each partition key attribute, and combine @@ -3285,11 +3337,9 @@ compute_hash_value(PartitionKey key, Datum *values, bool *isnull) * See get_qual_for_hash() for usage. */ Datum -satisfies_hash_partition(PG_FUNCTION_ARGS) +hash_partition_hash(PG_FUNCTION_ARGS) { - int modulus = PG_GETARG_INT32(0); - int remainder = PG_GETARG_INT32(1); - short nkeys = PG_NARGS() - 2; + short nkeys = PG_NARGS(); int i; Datum hash_array[PARTITION_MAX_KEYS]; bool isnull[PARTITION_MAX_KEYS]; @@ -3301,14 +3351,14 @@ satisfies_hash_partition(PG_FUNCTION_ARGS) * Partition key attribute's hash values start from third argument of * function. */ - isnull[i] = PG_ARGISNULL(i + 2); + isnull[i] = PG_ARGISNULL(i); if (!isnull[i]) - hash_array[i] = PG_GETARG_DATUM(i + 2); + hash_array[i] = PG_GETARG_DATUM(i); } /* Form a single 64-bit hash value */ rowHash = mix_hash_value(nkeys, hash_array, isnull); - PG_RETURN_BOOL(rowHash % modulus == remainder); + PG_RETURN_UINT64(rowHash); } diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 470b1f6482..fa7e9a6f2b 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -5523,8 +5523,10 @@ 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 2276 0 f f f f f f i s 3 0 16 "23 23 1007" _null_ _null_ _null_ _null_ _null_ satisfies_hash_partition _null_ _null_ _null_ )); -DESCR("hash partition CHECK constraint"); +DATA(insert OID = 315 ( hash_partition_modulus PGNSP PGUID 12 1 0 0 0 f f f f t f i s 2 0 23 "20 23" _null_ _null_ _null_ _null_ _null_ hash_partition_modulus _null_ _null_ _null_ )); +DESCR("to use in hash partition CHECK constraint"); +DATA(insert OID = 5028 ( hash_partition_hash PGNSP PGUID 12 1 0 2276 0 f f f f f f i s 1 0 20 "1007" _null_ _null_ _null_ _null_ _null_ hash_partition_hash _null_ _null_ _null_ )); +DESCR("to use in hash partition CHECK constraint"); /* * Symbolic values for provolatile column: these indicate whether the result diff --git a/src/include/fmgr.h b/src/include/fmgr.h index b604a5c162..7535f95382 100644 --- a/src/include/fmgr.h +++ b/src/include/fmgr.h @@ -245,6 +245,7 @@ extern struct varlena *pg_detoast_datum_packed(struct varlena *datum); #define PG_GETARG_FLOAT4(n) DatumGetFloat4(PG_GETARG_DATUM(n)) #define PG_GETARG_FLOAT8(n) DatumGetFloat8(PG_GETARG_DATUM(n)) #define PG_GETARG_INT64(n) DatumGetInt64(PG_GETARG_DATUM(n)) +#define PG_GETARG_UINT64(n) DatumGetUInt64(PG_GETARG_DATUM(n)) /* use this if you want the raw, possibly-toasted input datum: */ #define PG_GETARG_RAW_VARLENA_P(n) ((struct varlena *) PG_GETARG_POINTER(n)) /* use this if you want the input datum de-toasted: */ -- 2.11.0