40
40
#include "optimizer/planmain.h"
41
41
#include "optimizer/prep.h"
42
42
#include "optimizer/var.h"
43
+ #include "parser/parse_coerce.h"
43
44
#include "rewrite/rewriteManip.h"
44
45
#include "storage/lmgr.h"
45
46
#include "utils/array.h"
@@ -3085,9 +3086,11 @@ compute_hash_value(PartitionKey key, Datum *values, bool *isnull)
3085
3086
/*
3086
3087
* satisfies_hash_partition
3087
3088
*
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.
3091
3094
*
3092
3095
* Returns true if remainder produced when this computed single hash value is
3093
3096
* divided by the given modulus is equal to given remainder, otherwise false.
@@ -3100,60 +3103,160 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
3100
3103
typedef struct ColumnsHashData
3101
3104
{
3102
3105
Oid relid ;
3103
- int16 nkeys ;
3106
+ int nkeys ;
3107
+ Oid variadic_type ;
3108
+ int16 variadic_typlen ;
3109
+ bool variadic_typbyval ;
3110
+ char variadic_typalign ;
3104
3111
FmgrInfo partsupfunc [PARTITION_MAX_KEYS ];
3105
3112
} 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 ;
3111
3116
Datum seed = UInt64GetDatum (HASH_PARTITION_SEED );
3112
3117
ColumnsHashData * my_extra ;
3113
3118
uint64 rowHash = 0 ;
3114
3119
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
+
3115
3141
/*
3116
3142
* Cache hash function information.
3117
3143
*/
3118
3144
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 )
3121
3146
{
3122
3147
Relation parent ;
3123
3148
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 ;
3133
3150
3134
3151
/* 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 ();
3136
3155
key = RelationGetPartitionKey (parent );
3137
3156
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 ],
3142
3232
fcinfo -> flinfo -> fn_mcxt );
3233
+ }
3143
3234
3144
3235
/* Hold lock until commit */
3145
- heap_close (parent , NoLock );
3236
+ relation_close (parent , NoLock );
3146
3237
}
3147
3238
3148
- for ( i = 0 ; i < nkeys ; i ++ )
3239
+ if (! OidIsValid ( my_extra -> variadic_type ) )
3149
3240
{
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
+ */
3152
3249
3153
- if (! PG_ARGISNULL ( argno ) )
3250
+ for ( i = 0 ; i < nkeys ; i ++ )
3154
3251
{
3155
3252
Datum hash ;
3156
3253
3254
+ /* keys start from fourth argument of function. */
3255
+ int argno = i + 3 ;
3256
+
3257
+ if (PG_ARGISNULL (argno ))
3258
+ continue ;
3259
+
3157
3260
Assert (OidIsValid (my_extra -> partsupfunc [i ].fn_oid ));
3158
3261
3159
3262
hash = FunctionCall2 (& my_extra -> partsupfunc [i ],
@@ -3164,6 +3267,45 @@ satisfies_hash_partition(PG_FUNCTION_ARGS)
3164
3267
rowHash = hash_combine64 (rowHash , DatumGetUInt64 (hash ));
3165
3268
}
3166
3269
}
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
+ }
3167
3309
3168
3310
PG_RETURN_BOOL (rowHash % modulus == remainder );
3169
3311
}
0 commit comments