|
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 | TupleDesctupdesc,
|
99 | 98 | Bitmapset*modifiedCols,
|
100 | 99 | intmaxfieldlen);
|
101 |
| -staticchar*ExecBuildSlotPartitionKeyDescription(Relationrel, |
102 |
| -Datum*values, |
103 |
| -bool*isnull, |
104 |
| -intmaxfieldlen); |
105 | 100 | staticvoidEvalPlanQualStart(EPQState*epqstate,EState*parentestate,
|
106 | 101 | Plan*planTree);
|
107 |
| -staticvoidExecPartitionCheck(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 |
| -staticvoid |
| 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(Relationrel, |
3276 |
| -IndexresultRTindex, |
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 |
| -TupleDesctupDesc=RelationGetDescr(rel); |
3285 |
| -List*leaf_parts; |
3286 |
| -ListCell*cell; |
3287 |
| -inti; |
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 |
| -Relationpartrel; |
3316 |
| -TupleDescpart_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 |
| -intresult; |
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 |
| -Relationfailed_rel; |
3392 |
| -Datumkey_values[PARTITION_MAX_KEYS]; |
3393 |
| -boolkey_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 |
| -returnresult; |
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 |
| -staticchar* |
3424 |
| -ExecBuildSlotPartitionKeyDescription(Relationrel, |
3425 |
| -Datum*values, |
3426 |
| -bool*isnull, |
3427 |
| -intmaxfieldlen) |
3428 |
| -{ |
3429 |
| -StringInfoDatabuf; |
3430 |
| -PartitionKeykey=RelationGetPartitionKey(rel); |
3431 |
| -intpartnatts=get_partition_natts(key); |
3432 |
| -inti; |
3433 |
| -Oidrelid=RelationGetRelid(rel); |
3434 |
| -AclResultaclresult; |
3435 |
| - |
3436 |
| -if (check_enable_rls(relid,InvalidOid, true)==RLS_ENABLED) |
3437 |
| -returnNULL; |
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 |
| -AttrNumberattnum=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 |
| -returnNULL; |
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 |
| -intvallen; |
3471 |
| - |
3472 |
| -if (isnull[i]) |
3473 |
| -val="null"; |
3474 |
| -else |
3475 |
| -{ |
3476 |
| -Oidfoutoid; |
3477 |
| -booltypisvarlena; |
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 |
| -returnbuf.data; |
3502 |
| -} |