|
43 | 43 | #include "access/xact.h" |
44 | 44 | #include "catalog/namespace.h" |
45 | 45 | #include "catalog/partition.h" |
46 | | -#include "catalog/pg_inherits_fn.h" |
47 | 46 | #include "catalog/pg_publication.h" |
48 | 47 | #include "commands/matview.h" |
49 | 48 | #include "commands/trigger.h" |
@@ -98,14 +97,8 @@ static char *ExecBuildSlotValueDescription(Oid reloid, |
98 | 97 | TupleDesc tupdesc, |
99 | 98 | Bitmapset *modifiedCols, |
100 | 99 | int maxfieldlen); |
101 | | -static char *ExecBuildSlotPartitionKeyDescription(Relation rel, |
102 | | - Datum *values, |
103 | | - bool *isnull, |
104 | | - int maxfieldlen); |
105 | 100 | static void EvalPlanQualStart(EPQState *epqstate, EState *parentestate, |
106 | 101 | Plan *planTree); |
107 | | -static void ExecPartitionCheck(ResultRelInfo *resultRelInfo, |
108 | | - TupleTableSlot *slot, EState *estate); |
109 | 102 |
|
110 | 103 | /* |
111 | 104 | * Note that GetUpdatedColumns() also exists in commands/trigger.c. There does |
@@ -1854,8 +1847,10 @@ ExecRelCheck(ResultRelInfo *resultRelInfo, |
1854 | 1847 |
|
1855 | 1848 | /* |
1856 | 1849 | * ExecPartitionCheck --- check that tuple meets the partition constraint. |
| 1850 | + * |
| 1851 | + * Exported in executor.h for outside use. |
1857 | 1852 | */ |
1858 | | -static void |
| 1853 | +void |
1859 | 1854 | ExecPartitionCheck(ResultRelInfo *resultRelInfo, TupleTableSlot *slot, |
1860 | 1855 | EState *estate) |
1861 | 1856 | { |
@@ -3245,258 +3240,3 @@ EvalPlanQualEnd(EPQState *epqstate) |
3245 | 3240 | epqstate->planstate = NULL; |
3246 | 3241 | epqstate->origslot = NULL; |
3247 | 3242 | } |
3248 | | - |
3249 | | -/* |
3250 | | - * ExecSetupPartitionTupleRouting - set up information needed during |
3251 | | - * tuple routing for partitioned tables |
3252 | | - * |
3253 | | - * Output arguments: |
3254 | | - * 'pd' receives an array of PartitionDispatch objects with one entry for |
3255 | | - * every partitioned table in the partition tree |
3256 | | - * 'partitions' receives an array of ResultRelInfo* objects with one entry for |
3257 | | - * every leaf partition in the partition tree |
3258 | | - * 'tup_conv_maps' receives an array of TupleConversionMap objects with one |
3259 | | - * entry for every leaf partition (required to convert input tuple based |
3260 | | - * on the root table's rowtype to a leaf partition's rowtype after tuple |
3261 | | - * routing is done) |
3262 | | - * 'partition_tuple_slot' receives a standalone TupleTableSlot to be used |
3263 | | - * to manipulate any given leaf partition's rowtype after that partition |
3264 | | - * is chosen by tuple-routing. |
3265 | | - * 'num_parted' receives the number of partitioned tables in the partition |
3266 | | - * tree (= the number of entries in the 'pd' output array) |
3267 | | - * 'num_partitions' receives the number of leaf partitions in the partition |
3268 | | - * tree (= the number of entries in the 'partitions' and 'tup_conv_maps' |
3269 | | - * output arrays |
3270 | | - * |
3271 | | - * Note that all the relations in the partition tree are locked using the |
3272 | | - * RowExclusiveLock mode upon return from this function. |
3273 | | - */ |
3274 | | -void |
3275 | | -ExecSetupPartitionTupleRouting(Relation rel, |
3276 | | - Index resultRTindex, |
3277 | | - EState *estate, |
3278 | | - PartitionDispatch **pd, |
3279 | | - ResultRelInfo ***partitions, |
3280 | | - TupleConversionMap ***tup_conv_maps, |
3281 | | - TupleTableSlot **partition_tuple_slot, |
3282 | | - int *num_parted, int *num_partitions) |
3283 | | -{ |
3284 | | - TupleDesc tupDesc = RelationGetDescr(rel); |
3285 | | - List *leaf_parts; |
3286 | | - ListCell *cell; |
3287 | | - int i; |
3288 | | - ResultRelInfo *leaf_part_rri; |
3289 | | - |
3290 | | - /* |
3291 | | - * Get the information about the partition tree after locking all the |
3292 | | - * partitions. |
3293 | | - */ |
3294 | | - (void) find_all_inheritors(RelationGetRelid(rel), RowExclusiveLock, NULL); |
3295 | | - *pd = RelationGetPartitionDispatchInfo(rel, num_parted, &leaf_parts); |
3296 | | - *num_partitions = list_length(leaf_parts); |
3297 | | - *partitions = (ResultRelInfo **) palloc(*num_partitions * |
3298 | | - sizeof(ResultRelInfo *)); |
3299 | | - *tup_conv_maps = (TupleConversionMap **) palloc0(*num_partitions * |
3300 | | - sizeof(TupleConversionMap *)); |
3301 | | - |
3302 | | - /* |
3303 | | - * Initialize an empty slot that will be used to manipulate tuples of any |
3304 | | - * given partition's rowtype. It is attached to the caller-specified node |
3305 | | - * (such as ModifyTableState) and released when the node finishes |
3306 | | - * processing. |
3307 | | - */ |
3308 | | - *partition_tuple_slot = MakeTupleTableSlot(); |
3309 | | - |
3310 | | - leaf_part_rri = (ResultRelInfo *) palloc0(*num_partitions * |
3311 | | - sizeof(ResultRelInfo)); |
3312 | | - i = 0; |
3313 | | - foreach(cell, leaf_parts) |
3314 | | - { |
3315 | | - Relation partrel; |
3316 | | - TupleDesc part_tupdesc; |
3317 | | - |
3318 | | - /* |
3319 | | - * We locked all the partitions above including the leaf partitions. |
3320 | | - * Note that each of the relations in *partitions are eventually |
3321 | | - * closed by the caller. |
3322 | | - */ |
3323 | | - partrel = heap_open(lfirst_oid(cell), NoLock); |
3324 | | - part_tupdesc = RelationGetDescr(partrel); |
3325 | | - |
3326 | | - /* |
3327 | | - * Save a tuple conversion map to convert a tuple routed to this |
3328 | | - * partition from the parent's type to the partition's. |
3329 | | - */ |
3330 | | - (*tup_conv_maps)[i] = convert_tuples_by_name(tupDesc, part_tupdesc, |
3331 | | - gettext_noop("could not convert row type")); |
3332 | | - |
3333 | | - InitResultRelInfo(leaf_part_rri, |
3334 | | - partrel, |
3335 | | - resultRTindex, |
3336 | | - rel, |
3337 | | - estate->es_instrument); |
3338 | | - |
3339 | | - /* |
3340 | | - * Verify result relation is a valid target for INSERT. |
3341 | | - */ |
3342 | | - CheckValidResultRel(leaf_part_rri, CMD_INSERT); |
3343 | | - |
3344 | | - /* |
3345 | | - * Open partition indices (remember we do not support ON CONFLICT in |
3346 | | - * case of partitioned tables, so we do not need support information |
3347 | | - * for speculative insertion) |
3348 | | - */ |
3349 | | - if (leaf_part_rri->ri_RelationDesc->rd_rel->relhasindex && |
3350 | | - leaf_part_rri->ri_IndexRelationDescs == NULL) |
3351 | | - ExecOpenIndices(leaf_part_rri, false); |
3352 | | - |
3353 | | - estate->es_leaf_result_relations = |
3354 | | - lappend(estate->es_leaf_result_relations, leaf_part_rri); |
3355 | | - |
3356 | | - (*partitions)[i] = leaf_part_rri++; |
3357 | | - i++; |
3358 | | - } |
3359 | | -} |
3360 | | - |
3361 | | -/* |
3362 | | - * ExecFindPartition -- Find a leaf partition in the partition tree rooted |
3363 | | - * at parent, for the heap tuple contained in *slot |
3364 | | - * |
3365 | | - * estate must be non-NULL; we'll need it to compute any expressions in the |
3366 | | - * partition key(s) |
3367 | | - * |
3368 | | - * If no leaf partition is found, this routine errors out with the appropriate |
3369 | | - * error message, else it returns the leaf partition sequence number returned |
3370 | | - * by get_partition_for_tuple() unchanged. |
3371 | | - */ |
3372 | | -int |
3373 | | -ExecFindPartition(ResultRelInfo *resultRelInfo, PartitionDispatch *pd, |
3374 | | - TupleTableSlot *slot, EState *estate) |
3375 | | -{ |
3376 | | - int result; |
3377 | | - PartitionDispatchData *failed_at; |
3378 | | - TupleTableSlot *failed_slot; |
3379 | | - |
3380 | | - /* |
3381 | | - * First check the root table's partition constraint, if any. No point in |
3382 | | - * routing the tuple if it doesn't belong in the root table itself. |
3383 | | - */ |
3384 | | - if (resultRelInfo->ri_PartitionCheck) |
3385 | | - ExecPartitionCheck(resultRelInfo, slot, estate); |
3386 | | - |
3387 | | - result = get_partition_for_tuple(pd, slot, estate, |
3388 | | - &failed_at, &failed_slot); |
3389 | | - if (result < 0) |
3390 | | - { |
3391 | | - Relation failed_rel; |
3392 | | - Datum key_values[PARTITION_MAX_KEYS]; |
3393 | | - bool key_isnull[PARTITION_MAX_KEYS]; |
3394 | | - char *val_desc; |
3395 | | - ExprContext *ecxt = GetPerTupleExprContext(estate); |
3396 | | - |
3397 | | - failed_rel = failed_at->reldesc; |
3398 | | - ecxt->ecxt_scantuple = failed_slot; |
3399 | | - FormPartitionKeyDatum(failed_at, failed_slot, estate, |
3400 | | - key_values, key_isnull); |
3401 | | - val_desc = ExecBuildSlotPartitionKeyDescription(failed_rel, |
3402 | | - key_values, |
3403 | | - key_isnull, |
3404 | | - 64); |
3405 | | - Assert(OidIsValid(RelationGetRelid(failed_rel))); |
3406 | | - ereport(ERROR, |
3407 | | - (errcode(ERRCODE_CHECK_VIOLATION), |
3408 | | - errmsg("no partition of relation \"%s\" found for row", |
3409 | | - RelationGetRelationName(failed_rel)), |
3410 | | - val_desc ? errdetail("Partition key of the failing row contains %s.", val_desc) : 0)); |
3411 | | - } |
3412 | | - |
3413 | | - return result; |
3414 | | -} |
3415 | | - |
3416 | | -/* |
3417 | | - * BuildSlotPartitionKeyDescription |
3418 | | - * |
3419 | | - * This works very much like BuildIndexValueDescription() and is currently |
3420 | | - * used for building error messages when ExecFindPartition() fails to find |
3421 | | - * partition for a row. |
3422 | | - */ |
3423 | | -static char * |
3424 | | -ExecBuildSlotPartitionKeyDescription(Relation rel, |
3425 | | - Datum *values, |
3426 | | - bool *isnull, |
3427 | | - int maxfieldlen) |
3428 | | -{ |
3429 | | - StringInfoData buf; |
3430 | | - PartitionKey key = RelationGetPartitionKey(rel); |
3431 | | - int partnatts = get_partition_natts(key); |
3432 | | - int i; |
3433 | | - Oid relid = RelationGetRelid(rel); |
3434 | | - AclResult aclresult; |
3435 | | - |
3436 | | - if (check_enable_rls(relid, InvalidOid, true) == RLS_ENABLED) |
3437 | | - return NULL; |
3438 | | - |
3439 | | - /* If the user has table-level access, just go build the description. */ |
3440 | | - aclresult = pg_class_aclcheck(relid, GetUserId(), ACL_SELECT); |
3441 | | - if (aclresult != ACLCHECK_OK) |
3442 | | - { |
3443 | | - /* |
3444 | | - * Step through the columns of the partition key and make sure the |
3445 | | - * user has SELECT rights on all of them. |
3446 | | - */ |
3447 | | - for (i = 0; i < partnatts; i++) |
3448 | | - { |
3449 | | - AttrNumber attnum = get_partition_col_attnum(key, i); |
3450 | | - |
3451 | | - /* |
3452 | | - * If this partition key column is an expression, we return no |
3453 | | - * detail rather than try to figure out what column(s) the |
3454 | | - * expression includes and if the user has SELECT rights on them. |
3455 | | - */ |
3456 | | - if (attnum == InvalidAttrNumber || |
3457 | | - pg_attribute_aclcheck(relid, attnum, GetUserId(), |
3458 | | - ACL_SELECT) != ACLCHECK_OK) |
3459 | | - return NULL; |
3460 | | - } |
3461 | | - } |
3462 | | - |
3463 | | - initStringInfo(&buf); |
3464 | | - appendStringInfo(&buf, "(%s) = (", |
3465 | | - pg_get_partkeydef_columns(relid, true)); |
3466 | | - |
3467 | | - for (i = 0; i < partnatts; i++) |
3468 | | - { |
3469 | | - char *val; |
3470 | | - int vallen; |
3471 | | - |
3472 | | - if (isnull[i]) |
3473 | | - val = "null"; |
3474 | | - else |
3475 | | - { |
3476 | | - Oid foutoid; |
3477 | | - bool typisvarlena; |
3478 | | - |
3479 | | - getTypeOutputInfo(get_partition_col_typid(key, i), |
3480 | | - &foutoid, &typisvarlena); |
3481 | | - val = OidOutputFunctionCall(foutoid, values[i]); |
3482 | | - } |
3483 | | - |
3484 | | - if (i > 0) |
3485 | | - appendStringInfoString(&buf, ", "); |
3486 | | - |
3487 | | - /* truncate if needed */ |
3488 | | - vallen = strlen(val); |
3489 | | - if (vallen <= maxfieldlen) |
3490 | | - appendStringInfoString(&buf, val); |
3491 | | - else |
3492 | | - { |
3493 | | - vallen = pg_mbcliplen(val, vallen, maxfieldlen); |
3494 | | - appendBinaryStringInfo(&buf, val, vallen); |
3495 | | - appendStringInfoString(&buf, "..."); |
3496 | | - } |
3497 | | - } |
3498 | | - |
3499 | | - appendStringInfoChar(&buf, ')'); |
3500 | | - |
3501 | | - return buf.data; |
3502 | | -} |
0 commit comments