4040#include "optimizer/planmain.h"
4141#include "optimizer/prep.h"
4242#include "optimizer/var.h"
43+ #include "parser/parse_coerce.h"
4344#include "rewrite/rewriteManip.h"
4445#include "storage/lmgr.h"
4546#include "utils/array.h"
@@ -3085,9 +3086,11 @@ compute_hash_value(PartitionKey key, Datum *values, bool *isnull)
30853086/*
30863087 * satisfies_hash_partition
30873088 *
3088- * This is a SQL-callable function for use in hash partition constraints takes
3089- * an already computed hash values of each partition key attribute, and combine
3090- * them into a single hash value by calling hash_combine64.
3089+ * This is an SQL-callable function for use in hash partition constraints.
3090+ * The first three arguments are the parent table OID, modulus, and remainder.
3091+ * The remaining arguments are the value of the partitioning columns (or
3092+ * expressions); these are hashed and the results are combined into a single
3093+ * hash value by calling hash_combine64.
30913094 *
30923095 * Returns true if remainder produced when this computed single hash value is
30933096 * divided by the given modulus is equal to given remainder, otherwise false.
@@ -3100,60 +3103,160 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
31003103 typedef struct ColumnsHashData
31013104 {
31023105 Oid relid ;
3103- int16 nkeys ;
3106+ int nkeys ;
3107+ Oid variadic_type ;
3108+ int16 variadic_typlen ;
3109+ bool variadic_typbyval ;
3110+ char variadic_typalign ;
31043111 FmgrInfo partsupfunc [PARTITION_MAX_KEYS ];
31053112 } ColumnsHashData ;
3106- Oid parentId = PG_GETARG_OID (0 );
3107- int modulus = PG_GETARG_INT32 (1 );
3108- int remainder = PG_GETARG_INT32 (2 );
3109- short nkeys = PG_NARGS () - 3 ;
3110- int i ;
3113+ Oid parentId ;
3114+ int modulus ;
3115+ int remainder ;
31113116 Datum seed = UInt64GetDatum (HASH_PARTITION_SEED );
31123117 ColumnsHashData * my_extra ;
31133118 uint64 rowHash = 0 ;
31143119
3120+ /* Return null if the parent OID, modulus, or remainder is NULL. */
3121+ if (PG_ARGISNULL (0 ) || PG_ARGISNULL (1 ) || PG_ARGISNULL (2 ))
3122+ PG_RETURN_NULL ();
3123+ parentId = PG_GETARG_OID (0 );
3124+ modulus = PG_GETARG_INT32 (1 );
3125+ remainder = PG_GETARG_INT32 (2 );
3126+
3127+ /* Sanity check modulus and remainder. */
3128+ if (modulus <= 0 )
3129+ ereport (ERROR ,
3130+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
3131+ errmsg ("modulus for hash partition must be a positive integer" )));
3132+ if (remainder < 0 )
3133+ ereport (ERROR ,
3134+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
3135+ errmsg ("remainder for hash partition must be a non-negative integer" )));
3136+ if (remainder >= modulus )
3137+ ereport (ERROR ,
3138+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
3139+ errmsg ("remainder for hash partition must be less than modulus" )));
3140+
31153141 /*
31163142 * Cache hash function information.
31173143 */
31183144 my_extra = (ColumnsHashData * ) fcinfo -> flinfo -> fn_extra ;
3119- if (my_extra == NULL || my_extra -> nkeys != nkeys ||
3120- my_extra -> relid != parentId )
3145+ if (my_extra == NULL || my_extra -> relid != parentId )
31213146 {
31223147 Relation parent ;
31233148 PartitionKey key ;
3124- int j ;
3125-
3126- fcinfo -> flinfo -> fn_extra =
3127- MemoryContextAllocZero (fcinfo -> flinfo -> fn_mcxt ,
3128- offsetof(ColumnsHashData , partsupfunc ) +
3129- sizeof (FmgrInfo ) * nkeys );
3130- my_extra = (ColumnsHashData * ) fcinfo -> flinfo -> fn_extra ;
3131- my_extra -> nkeys = nkeys ;
3132- my_extra -> relid = parentId ;
3149+ int j ;
31333150
31343151 /* Open parent relation and fetch partition keyinfo */
3135- parent = heap_open (parentId , AccessShareLock );
3152+ parent = try_relation_open (parentId , AccessShareLock );
3153+ if (parent == NULL )
3154+ PG_RETURN_NULL ();
31363155 key = RelationGetPartitionKey (parent );
31373156
3138- Assert (key -> partnatts == nkeys );
3139- for (j = 0 ; j < nkeys ; ++ j )
3140- fmgr_info_copy (& my_extra -> partsupfunc [j ],
3141- key -> partsupfunc ,
3157+ /* Reject parent table that is not hash-partitioned. */
3158+ if (parent -> rd_rel -> relkind != RELKIND_PARTITIONED_TABLE ||
3159+ key -> strategy != PARTITION_STRATEGY_HASH )
3160+ ereport (ERROR ,
3161+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
3162+ errmsg ("\"%s\" is not a hash partitioned table" ,
3163+ get_rel_name (parentId ))));
3164+
3165+ if (!get_fn_expr_variadic (fcinfo -> flinfo ))
3166+ {
3167+ int nargs = PG_NARGS () - 3 ;
3168+
3169+ /* complain if wrong number of column values */
3170+ if (key -> partnatts != nargs )
3171+ ereport (ERROR ,
3172+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
3173+ errmsg ("number of partitioning columns (%d) does not match number of partition keys provided (%d)" ,
3174+ key -> partnatts , nargs )));
3175+
3176+ /* allocate space for our cache */
3177+ fcinfo -> flinfo -> fn_extra =
3178+ MemoryContextAllocZero (fcinfo -> flinfo -> fn_mcxt ,
3179+ offsetof(ColumnsHashData , partsupfunc ) +
3180+ sizeof (FmgrInfo ) * nargs );
3181+ my_extra = (ColumnsHashData * ) fcinfo -> flinfo -> fn_extra ;
3182+ my_extra -> relid = parentId ;
3183+ my_extra -> nkeys = key -> partnatts ;
3184+
3185+ /* check argument types and save fmgr_infos */
3186+ for (j = 0 ; j < key -> partnatts ; ++ j )
3187+ {
3188+ Oid argtype = get_fn_expr_argtype (fcinfo -> flinfo , j + 3 );
3189+
3190+ if (argtype != key -> parttypid [j ] && !IsBinaryCoercible (argtype , key -> parttypid [j ]))
3191+ ereport (ERROR ,
3192+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
3193+ errmsg ("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"" ,
3194+ j + 1 , format_type_be (key -> parttypid [j ]), format_type_be (argtype ))));
3195+
3196+ fmgr_info_copy (& my_extra -> partsupfunc [j ],
3197+ & key -> partsupfunc [j ],
3198+ fcinfo -> flinfo -> fn_mcxt );
3199+ }
3200+
3201+ }
3202+ else
3203+ {
3204+ ArrayType * variadic_array = PG_GETARG_ARRAYTYPE_P (3 );
3205+
3206+ /* allocate space for our cache -- just one FmgrInfo in this case */
3207+ fcinfo -> flinfo -> fn_extra =
3208+ MemoryContextAllocZero (fcinfo -> flinfo -> fn_mcxt ,
3209+ offsetof(ColumnsHashData , partsupfunc ) +
3210+ sizeof (FmgrInfo ));
3211+ my_extra = (ColumnsHashData * ) fcinfo -> flinfo -> fn_extra ;
3212+ my_extra -> relid = parentId ;
3213+ my_extra -> nkeys = key -> partnatts ;
3214+ my_extra -> variadic_type = ARR_ELEMTYPE (variadic_array );
3215+ get_typlenbyvalalign (my_extra -> variadic_type ,
3216+ & my_extra -> variadic_typlen ,
3217+ & my_extra -> variadic_typbyval ,
3218+ & my_extra -> variadic_typalign );
3219+
3220+ /* check argument types */
3221+ for (j = 0 ; j < key -> partnatts ; ++ j )
3222+ if (key -> parttypid [j ] != my_extra -> variadic_type )
3223+ ereport (ERROR ,
3224+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
3225+ errmsg ("column %d of the partition key has type \"%s\", but supplied value is of type \"%s\"" ,
3226+ j + 1 ,
3227+ format_type_be (key -> parttypid [j ]),
3228+ format_type_be (my_extra -> variadic_type ))));
3229+
3230+ fmgr_info_copy (& my_extra -> partsupfunc [0 ],
3231+ & key -> partsupfunc [0 ],
31423232 fcinfo -> flinfo -> fn_mcxt );
3233+ }
31433234
31443235 /* Hold lock until commit */
3145- heap_close (parent , NoLock );
3236+ relation_close (parent , NoLock );
31463237 }
31473238
3148- for ( i = 0 ; i < nkeys ; i ++ )
3239+ if (! OidIsValid ( my_extra -> variadic_type ) )
31493240 {
3150- /* keys start from fourth argument of function. */
3151- int argno = i + 3 ;
3241+ int nkeys = my_extra -> nkeys ;
3242+ int i ;
3243+
3244+ /*
3245+ * For a non-variadic call, neither the number of arguments nor their
3246+ * types can change across calls, so avoid the expense of rechecking
3247+ * here.
3248+ */
31523249
3153- if (! PG_ARGISNULL ( argno ) )
3250+ for ( i = 0 ; i < nkeys ; i ++ )
31543251 {
31553252 Datum hash ;
31563253
3254+ /* keys start from fourth argument of function. */
3255+ int argno = i + 3 ;
3256+
3257+ if (PG_ARGISNULL (argno ))
3258+ continue ;
3259+
31573260 Assert (OidIsValid (my_extra -> partsupfunc [i ].fn_oid ));
31583261
31593262 hash = FunctionCall2 (& my_extra -> partsupfunc [i ],
@@ -3164,6 +3267,45 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
31643267 rowHash = hash_combine64 (rowHash , DatumGetUInt64 (hash ));
31653268 }
31663269 }
3270+ else
3271+ {
3272+ ArrayType * variadic_array = PG_GETARG_ARRAYTYPE_P (3 );
3273+ int i ;
3274+ int nelems ;
3275+ Datum * datum ;
3276+ bool * isnull ;
3277+
3278+ deconstruct_array (variadic_array ,
3279+ my_extra -> variadic_type ,
3280+ my_extra -> variadic_typlen ,
3281+ my_extra -> variadic_typbyval ,
3282+ my_extra -> variadic_typalign ,
3283+ & datum , & isnull , & nelems );
3284+
3285+ /* complain if wrong number of column values */
3286+ if (nelems != my_extra -> nkeys )
3287+ ereport (ERROR ,
3288+ (errcode (ERRCODE_INVALID_PARAMETER_VALUE ),
3289+ errmsg ("number of partitioning columns (%d) does not match number of partition keys provided (%d)" ,
3290+ my_extra -> nkeys , nelems )));
3291+
3292+ for (i = 0 ; i < nelems ; i ++ )
3293+ {
3294+ Datum hash ;
3295+
3296+ if (isnull [i ])
3297+ continue ;
3298+
3299+ Assert (OidIsValid (my_extra -> partsupfunc [0 ].fn_oid ));
3300+
3301+ hash = FunctionCall2 (& my_extra -> partsupfunc [0 ],
3302+ datum [i ],
3303+ seed );
3304+
3305+ /* Form a single 64-bit hash value */
3306+ rowHash = hash_combine64 (rowHash , DatumGetUInt64 (hash ));
3307+ }
3308+ }
31673309
31683310 PG_RETURN_BOOL (rowHash % modulus == remainder );
31693311}
0 commit comments