856 lines
26 KiB
C++
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;
|
|
}
|