Files
openGauss-server/src/gausskernel/optimizer/util/bucketpruning.cpp
2021-09-23 15:19:37 +08:00

856 lines
26 KiB
C++

/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* bucketpruning.cpp
* functions related to bucketpruning
*
* IDENTIFICATION
* src/gausskernel/optimizer/util/bucketpruning.cpp
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#include "access/hash.h"
#include "parser/parsetree.h"
#include "pgxc/locator.h"
#include "optimizer/bucketinfo.h"
#include "optimizer/bucketpruning.h"
#include "optimizer/dynsmp.h"
#include "optimizer/planner.h"
#include "nodes/bitmapset.h"
#include "nodes/makefuncs.h"
#include "nodes/relation.h"
#include "nodes/pg_list.h"
#include "nodes/nodeFuncs.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "pgxc/groupmgr.h"
#include "utils/plancache.h"
#include "pgxc/pgxc.h"
#include "executor/executor.h"
/*
* BucketPruningContext
*
* holds all things needed for pruning
* pass things around when pruning each expr
*
*/
typedef struct BucketPruningContext {
PlannerInfo* root;
RelOptInfo* rel;
RangeTblEntry* rte; /* the range table for pruning */
AttrNumber attno; /* the distribute key attno */
Expr* expr; /* the expr for pruning */
} BucketPruningContext;
/*
* PruningStatus
*
* indicate the pruning result's status
*
* BUCKETS_UNINITIED: before pruning, the result is not know
* BUCKETS_FULL: have to scan all the buckets
* BUCKETS_EMPTY: all the buckets are pruned
* BUCKETS_PRUNED: only some of the buckets are pruning, but not all
*/
typedef enum PruningStatus { BUCKETS_UNINITIED, BUCKETS_FULL, BUCKETS_EMPTY, BUCKETS_PRUNED } PruningStatus;
/*
* BucketPruningResult
*
* store the intermediate pruning results
*
* status: one of BUCKETS_UNINITIED/BUCKETS_FULL/BUCKETS_EMPTY/BUCKETS_PRUNED
* see PruningStatus for what it means
* buckets: a bitmapset containing which bucket id have to be scanned
*/
typedef struct BucketPruningResult {
PruningStatus status;
Bitmapset* buckets;
} BucketPruningResult;
static List* BucketPruningMain(PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* rte, List* restrictInfo);
static BucketPruningContext* makePruningContext(
PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* rte, List* restrictInfo);
static Expr* RestrictInfoGetExpr(List* restrictInfo);
static BucketPruningResult* BucketPruningForExpr(BucketPruningContext* bpcxt, Expr* expr);
static int getConstBucketId(Const* val, int bucketmapsize);
static BucketPruningResult* BucketPruningForBoolExpr(BucketPruningContext* bpcxt, BoolExpr* expr);
static BucketPruningResult* BucketPruningForOpExpr(BucketPruningContext* bpcxt, OpExpr* expr);
static int GetExecBucketId(ExecNodes* exec_nodes, ParamListInfo params);
/*
* @Description: make a pruning result based on the PruningStatus
*
* BUCKETS_UNINITIED: before pruning, the result is not know
* BUCKETS_FULL: make a bitmapset contains all the buckets
* BUCKETS_EMPTY: make a pruning result contain no buckets
* BUCKETS_PRUNED: **not allowed**
*/
static BucketPruningResult* makePruningResult(PruningStatus status, int bucketmapsize = 0)
{
BucketPruningResult* result = (BucketPruningResult*)palloc0(sizeof(BucketPruningResult));
Assert(status != BUCKETS_PRUNED);
result->buckets = NULL;
result->status = status;
if (status == BUCKETS_FULL) {
for (int i = 0; i < bucketmapsize; i++) {
result->buckets = bms_add_member(result->buckets, i);
}
}
return result;
}
/*
* @Description: make a pruning result based on the Bitmapset
*
* when the bitmapset is empty a EMPTY result is returned
* when the bitmapset is full a FULL result is returned and the bitmap set is re-used
* when the bitmapset is not-full a PRUNED result is returned
*/
static BucketPruningResult* makePruningResult(Bitmapset* buckets, int bucketmapsize)
{
if (bms_is_empty(buckets)) {
return makePruningResult(BUCKETS_EMPTY);
}
BucketPruningResult* result = (BucketPruningResult*)palloc0(sizeof(BucketPruningResult));
if (bms_num_members(buckets) == bucketmapsize) {
Assert(bucketmapsize != 0);
result->status = BUCKETS_FULL;
} else {
result->status = BUCKETS_PRUNED;
}
result->buckets = buckets;
return result;
}
/*
* @Description: make a pruning result given a single bucket id
*
* given a bucket id, construct a PRUNED BucketPruningResult for it
* if exclude is true, will make a PRUNED BucketPruningResult excludes x
* which quite handy in var=const and var!=const
*/
static BucketPruningResult* makePruningResult(int x, bool exclude = false)
{
BucketPruningResult* result = (BucketPruningResult*)palloc0(sizeof(BucketPruningResult));
if (exclude) {
Assert(0);
for (int i = 0; i < BUCKETDATALEN; i++) {
if (exclude && i == x) {
continue;
}
result->buckets = bms_add_member(result->buckets, i);
}
} else {
result->buckets = bms_make_singleton(x);
}
result->status = BUCKETS_PRUNED;
return result;
}
/*
* @Description: intersect pruning result of a,b
*
* given two pruning results of a b
* this function intersect the results
* for example a=[1,2,3] b=[2,3,4]
* the function return [2,3] of the intersection
*
* @return a list contains the intersection result
*/
static BucketPruningResult* intersectPruningResult(BucketPruningResult* a, BucketPruningResult* b, int bucketmapsize)
{
if (a->status == BUCKETS_UNINITIED && b->status == BUCKETS_UNINITIED) {
return makePruningResult(BUCKETS_UNINITIED);
}
if (a->status == BUCKETS_UNINITIED && b->status != BUCKETS_UNINITIED) {
return b;
}
if (a->status != BUCKETS_UNINITIED && b->status == BUCKETS_UNINITIED) {
return a;
}
if (a->status == BUCKETS_EMPTY || b->status == BUCKETS_EMPTY) {
return makePruningResult(BUCKETS_EMPTY);
}
return makePruningResult(bms_intersect(a->buckets, b->buckets), bucketmapsize);
}
/*
* @Description: union pruning result of a,b
*
* given two pruning results of a b
* this function union the results
* for example a=[1,2,3] b=[2,3,4]
* the function return [1,2,3,4]
*
* @return a list contains the union result
*/
static BucketPruningResult* unionPruningResult(BucketPruningResult* a, BucketPruningResult* b, int bucketmapsize)
{
if (a->status == BUCKETS_UNINITIED && b->status == BUCKETS_UNINITIED) {
return makePruningResult(BUCKETS_UNINITIED);
}
if (a->status == BUCKETS_UNINITIED && b->status != BUCKETS_UNINITIED) {
return b;
}
if (a->status != BUCKETS_UNINITIED && b->status == BUCKETS_UNINITIED) {
return a;
}
if (a->status == BUCKETS_FULL || b->status == BUCKETS_FULL) {
return makePruningResult(BUCKETS_FULL, bucketmapsize);
}
return makePruningResult(bms_union(a->buckets, b->buckets), bucketmapsize);
}
/*
* @Description: for NotExpr, return buckets A without members of B
*
* given two pruning results of a b
* this function not the results in b
* for example a=[1,2,3] b=[2,3,4]
* the function return [1]
*
* @return a list contains the buckets A without members of B
*/
static BucketPruningResult* notPruningResult(BucketPruningResult* a, BucketPruningResult* b, int bucketmapsize)
{
if (a->status == BUCKETS_UNINITIED && b->status == BUCKETS_UNINITIED) {
return makePruningResult(BUCKETS_UNINITIED);
}
if (a->status == BUCKETS_UNINITIED && b->status != BUCKETS_UNINITIED) {
return notPruningResult(makePruningResult(BUCKETS_FULL, bucketmapsize), b, bucketmapsize);
}
if (a->status != BUCKETS_UNINITIED && b->status == BUCKETS_UNINITIED) {
return a;
}
if (a->status == BUCKETS_EMPTY || b->status == BUCKETS_FULL) {
return makePruningResult(BUCKETS_EMPTY);
}
return makePruningResult(bms_difference(a->buckets, b->buckets), bucketmapsize);
}
/*
* @Description: set bucketinfo to RelOptInfo and do pruning if possible
*
* @in PlannerInfo: it holds some important info we need
* @out RelOptInfo: the bucketinfo will set to rel->bucketInfo
* @in RangeTblEntry: the user might have selected some buckets from sql stmt
*/
void set_rel_bucketinfo(PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* rte)
{
/* add the bucketinfo to the rel */
if (rte->isbucket) {
/* the user select the buckets from sql stmt */
rel->bucketInfo = makeNode(BucketInfo);
rel->bucketInfo->buckets = rte->buckets;
} else if (rte->relhasbucket) {
/* construct the bucket info and ready for pruning */
rel->bucketInfo = makeNode(BucketInfo);
rel->bucketInfo->buckets = BucketPruningMain(root, rel, rte, rel->baserestrictinfo);
} else {
/* the relation does not have underlying buckets */
rel->bucketInfo = NULL;
}
return;
}
/*
* @Description: given a restrictInfo list convert it into an expr
*
* @in restrictInfo: a rel's restrict info
*/
static Expr* RestrictInfoGetExpr(List* restrictInfo)
{
ListCell* cell = NULL;
List* exprList = NIL;
Expr* expr = NULL;
foreach (cell, restrictInfo) {
RestrictInfo* resinfo = (RestrictInfo*)lfirst(cell);
if (PointerIsValid(resinfo->clause)) {
exprList = lappend(exprList, copyObject(resinfo->clause));
}
}
if (exprList == NIL) {
return NULL;
}
if (list_length(exprList) == 0) {
return NULL;
}
if (list_length(exprList) == 1) {
expr = (Expr*)list_nth(exprList, 0);
} else {
expr = makeBoolExpr(AND_EXPR, exprList, 0);
}
return expr;
}
/*
* @Description: Get Distribute Key Attno for later check if
* Var in "Var op Const" is referencing the
* Distribute key.
*
* @in Oid: the oid of the relation
* @return the distribute key attno if any
*/
AttrNumber GetDistributeKeyAttno(Oid reloid)
{
AttrNumber keyattno = InvalidAttrNumber;
if (IS_PGXC_COORDINATOR) {
RelationLocInfo* locinfo = GetRelationLocInfo(reloid);
if (locinfo != NULL && locinfo->locatorType == LOCATOR_TYPE_HASH && list_length(locinfo->partAttrNum) == 1) {
keyattno = (int16)(linitial_int(locinfo->partAttrNum));
}
} else if (IS_PGXC_DATANODE) {
Relation relation = heap_open(reloid, NoLock);
int2vector* colids = relation->rd_bucketkey->bucketKey;
Assert(REALTION_BUCKETKEY_VALID(relation));
if (colids->ndim == 1 && colids->dim1 == 1) {
keyattno = colids->values[0];
}
heap_close(relation, NoLock);
}
return keyattno;
}
/*
* @Description: if pruning is possible fill the info need and return the BucketPruningContext
*
* @in PlannerInfo: this holds many things needed during pruning
* @in RelOptInfo : this holds many things needed during pruning
* @in RangeTblEntry: this holds many things needed during pruning
* @in List of restrictInfo: the restrict info used for pruning
* @return if we can do pruning return BucketPruingContext
* if pruning is not possible return NULL
*/
static BucketPruningContext* makePruningContext(
PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* rte, List* restrictInfo)
{
Expr* expr;
AttrNumber attno;
/* get expr for pruning */
if ((expr = RestrictInfoGetExpr(restrictInfo)) == NULL) {
return NULL;
}
/* get distribute key attno */
if ((attno = GetDistributeKeyAttno(rte->relid)) == InvalidAttrNumber) {
return NULL;
}
BucketPruningContext* bpcxt = (BucketPruningContext*)palloc0(sizeof(BucketPruningContext));
bpcxt->root = root;
bpcxt->rel = rel;
bpcxt->rte = rte;
bpcxt->attno = attno;
bpcxt->expr = expr;
return bpcxt;
}
/*
* @Description: given a pruningResult list convert it into an List
*
* In the pruning process we use bucketid bitmapset, however the
* BucketInfo contains a bucketid List. This function convert
* the bucket Bitmapset to bucket List
*/
List* PruningResultGetBucketList(BucketPruningResult* pruningResult)
{
List* bucketlist = NIL;
if (pruningResult->status == BUCKETS_PRUNED) {
int bucketid = -1;
while ((bucketid = bms_next_member(pruningResult->buckets, bucketid)) >= 0) {
bucketlist = lappend_int(bucketlist, bucketid);
}
return bucketlist;
} else {
return NIL;
}
}
/*
* @Description: The Main Entrance of bucket pruning
*
* for a given relation pruning buckets based on restrictInfo
* step1. make a BucketPruningContext
* step2. do pruning if possible
* step3. return the BucketList as the pruning results
*
* we use a memory context to hold all the memory allocated
* during pruning, because we allocate many BucketPruningResult
* in the middle of pruning, have to free them though.
*
* @in PlannerInfo: this holds many things needed during pruning
* @in RelOptInfo : this holds many things needed during pruning
* @in RangeTblEntry: this holds many things needed during pruning
* @in List of restrictInfo: the restrict info used for pruning
* @return the pruning results
*/
static List* BucketPruningMain(PlannerInfo* root, RelOptInfo* rel, RangeTblEntry* rte, List* restrictInfo)
{
List* bucketlist = NIL;
BucketPruningResult* pruningResult = NULL;
MemoryContext mcxt = AllocSetContextCreate(CurrentMemoryContext,
"BucketPruningMemoryContext",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
/* all the memory allocated during pruing will hold in this cxt */
MemoryContext old = MemoryContextSwitchTo(mcxt);
PG_TRY();
{
BucketPruningContext* bpcxt = makePruningContext(root, rel, rte, restrictInfo);
if (bpcxt != NULL) {
/* do the hard part */
pruningResult = BucketPruningForExpr(bpcxt, bpcxt->expr);
}
}
PG_CATCH();
{
/* restore not-pruned state */
bucketlist = NIL;
pruningResult = NULL;
/* switch back to the original cxt, so the final result can be made */
(void)MemoryContextSwitchTo(old);
ErrorData* edata = CopyErrorData();
elog(LOG, "[BucketPruning] bucket pruning failed : %s", edata->message);
FreeErrorData(edata);
FlushErrorState();
}
PG_END_TRY();
/* switch back to the original cxt, so the final result can be made */
(void)MemoryContextSwitchTo(old);
if (pruningResult != NULL) {
/* covert to a proper list format */
bucketlist = PruningResultGetBucketList(pruningResult);
}
/* now we have all things we need, free the mem during pruning */
MemoryContextDelete(mcxt);
return bucketlist;
}
/*
* @Description: do pruning for a given expr
*
* this is the main workfunction for the bucket pruning
* which recursively goes into expr and pruning when possible
* currently we can only do BoolExpr and OpExpr pruning
*
* @in BucketPruningContext: the context needed for pruning
* @in Expr: the expr used for pruning
* @return the pruning result for the given expr
*/
static BucketPruningResult* BucketPruningForExpr(BucketPruningContext* bpcxt, Expr* expr)
{
BucketPruningResult* result = NULL;
switch (nodeTag(expr)) {
case T_BoolExpr: {
BoolExpr* boolExpr = NULL;
boolExpr = (BoolExpr*)expr;
result = BucketPruningForBoolExpr(bpcxt, boolExpr);
} break;
case T_OpExpr: {
OpExpr* opExpr = NULL;
opExpr = (OpExpr*)expr;
result = BucketPruningForOpExpr(bpcxt, opExpr);
} break;
default: {
return makePruningResult(BUCKETS_FULL, bpcxt->rte->bucketmapsize);
} break;
}
return result;
}
/*
* @Description: do pruning for a given BoolExpr
*
* handle AND/OR/NOT cases of BoolExpr
*
* @in BucketPruningContext: the context needed for pruning
* @in Expr: the expr used for pruning
* @return the pruning result for the given BoolExpr
*/
static BucketPruningResult* BucketPruningForBoolExpr(BucketPruningContext* bpcxt, BoolExpr* expr)
{
ListCell* cell = NULL;
BucketPruningResult* result = makePruningResult(BUCKETS_UNINITIED);
foreach (cell, expr->args) {
Expr* childExpr = (Expr*)lfirst(cell);
BucketPruningResult* childPruningResult = BucketPruningForExpr(bpcxt, childExpr);
switch (expr->boolop) {
case AND_EXPR:
result = intersectPruningResult(result, childPruningResult, bpcxt->rte->bucketmapsize);
break;
case OR_EXPR:
result = unionPruningResult(result, childPruningResult, bpcxt->rte->bucketmapsize);
break;
case NOT_EXPR:
result = notPruningResult(result, childPruningResult, bpcxt->rte->bucketmapsize);
/* fall through */
default:
break;
}
}
return result;
}
/*
* @Description: do pruning for a given OpExpr
*
* support for "var op const" kind of OpExpr
* (1) the var must be a distribute col of the rel
* (2) the op must be =
* otherwise we cannot do pruning
*
* @in BucketPruningContext: the context needed for pruning
* @in Expr: the expr used for pruning
* @return the pruning result for the given expr
*/
static BucketPruningResult* BucketPruningForOpExpr(BucketPruningContext* bpcxt, OpExpr* expr)
{
char* opName = NULL;
Expr* leftArg = NULL;
Expr* rightArg = NULL;
Const* constArg = NULL;
Var* varArg = NULL;
bool rightArgIsConst = true;
/* only handle op of 2 arguments */
if (!PointerIsValid(expr) || list_length(expr->args) != 2 || !PointerIsValid(opName = get_opname(expr->opno))) {
return makePruningResult(BUCKETS_FULL, bpcxt->rte->bucketmapsize);
}
leftArg = (Expr*)list_nth(expr->args, 0);
rightArg = (Expr*)list_nth(expr->args, 1);
/* handle the relabel type */
while (leftArg && IsA(leftArg, RelabelType)) {
leftArg = ((RelabelType*)leftArg)->arg;
}
while (rightArg && IsA(rightArg, RelabelType)) {
rightArg = ((RelabelType*)rightArg)->arg;
}
/* we only handle var op const */
if (!((T_Const == nodeTag(leftArg) && T_Var == nodeTag(rightArg)) ||
(T_Var == nodeTag(leftArg) && T_Const == nodeTag(rightArg)))) {
return makePruningResult(BUCKETS_FULL, bpcxt->rte->bucketmapsize);
}
if (T_Const == nodeTag(leftArg)) {
constArg = (Const*)leftArg;
varArg = (Var*)rightArg;
rightArgIsConst = false;
} else {
constArg = (Const*)rightArg;
varArg = (Var*)leftArg;
}
/* see if var is distribute key */
RangeTblEntry* rte = (RangeTblEntry*)planner_rt_fetch((int)(varArg->varno), bpcxt->root);
/* var is not referencing this relation */
if (rte->relid != bpcxt->rte->relid) {
return makePruningResult(BUCKETS_FULL, bpcxt->rte->bucketmapsize);
}
/* var is not referencing the distribute col */
if (varArg->varattno != bpcxt->attno) {
return makePruningResult(BUCKETS_FULL, bpcxt->rte->bucketmapsize);
}
/* time for pruning */
int id = getConstBucketId(constArg, bpcxt->rte->bucketmapsize);
if (pg_strcasecmp(opName, "=") == 0) {
return makePruningResult(id);
}
return makePruningResult(BUCKETS_FULL, bpcxt->rte->bucketmapsize);
}
/*
* @Description: calculate the bucketid for a const val
*
* notice the algorithm must be the same with
* the executor, double check it when make modify
*
* @in Const: the const val which we want the bucketid of it
* @return the bucketid index of the const val
*/
static int getConstBucketId(Const* val, int bucketmapsize)
{
uint32 hashval = 0;
int bucketid = 0;
if (val->constisnull) {
return 0;
}
hashval = compute_hash(val->consttype, val->constvalue, LOCATOR_TYPE_HASH);
bucketid = compute_modulo((unsigned int)(abs((int)hashval)), bucketmapsize);
return bucketid;
}
void setCachedPlanBucketId(CachedPlan *cplan, ParamListInfo boundParams)
{
/* run time bucket purning for generic plan */
ListCell *p = NULL;
/* not support global plan cache yet */
if (g_instance.attr.attr_common.enable_global_plancache) {
return;
}
foreach (p, cplan->stmt_list) {
PlannedStmt* pstmt = (PlannedStmt*)lfirst(p);
if (IsA(pstmt, PlannedStmt)){
/* set the main plan */
setPlanBucketId(pstmt->planTree, boundParams, cplan->context);
/* also the sub plans */
ListCell* lc = NULL;
foreach (lc, pstmt->subplans) {
Plan* subPlan = (Plan*)lfirst(lc);
setPlanBucketId(subPlan, boundParams, cplan->context);
}
}
}
}
static void setScanPlanBucketId(Plan* plan, ParamListInfo params, MemoryContext cxt)
{
Scan* scan = (Scan*)plan;
/* free the previous results */
if (scan->bucketInfo->buckets != NIL) {
list_free(scan->bucketInfo->buckets);
scan->bucketInfo->buckets = NIL;
}
/* try runtime purning */
int bucketid = GetExecBucketId(plan->exec_nodes, params);
if (bucketid != INVALID_BUCKET_ID) {
/* for cached plan , buckets lives in a longer memory context */
MemoryContext oldcxt = MemoryContextSwitchTo(cxt);
scan->bucketInfo->buckets = lappend_int(scan->bucketInfo->buckets, bucketid);
MemoryContextSwitchTo(oldcxt);
}
}
/*
* Execution time determining of target bucket id
*/
void setPlanBucketId(Plan* plan, ParamListInfo params, MemoryContext cxt)
{
if (plan == NULL)
return;
/* Guard against stack overflow due to overly complex expressions */
check_stack_depth();
/* nodes we are interested */
switch (nodeTag(plan)) {
case T_SeqScan:
case T_CStoreScan:
case T_IndexScan:
case T_IndexOnlyScan:
case T_BitmapHeapScan:
case T_BitmapIndexScan:
case T_TidScan:
case T_CStoreIndexScan:
case T_CStoreIndexCtidScan:
case T_CStoreIndexHeapScan: {
Scan* scan = (Scan*)plan;
/* quit if not a hashbucket relation */
if (scan->bucketInfo == NULL) {
break;
}
setScanPlanBucketId(plan, params, cxt);
break;
}
case T_SubqueryScan:
case T_VecSubqueryScan: {
SubqueryScan* sc = (SubqueryScan*)plan;
setPlanBucketId(sc->subplan, params, cxt);
break;
}
default: {
/* recurse the left tree and right tree */
setPlanBucketId(plan->lefttree, params, cxt);
setPlanBucketId(plan->righttree, params, cxt);
break;
}
}
ListCell* lc = NULL;
foreach(lc, get_plan_list(plan)) {
setPlanBucketId((Plan*)lfirst(lc), params, cxt);
}
return;
}
static int GetExecBucketId(ExecNodes* exec_nodes, ParamListInfo params)
{
bool isnull = false;
MemoryContext oldContext;
if (exec_nodes == NULL || exec_nodes->bucketexpr == NULL)
return INVALID_BUCKET_ID;
RelationLocInfo* rel_loc_info = NULL;
if (IS_PGXC_DATANODE) {
rel_loc_info = GetRelationLocInfoDN(exec_nodes->bucketrelid);
} else {
rel_loc_info = GetRelationLocInfo(exec_nodes->bucketrelid);
}
if (rel_loc_info == NULL) {
return INVALID_BUCKET_ID;
}
int len = list_length(rel_loc_info->partAttrNum);
/* It should switch memctx to ExprContext for makenode in ExecInitExpr */
Datum* values = (Datum*)palloc(len * sizeof(Datum));
bool* null = (bool*)palloc(len * sizeof(bool));
Oid* typOid = (Oid*)palloc(len * sizeof(Oid));
List* dist_col = NULL;
int i = 0;
EState* estate = CreateExecutorState();
estate->es_param_list_info = params;
ExprContext* exprcontext = CreateExprContext(estate);
ListCell* cell = NULL;
foreach (cell, exec_nodes->bucketexpr) {
Expr* expr = (Expr*)lfirst(cell);
oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
ExprState* exprstate = ExecInitExpr(expr, NULL);
Datum partvalue = ExecEvalExpr(exprstate, exprcontext, &isnull, NULL);
MemoryContextSwitchTo(oldContext);
values[i] = partvalue;
null[i] = isnull;
typOid[i] = exprType((Node*)expr);
dist_col = lappend_int(dist_col, i);
i++;
}
ExecNodes* nodes = GetRelationNodes(rel_loc_info,
values, null,
typOid,
dist_col,
exec_nodes->accesstype,
false,
false);
FreeExprContext(exprcontext, true);
FreeExecutorState(estate);
FreeRelationLocInfo(rel_loc_info);
pfree_ext(values);
pfree_ext(null);
pfree_ext(typOid);
list_free_ext(dist_col);
return nodes->bucketid;
}
BucketInfo* CalBucketInfo(ScanState* state)
{
Assert(state != NULL);
if (unlikely((state->ps.plan == NULL) || (state->ps.state == NULL))) {
return NULL;
}
BucketInfo* bkinfo = NULL;
int bucketid = GetExecBucketId(state->ps.plan->exec_nodes, state->ps.state->es_param_list_info);
if (bucketid != INVALID_BUCKET_ID) {
bkinfo = (BucketInfo*)palloc0(sizeof(BucketInfo));
bkinfo->buckets = NIL;
bkinfo->buckets = lappend_int(bkinfo->buckets, bucketid);
}
return bkinfo;
}