2543 lines
93 KiB
C++
2543 lines
93 KiB
C++
/*
|
|
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
|
* Portions Copyright (c) 2021, openGauss Contributors
|
|
*
|
|
* 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.
|
|
* -------------------------------------------------------------------------
|
|
*
|
|
* pruning.cpp
|
|
* data partition
|
|
*
|
|
* IDENTIFICATION
|
|
* src/gausskernel/optimizer/util/pruning.cpp
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
#include "postgres.h"
|
|
#include "knl/knl_variable.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "catalog/index.h"
|
|
#include "commands/tablecmds.h"
|
|
#include "nodes/makefuncs.h"
|
|
#include "nodes/nodeFuncs.h"
|
|
#include "nodes/relation.h"
|
|
#include "optimizer/clauses.h"
|
|
#include "optimizer/pruning.h"
|
|
#include "optimizer/pruningboundary.h"
|
|
#include "optimizer/subpartitionpruning.h"
|
|
#include "utils/array.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/rel_gs.h"
|
|
#include "utils/syscache.h"
|
|
#include "utils/relcache.h"
|
|
#include "catalog/pg_partition_fn.h"
|
|
|
|
static PruningResult* partitionPruningFromBoolExpr(const BoolExpr* expr, PruningContext* context);
|
|
static PruningResult* intersectChildPruningResult(const List* resultList, PruningContext* context);
|
|
static PruningResult* unionChildPruningResult(const List* resultList, PruningContext* context);
|
|
static PruningResult* partitionPruningFromScalarArrayOpExpr
|
|
(const ScalarArrayOpExpr* arrayExpr, PruningContext* pruningCtx);
|
|
static PruningResult* recordBoundaryFromOpExpr(const OpExpr* expr, PruningContext* context);
|
|
static PruningResult* partitionPruningFromNullTest(NullTest* expr, PruningContext* context);
|
|
static void partitionFilter(PruningContext* context, PruningResult* pruningResult);
|
|
static bool partitionShouldEliminated(PruningContext* context, int partSeq, PruningBoundary* boundary);
|
|
static PartKeyRange* constructPartKeyRange(PruningContext* context, int partSeq);
|
|
static PruningBoundary* mergeBoundary(PruningBoundary* leftBoundary, PruningBoundary* rightBoundary);
|
|
static void cleanPruningBottom(PruningContext* context, PartitionIdentifier* bottomSeq, Const* value);
|
|
static void cleanPruningTop(PruningContext* context, PartitionIdentifier* topSeq, Const* value);
|
|
static void destroyPruningResultList(List* resultList);
|
|
static Node* EvalExprValueWhenPruning(PruningContext* context, Node* node);
|
|
|
|
static PruningResult* recordEqualFromOpExpr(PartitionType partType,
|
|
const OpExpr* expr, PruningContext* context);
|
|
static PruningResult* partitionPruningFromBoolExpr(PartitionType partType,
|
|
const BoolExpr* expr, PruningContext* context);
|
|
static PruningResult* partitionPruningFromNullTest(PartitionType partType,
|
|
NullTest* expr, PruningContext* context);
|
|
static PruningResult* partitionPruningFromScalarArrayOpExpr(PartitionType partType,
|
|
const ScalarArrayOpExpr* arrayExpr, PruningContext* pruningCtx);
|
|
static PruningResult* partitionEqualPruningWalker(PartitionType partType, Expr* expr, PruningContext* pruningCtx);
|
|
|
|
#define NoNeedPruning(pruningResult) \
|
|
(PruningResultIsFull(pruningResult) || PruningResultIsEmpty(pruningResult) || \
|
|
!PointerIsValid((pruningResult)->boundary))
|
|
#define IsCleanPruningBottom(bottomSeqPtr, pruningResult, bottomValue) \
|
|
((pruningResult)->boundary->partitionKeyNum > 1 && PointerIsValid((bottomValue)[0]) && \
|
|
!(pruningResult)->boundary->minClose[0])
|
|
|
|
#define IsCleanPruningTop(topSeqPtr, pruningResult, topValue) \
|
|
((pruningResult)->boundary->partitionKeyNum > 1 && (topValue) && PointerIsValid((topValue)[0]) && \
|
|
!(pruningResult)->boundary->maxClose[0])
|
|
|
|
static PartitionMap* GetRelPartitionMap(Relation relation)
|
|
{
|
|
return relation->partMap;
|
|
}
|
|
|
|
static void CollectSubpartitionPruningResults(PruningResult* resPartition, Relation current_relation)
|
|
{
|
|
if (!RelationIsSubPartitioned(current_relation)) {
|
|
return;
|
|
}
|
|
|
|
int partSeq;
|
|
ListCell *cell = NULL;
|
|
Oid partitionOid = InvalidOid;
|
|
foreach (cell, resPartition->ls_rangeSelectedPartitions) {
|
|
partSeq = lfirst_int(cell);
|
|
partitionOid = getPartitionOidFromSequence(current_relation, partSeq, resPartition->partMap);
|
|
SubPartitionPruningResult *subPartPruningRes =
|
|
PreGetSubPartitionFullPruningResult(current_relation, partitionOid);
|
|
if (subPartPruningRes == NULL) {
|
|
continue;
|
|
}
|
|
subPartPruningRes->partSeq = partSeq;
|
|
resPartition->ls_selectedSubPartitions = lappend(resPartition->ls_selectedSubPartitions, subPartPruningRes);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @@GaussDB@@
|
|
* Brief
|
|
* Description :Get Partition Info from expr when Partition Info not in plan.
|
|
* return value:
|
|
*/
|
|
PruningResult* GetPartitionInfo(PruningResult* result, EState* estate, Relation current_relation)
|
|
{
|
|
PruningResult* resPartition = NULL;
|
|
PruningContext context;
|
|
|
|
context.pruningType = PruningPartition;
|
|
context.GetPartitionMap = GetRelPartitionMap;
|
|
context.root = NULL;
|
|
context.rte = NULL;
|
|
context.estate = estate;
|
|
context.relation = current_relation;
|
|
/* if the partmap which copied before static pruning exists, it will replace the rel->partMap, seen in
|
|
* GetPartitionMap */
|
|
context.partmap = result->partMap;
|
|
|
|
if (current_relation->partMap->type == PART_TYPE_LIST || current_relation->partMap->type == PART_TYPE_HASH) {
|
|
resPartition = partitionEqualPruningWalker(current_relation->partMap->type, result->expr, &context);
|
|
} else {
|
|
resPartition = partitionPruningWalker(result->expr, &context);
|
|
}
|
|
partitionPruningFromBoundary(&context, resPartition);
|
|
if (PruningResultIsFull(resPartition) ||
|
|
(resPartition->bm_rangeSelectedPartitions == NULL && PruningResultIsSubset(resPartition))) {
|
|
destroyPruningResult(resPartition);
|
|
resPartition = getFullPruningResult(current_relation);
|
|
CollectSubpartitionPruningResults(resPartition, current_relation);
|
|
return resPartition;
|
|
}
|
|
if (PointerIsValid(resPartition) && !PruningResultIsFull(resPartition))
|
|
generateListFromPruningBM(resPartition);
|
|
|
|
CollectSubpartitionPruningResults(resPartition, current_relation);
|
|
|
|
return resPartition;
|
|
}
|
|
|
|
/*
|
|
* @@GaussDB@@
|
|
* Brief
|
|
* Description :
|
|
* return value:
|
|
*/
|
|
PruningResult* copyPruningResult(PruningResult* srcPruningResult)
|
|
{
|
|
if (srcPruningResult != NULL) {
|
|
PruningResult* newpruningInfo = makeNode(PruningResult);
|
|
|
|
newpruningInfo->state = srcPruningResult->state;
|
|
newpruningInfo->bm_rangeSelectedPartitions = bms_copy(srcPruningResult->bm_rangeSelectedPartitions);
|
|
newpruningInfo->intervalOffset = srcPruningResult->intervalOffset;
|
|
newpruningInfo->intervalSelectedPartitions = bms_copy(srcPruningResult->intervalSelectedPartitions);
|
|
newpruningInfo->ls_rangeSelectedPartitions = (List*)copyObject(srcPruningResult->ls_rangeSelectedPartitions);
|
|
newpruningInfo->ls_selectedSubPartitions = (List*)copyObject(srcPruningResult->ls_selectedSubPartitions);
|
|
newpruningInfo->paramArg = (Param *)copyObject(srcPruningResult->paramArg);
|
|
newpruningInfo->expr = (Expr *)copyObject(srcPruningResult->expr);
|
|
newpruningInfo->exprPart = (OpExpr *)copyObject(srcPruningResult->exprPart);
|
|
newpruningInfo->partMap = (PartitionMap *)CopyPartitionMap(srcPruningResult->partMap);
|
|
|
|
return newpruningInfo;
|
|
} else {
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
void generateListFromPruningBM(PruningResult* result)
|
|
{
|
|
int partitions = 0;
|
|
int i = 0;
|
|
int tmpcheck = 0;
|
|
Bitmapset* tmpset = NULL;
|
|
result->ls_rangeSelectedPartitions = NULL;
|
|
|
|
tmpset = bms_copy(result->bm_rangeSelectedPartitions);
|
|
partitions = bms_num_members(result->bm_rangeSelectedPartitions);
|
|
|
|
for (; i < partitions; i++) {
|
|
tmpcheck = bms_first_member(tmpset);
|
|
AssertEreport(-1 != tmpcheck, MOD_OPT, "");
|
|
if (-1 != tmpcheck) {
|
|
result->ls_rangeSelectedPartitions = lappend_int(result->ls_rangeSelectedPartitions, tmpcheck);
|
|
}
|
|
}
|
|
bms_free_ext(tmpset);
|
|
}
|
|
|
|
List* restrictInfoListToExprList(List* restrictInfoList)
|
|
{
|
|
ListCell* cell = NULL;
|
|
RestrictInfo* iteratorRestrict = NULL;
|
|
List* exprList = NIL;
|
|
Expr* expr = NULL;
|
|
foreach (cell, restrictInfoList) {
|
|
iteratorRestrict = (RestrictInfo*)lfirst(cell);
|
|
if (PointerIsValid(iteratorRestrict->clause)) {
|
|
expr = (Expr*)copyObject(iteratorRestrict->clause);
|
|
exprList = lappend(exprList, expr);
|
|
}
|
|
}
|
|
return exprList;
|
|
}
|
|
|
|
/*
|
|
* @@GaussDB@@
|
|
* Brief
|
|
* Description : eliminate partitions which don't contain those tuple satisfy expression.
|
|
* return value: non-eliminated partitions.
|
|
*/
|
|
PruningResult* partitionPruningForRestrictInfo(
|
|
PlannerInfo* root, RangeTblEntry* rte, Relation rel, List* restrictInfoList, PartitionMap *partmap)
|
|
{
|
|
PruningResult* result = NULL;
|
|
Expr* expr = NULL;
|
|
|
|
if (0 == list_length(restrictInfoList)) {
|
|
result = getFullPruningResult(rel);
|
|
} else {
|
|
List* exprList = NULL;
|
|
int length = 0;
|
|
|
|
exprList = restrictInfoListToExprList(restrictInfoList);
|
|
|
|
length = list_length(exprList);
|
|
if (length == 0) {
|
|
result = getFullPruningResult(rel);
|
|
} else if (length == 1) {
|
|
expr = (Expr*)list_nth(exprList, 0);
|
|
result = partitionPruningForExpr(root, rte, rel, expr);
|
|
} else {
|
|
expr = makeBoolExpr(AND_EXPR, exprList, 0);
|
|
result = partitionPruningForExpr(root, rte, rel, expr);
|
|
}
|
|
}
|
|
|
|
if (PointerIsValid(result) && !PruningResultIsFull(result)) {
|
|
generateListFromPruningBM(result);
|
|
}
|
|
|
|
if (RelationIsSubPartitioned(rel) && PointerIsValid(result)) {
|
|
Bitmapset *partIdx = NULL;
|
|
List* part_seqs = result->ls_rangeSelectedPartitions;
|
|
ListCell *cell = NULL;
|
|
RangeTblEntry *partRte = (RangeTblEntry *)copyObject(rte);
|
|
|
|
foreach (cell, part_seqs)
|
|
{
|
|
int part_seq = lfirst_int(cell);
|
|
Oid partOid = getPartitionOidFromSequence(rel, part_seq, partmap);
|
|
Partition partTable = tryPartitionOpen(rel, partOid, AccessShareLock);
|
|
if (!partTable) {
|
|
PartStatus currStatus = PartitionGetMetadataStatus(partOid, false);
|
|
if (currStatus != PART_METADATA_INVISIBLE) {
|
|
ReportPartitionOpenError(rel, partOid);
|
|
}
|
|
continue;
|
|
}
|
|
Relation partRel = partitionGetRelation(rel, partTable);
|
|
PruningResult *subResult = NULL;
|
|
partRte->relid = partOid;
|
|
|
|
if (list_length(restrictInfoList) == 0) {
|
|
subResult = getFullPruningResult(partRel);
|
|
} else {
|
|
subResult = partitionPruningForExpr(root, partRte, partRel, expr);
|
|
}
|
|
|
|
if (PointerIsValid(subResult) && !PruningResultIsEmpty(subResult)) {
|
|
generateListFromPruningBM(subResult);
|
|
if (bms_num_members(subResult->bm_rangeSelectedPartitions) > 0) {
|
|
SubPartitionPruningResult *subPruning = makeNode(SubPartitionPruningResult);
|
|
subPruning->partSeq = part_seq;
|
|
subPruning->bm_selectedSubPartitions = subResult->bm_rangeSelectedPartitions;
|
|
subPruning->ls_selectedSubPartitions = subResult->ls_rangeSelectedPartitions;
|
|
result->ls_selectedSubPartitions = lappend(result->ls_selectedSubPartitions,
|
|
subPruning);
|
|
partIdx = bms_add_member(partIdx, part_seq);
|
|
}
|
|
}
|
|
|
|
releaseDummyRelation(&partRel);
|
|
partitionClose(rel, partTable, AccessShareLock);
|
|
}
|
|
|
|
// adjust
|
|
if (!bms_equal(result->bm_rangeSelectedPartitions, partIdx)) {
|
|
result->bm_rangeSelectedPartitions = partIdx;
|
|
generateListFromPruningBM(result);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* @@GaussDB@@
|
|
* Target : data partition
|
|
* Brief : select * from partition (partition_name) or select * from partition for (partition_values_list)
|
|
* Description : eliminate partitions which don't contain those tuple satisfy expression.
|
|
* return value : non-eliminated partitions.
|
|
*/
|
|
PruningResult* singlePartitionPruningForRestrictInfo(Oid partitionOid, Relation rel)
|
|
{
|
|
bool find = false;
|
|
int partitionSeq = 0;
|
|
PruningResult* pruningRes = NULL;
|
|
int counter = 0;
|
|
|
|
if (!PointerIsValid(rel)) {
|
|
return NULL;
|
|
}
|
|
|
|
/* shouldn't happen */
|
|
if (!PointerIsValid(rel->partMap)) {
|
|
ereport(ERROR, (errmodule(MOD_OPT),
|
|
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
|
errmsg("relation of oid=\"%u\" is not partitioned table", rel->rd_id)));
|
|
}
|
|
|
|
/*
|
|
* only one possible partitionOid is InvalidOid:
|
|
* select * from partitioned_table_name partition for (values);
|
|
* and values pruning interval partition which does not physical exist.
|
|
*/
|
|
if (!OidIsValid(partitionOid) && rel->partMap->type != PART_TYPE_INTERVAL) {
|
|
return NULL;
|
|
}
|
|
|
|
pruningRes = makeNode(PruningResult);
|
|
pruningRes->state = PRUNING_RESULT_SUBSET;
|
|
|
|
/* it's a pattitioned table without interval*/
|
|
if (rel->partMap->type == PART_TYPE_RANGE || rel->partMap->type == PART_TYPE_INTERVAL) {
|
|
RangePartitionMap* rangePartMap = NULL;
|
|
rangePartMap = (RangePartitionMap*)rel->partMap;
|
|
|
|
for (counter = 0; counter < rangePartMap->rangeElementsNum; counter++) {
|
|
/* the partition is a range partition */
|
|
if ((rangePartMap->rangeElements + counter)->partitionOid == partitionOid) {
|
|
find = true;
|
|
partitionSeq = counter;
|
|
|
|
break;
|
|
}
|
|
}
|
|
} else if (PART_TYPE_LIST == rel->partMap->type) {
|
|
ListPartitionMap* listPartMap = NULL;
|
|
listPartMap = (ListPartitionMap*)rel->partMap;
|
|
|
|
for (counter = 0; counter < listPartMap->listElementsNum; counter++) {
|
|
/* the partition is a range partition */
|
|
if ((listPartMap->listElements + counter)->partitionOid == partitionOid) {
|
|
find = true;
|
|
partitionSeq = counter;
|
|
|
|
break;
|
|
}
|
|
}
|
|
} else if (PART_TYPE_HASH == rel->partMap->type) {
|
|
HashPartitionMap* hashPartMap = NULL;
|
|
hashPartMap = (HashPartitionMap*)rel->partMap;
|
|
|
|
for (counter = 0; counter < hashPartMap->hashElementsNum; counter++) {
|
|
/* the partition is a range partition */
|
|
if ((hashPartMap->hashElements + counter)->partitionOid == partitionOid) {
|
|
find = true;
|
|
partitionSeq = counter;
|
|
|
|
break;
|
|
}
|
|
}
|
|
} else { /* shouldn't happen */
|
|
pfree_ext(pruningRes);
|
|
ereport(ERROR, (errmodule(MOD_OPT),
|
|
errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unsupport partition type")));
|
|
}
|
|
|
|
/* In normal condition, it should never happen.
|
|
* But if the Query is from a view/rule contains a partition, this case may happen if the partition is dropped by
|
|
* DDL operation, such as DROP/SPLIT/MERGE/TRUNCATE (UPDATE GLOBAL INDEX)/EXCHANGE (UPDATE GLOBAL INDEX) */
|
|
if (!find) {
|
|
pfree_ext(pruningRes);
|
|
ereport(ERROR,
|
|
(errmodule(MOD_OPT), errcode(ERRCODE_UNDEFINED_TABLE),
|
|
errmsg("fail to find partition with oid %u for partitioned table %u", partitionOid, rel->rd_id),
|
|
errdetail("this partition may have already been dropped by DDL operation"),
|
|
errhint("Check if this query contains a view that refrences the target partition. "
|
|
"If so, REBUILD this view.")));
|
|
}
|
|
|
|
pruningRes->bm_rangeSelectedPartitions = bms_make_singleton(partitionSeq);
|
|
|
|
generateListFromPruningBM(pruningRes);
|
|
if (RelationIsSubPartitioned(rel)) {
|
|
SubPartitionPruningResult *subPartPruningRes = PreGetSubPartitionFullPruningResult(rel, partitionOid);
|
|
if (subPartPruningRes == NULL) {
|
|
return pruningRes;
|
|
}
|
|
subPartPruningRes->partSeq = partitionSeq;
|
|
pruningRes->ls_selectedSubPartitions = lappend(pruningRes->ls_selectedSubPartitions, subPartPruningRes);
|
|
}
|
|
return pruningRes;
|
|
}
|
|
|
|
/*
|
|
* @@GaussDB@@
|
|
* Target : data subpartition
|
|
* Brief : select * from subpartition (subpartition_name)
|
|
* Description : eliminate subpartitions which don't contain those tuple satisfy expression.
|
|
* return value : non-eliminated subpartitions.
|
|
*/
|
|
PruningResult* SingleSubPartitionPruningForRestrictInfo(Oid subPartitionOid, Relation rel, Oid partOid)
|
|
{
|
|
int partitionSeq = 0;
|
|
int subPartitionSeq = 0;
|
|
PruningResult* pruningRes = NULL;
|
|
|
|
if (!PointerIsValid(rel) || !OidIsValid(subPartitionOid) || !OidIsValid(partOid)) {
|
|
return NULL;
|
|
}
|
|
|
|
/* shouldn't happen */
|
|
if (!PointerIsValid(rel->partMap)) {
|
|
ereport(ERROR, (errmodule(MOD_OPT),
|
|
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
|
errmsg("relation of oid=\"%u\" is not partitioned table", rel->rd_id)));
|
|
}
|
|
pruningRes = makeNode(PruningResult);
|
|
pruningRes->state = PRUNING_RESULT_SUBSET;
|
|
|
|
partitionSeq = getPartitionElementsIndexByOid(rel, partOid);
|
|
/* In normal condition, it should never happen.
|
|
* But if the Query is from a view/rule contains a subpartition, this case may happen if the parent partition of
|
|
* this subpartition is dropped by DDL operation, such as DROP/TRUNCATE (UPDATE GLOBAL INDEX) */
|
|
if (partitionSeq < 0) {
|
|
ereport(ERROR,
|
|
(errmodule(MOD_OPT), errcode(ERRCODE_UNDEFINED_TABLE),
|
|
errmsg("fail to find partition with oid %u for partitioned table %u", partOid, rel->rd_id),
|
|
errdetail("this partition may have already been dropped by DDL operation"),
|
|
errhint("Check if this query contains a view that refrences a subpartition owned by the target partition. "
|
|
"If so, REBUILD this view.")));
|
|
}
|
|
|
|
Partition part = partitionOpen(rel, partOid, NoLock);
|
|
Relation partRel = partitionGetRelation(rel, part);
|
|
subPartitionSeq = getPartitionElementsIndexByOid(partRel, subPartitionOid);
|
|
/* In normal condition, it should never happen.
|
|
* But if the Query is from a view/rule contains a subpartition, this case may happen if the subpartition is dropped
|
|
* by DDL operation, such as DROP/SPLIT/MERGE/TRUNCATE (UPDATE GLOBAL INDEX)/EXCHANGE (UPDATE GLOBAL INDEX) */
|
|
if (subPartitionSeq < 0) {
|
|
ereport(ERROR,
|
|
(errmodule(MOD_OPT), errcode(ERRCODE_UNDEFINED_TABLE),
|
|
errmsg("fail to find subpartition with oid %u for partitioned table %u", subPartitionOid, rel->rd_id),
|
|
errdetail("this subpartition may have already been dropped by DDL operation"),
|
|
errhint("Check if this query contains a view that refrences the target subpartition. "
|
|
"If so, REBUILD this view.")));
|
|
}
|
|
releaseDummyRelation(&partRel);
|
|
partitionClose(rel, part, NoLock);
|
|
|
|
SubPartitionPruningResult *subPartPruningRes = makeNode(SubPartitionPruningResult);
|
|
subPartPruningRes->bm_selectedSubPartitions =
|
|
bms_add_member(subPartPruningRes->bm_selectedSubPartitions, subPartitionSeq);
|
|
subPartPruningRes->ls_selectedSubPartitions =
|
|
lappend_int(subPartPruningRes->ls_selectedSubPartitions, subPartitionSeq);
|
|
subPartPruningRes->partSeq = partitionSeq;
|
|
pruningRes->ls_selectedSubPartitions = lappend(pruningRes->ls_selectedSubPartitions, subPartPruningRes);
|
|
pruningRes->ls_rangeSelectedPartitions = lappend_int(pruningRes->ls_rangeSelectedPartitions, partitionSeq);
|
|
pruningRes->bm_rangeSelectedPartitions = bms_make_singleton(partitionSeq);
|
|
|
|
return pruningRes;
|
|
}
|
|
|
|
/*
|
|
* @@GaussDB@@
|
|
* Brief
|
|
* Description : eliminate partitions which don't contain those tuple satisfy expression.
|
|
* return value: non-eliminated partitions.
|
|
*/
|
|
PruningResult* partitionPruningForExpr(PlannerInfo* root, RangeTblEntry* rte, Relation rel, Expr* expr)
|
|
{
|
|
PruningContext* context = NULL;
|
|
PruningResult* result = NULL;
|
|
|
|
if (!PointerIsValid(rel) || !PointerIsValid(rte) || !PointerIsValid(expr)) {
|
|
ereport(ERROR, (errmodule(MOD_OPT),
|
|
errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
|
errmsg("partitionPruningForExpr: parameter can not be null")));
|
|
}
|
|
|
|
context = (PruningContext*)palloc0(sizeof(PruningContext));
|
|
context->root = root;
|
|
context->relation = rel;
|
|
context->estate = NULL;
|
|
context->rte = rte;
|
|
context->GetPartitionMap = GetRelPartitionMap;
|
|
context->pruningType = PruningPartition;
|
|
|
|
bool isnull = PartExprKeyIsNull(rel, NULL);
|
|
if (isnull) {
|
|
if (rel->partMap != NULL && (rel->partMap->type == PART_TYPE_LIST || rel->partMap->type == PART_TYPE_HASH)) {
|
|
// for List/Hash partitioned table
|
|
result = partitionEqualPruningWalker(rel->partMap->type, expr, context);
|
|
} else {
|
|
// for Range/Interval partitioned table
|
|
result = partitionPruningWalker(expr, context);
|
|
}
|
|
} else {
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
}
|
|
|
|
if (result->exprPart != NULL || result->paramArg != NULL) {
|
|
Param* paramArg = (Param *)copyObject(result->paramArg);
|
|
bool isPbeSinlePartition = result->isPbeSinlePartition;
|
|
destroyPruningResult(result);
|
|
result = getFullPruningResult(rel);
|
|
result->expr = expr;
|
|
result->paramArg = paramArg;
|
|
result->isPbeSinlePartition = isPbeSinlePartition;
|
|
return result;
|
|
}
|
|
/* Never happen, just to be self-contained */
|
|
if (!PointerIsValid(result)) {
|
|
ereport(ERROR, (errmodule(MOD_OPT),
|
|
errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("get null for partition pruning")));
|
|
}
|
|
|
|
partitionPruningFromBoundary(context, result);
|
|
if (PruningResultIsFull(result)) {
|
|
destroyPruningResult(result);
|
|
result = getFullPruningResult(rel);
|
|
}
|
|
|
|
if (PointerIsValid(context)) {
|
|
pfree_ext(context);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* @@GaussDB@@
|
|
* Brief
|
|
* Description : the hook function for expression walker function.
|
|
* If the expression node is AND,OR or OpExpr(>,>=,=,<=,<), this function would pruning,
|
|
* else get all partitions of partitioned table.
|
|
* return value: true if success, else false.
|
|
* This function will print log, if failed, however fill the PruningReslut with full set of partitions.
|
|
*/
|
|
PruningResult* partitionPruningWalker(Expr* expr, PruningContext* pruningCtx)
|
|
{
|
|
PruningResult* result = NULL;
|
|
|
|
switch (nodeTag(expr)) {
|
|
case T_BoolExpr: {
|
|
BoolExpr* boolExpr = NULL;
|
|
boolExpr = (BoolExpr*)expr;
|
|
result = partitionPruningFromBoolExpr(boolExpr, pruningCtx);
|
|
} break;
|
|
case T_OpExpr: {
|
|
OpExpr* opExpr = NULL;
|
|
opExpr = (OpExpr*)expr;
|
|
result = recordBoundaryFromOpExpr(opExpr, pruningCtx);
|
|
} break;
|
|
case T_NullTest: {
|
|
NullTest* ntExpr = NULL;
|
|
ntExpr = (NullTest*)expr;
|
|
result = partitionPruningFromNullTest(ntExpr, pruningCtx);
|
|
} break;
|
|
case T_ScalarArrayOpExpr: {
|
|
ScalarArrayOpExpr* arrayExpr = NULL;
|
|
arrayExpr = (ScalarArrayOpExpr*)expr;
|
|
result = partitionPruningFromScalarArrayOpExpr(arrayExpr, pruningCtx);
|
|
} break;
|
|
case T_Const: {
|
|
Const* cst = (Const*)expr;
|
|
if (BOOLOID == cst->consttype && !cst->constisnull && 0 == cst->constvalue) {
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_EMPTY;
|
|
} else {
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_FULL;
|
|
}
|
|
result->isPbeSinlePartition = false;
|
|
} break;
|
|
default: {
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
} break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static PruningResult* partitionEqualPruningWalker(PartitionType partType, Expr* expr, PruningContext* pruningCtx)
|
|
{
|
|
PruningResult* result = NULL;
|
|
|
|
AssertEreport(PointerIsValid(pruningCtx), MOD_OPT, "pruningCtx pointer is NULL.");
|
|
AssertEreport(PointerIsValid(expr), MOD_OPT, "expr pointer is NULL.");
|
|
|
|
switch (nodeTag(expr)) {
|
|
case T_BoolExpr: {
|
|
BoolExpr* boolExpr = NULL;
|
|
|
|
boolExpr = (BoolExpr*)expr;
|
|
|
|
result = partitionPruningFromBoolExpr(partType, boolExpr, pruningCtx);
|
|
} break;
|
|
case T_OpExpr: {
|
|
OpExpr* opExpr = NULL;
|
|
|
|
opExpr = (OpExpr*)expr;
|
|
|
|
result = recordEqualFromOpExpr(partType, opExpr, pruningCtx);
|
|
} break;
|
|
case T_NullTest: {
|
|
NullTest* ntExpr = NULL;
|
|
ntExpr = (NullTest*)expr;
|
|
result = partitionPruningFromNullTest(partType, ntExpr, pruningCtx);
|
|
} break;
|
|
case T_ScalarArrayOpExpr: {
|
|
ScalarArrayOpExpr* arrayExpr = NULL;
|
|
arrayExpr = (ScalarArrayOpExpr*)expr;
|
|
result = partitionPruningFromScalarArrayOpExpr(partType, arrayExpr, pruningCtx);
|
|
} break;
|
|
case T_Const: {
|
|
Const* cst = (Const*)expr;
|
|
if (BOOLOID == cst->consttype && !cst->constisnull && 0 == cst->constvalue) {
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_EMPTY;
|
|
} else {
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_FULL;
|
|
}
|
|
result->isPbeSinlePartition = false;
|
|
} break;
|
|
default: {
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
} break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* @@GaussDB@@
|
|
* Brief
|
|
* Description : If expression node is AND or OR, this function would be triggered.
|
|
* It will process left child and right child by call expression_tree_walker().
|
|
* return value: true if success, else false.
|
|
* This function will print log, if failed, however fill the PruningReslut with full set of partitions.
|
|
*/
|
|
static PruningResult* partitionPruningFromBoolExpr(const BoolExpr* expr, PruningContext* context)
|
|
{
|
|
Expr* arg = NULL;
|
|
List* resultList = NULL;
|
|
ListCell* cell = NULL;
|
|
PruningResult* result = NULL;
|
|
PruningResult* iterator = NULL;
|
|
|
|
if (context != NULL) {
|
|
AssertEreport(PointerIsValid(expr), MOD_OPT, "Pointer expr is NULL.");
|
|
}
|
|
|
|
if (expr->boolop == NOT_EXPR) {
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
|
|
foreach (cell, expr->args) {
|
|
arg = (Expr*)lfirst(cell);
|
|
iterator = partitionPruningWalker(arg, context);
|
|
|
|
if (iterator->paramArg != NULL || iterator->exprPart != NULL) {
|
|
return iterator;
|
|
}
|
|
resultList = lappend(resultList, iterator);
|
|
}
|
|
|
|
switch (expr->boolop) {
|
|
case AND_EXPR:
|
|
result = intersectChildPruningResult(resultList, context);
|
|
break;
|
|
case OR_EXPR:
|
|
result = unionChildPruningResult(resultList, context);
|
|
result->isPbeSinlePartition = false;
|
|
break;
|
|
case NOT_EXPR:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
destroyPruningResultList(resultList);
|
|
|
|
return result;
|
|
}
|
|
|
|
static PruningResult* partitionPruningFromBoolExpr(PartitionType partType, const BoolExpr* expr,
|
|
PruningContext* context)
|
|
{
|
|
Expr* arg = NULL;
|
|
List* resultList = NULL;
|
|
ListCell* cell = NULL;
|
|
PruningResult* result = NULL;
|
|
PruningResult* iterator = NULL;
|
|
|
|
AssertEreport(PointerIsValid(expr), MOD_OPT, "Pointer expr is NULL.");
|
|
AssertEreport(PointerIsValid(context), MOD_OPT, "Pointer context is NULL.");
|
|
AssertEreport(PointerIsValid(context->relation), MOD_OPT, "Pointer context->relation is NULL.");
|
|
|
|
if (expr->boolop == NOT_EXPR) {
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
|
|
foreach (cell, expr->args) {
|
|
arg = (Expr*)lfirst(cell);
|
|
iterator = partitionEqualPruningWalker(partType, arg, context);
|
|
|
|
if (iterator->paramArg != NULL || iterator->exprPart != NULL) {
|
|
return iterator;
|
|
}
|
|
resultList = lappend(resultList, iterator);
|
|
}
|
|
|
|
switch (expr->boolop) {
|
|
case AND_EXPR:
|
|
result = intersectChildPruningResult(resultList, context);
|
|
break;
|
|
case OR_EXPR:
|
|
result = unionChildPruningResult(resultList, context);
|
|
result->isPbeSinlePartition = false;
|
|
break;
|
|
case NOT_EXPR:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
destroyPruningResultList(resultList);
|
|
|
|
return result;
|
|
}
|
|
|
|
static PruningResult* partitionPruningFromNullTest(NullTest* expr, PruningContext* context)
|
|
{
|
|
PruningResult* result = NULL;
|
|
RangePartitionMap* partMap = NULL;
|
|
int partKeyNum = 0;
|
|
int attrOffset = 0;
|
|
Expr* arg = NULL;
|
|
Var* var = NULL;
|
|
RangeElement range;
|
|
|
|
AssertEreport(PointerIsValid(expr), MOD_OPT, "Pointer expr is NULL.");
|
|
|
|
arg = expr->arg;
|
|
|
|
result = makeNode(PruningResult);
|
|
|
|
if (T_RelabelType == nodeTag(arg)) {
|
|
arg = ((RelabelType*)arg)->arg;
|
|
}
|
|
|
|
if (T_Var != nodeTag(arg)) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
return result;
|
|
}
|
|
|
|
var = (Var*)arg;
|
|
|
|
/* Var's column MUST belongs to parition key columns */
|
|
partMap = (RangePartitionMap*)(GetPartitionMap(context));
|
|
partKeyNum = partMap->partitionKey->dim1;
|
|
|
|
attrOffset = varIsInPartitionKey(var->varattno, partMap->partitionKey, partKeyNum);
|
|
if (attrOffset != 0) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
return result;
|
|
}
|
|
|
|
if (expr->nulltesttype != IS_NULL) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
return result;
|
|
}
|
|
|
|
if (PartitionMapIsInterval(partMap)) {
|
|
result->state = PRUNING_RESULT_EMPTY;
|
|
return result;
|
|
}
|
|
|
|
range = partMap->rangeElements[partMap->rangeElementsNum - 1];
|
|
|
|
if (!(range.boundary[0]->ismaxvalue)) {
|
|
result->state = PRUNING_RESULT_EMPTY;
|
|
return result;
|
|
}
|
|
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
result->isPbeSinlePartition = true;
|
|
|
|
result->bm_rangeSelectedPartitions = bms_make_singleton(partMap->rangeElementsNum - 1);
|
|
|
|
return result;
|
|
}
|
|
|
|
static PruningResult* partitionPruningFromNullTest(PartitionType partType, NullTest* expr, PruningContext* context)
|
|
{
|
|
PruningResult* result = NULL;
|
|
int partKeyNum = 0;
|
|
int attrOffset = 0;
|
|
Expr* arg = NULL;
|
|
Var* var = NULL;
|
|
|
|
AssertEreport(PointerIsValid(expr), MOD_OPT, "Pointer expr is NULL.");
|
|
|
|
arg = expr->arg;
|
|
|
|
result = makeNode(PruningResult);
|
|
|
|
if (T_RelabelType == nodeTag(arg)) {
|
|
arg = ((RelabelType*)arg)->arg;
|
|
}
|
|
|
|
if (T_Var != nodeTag(arg)) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
return result;
|
|
}
|
|
|
|
var = (Var*)arg;
|
|
|
|
/* Var's column MUST belongs to parition key columns */
|
|
if (partType == PART_TYPE_LIST) {
|
|
ListPartitionMap* listPartMap = (ListPartitionMap*)(context->relation->partMap);
|
|
partKeyNum = listPartMap->partitionKey->dim1;
|
|
attrOffset = varIsInPartitionKey(var->varattno, listPartMap->partitionKey, partKeyNum);
|
|
} else {
|
|
HashPartitionMap* hashPartMap = (HashPartitionMap*)(context->relation->partMap);
|
|
partKeyNum = hashPartMap->partitionKey->dim1;
|
|
attrOffset = varIsInPartitionKey(var->varattno, hashPartMap->partitionKey, partKeyNum);
|
|
}
|
|
if (attrOffset != 0) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
return result;
|
|
}
|
|
|
|
if (expr->nulltesttype == IS_NULL && partType == PART_TYPE_LIST) {
|
|
ListPartitionMap* listPartMap = (ListPartitionMap*)(context->relation->partMap);
|
|
bool hasDefault = false;
|
|
int defaultPartitionIndex = -1;
|
|
for (int i = 0; i < listPartMap->listElementsNum; i++) {
|
|
ListPartElement list = listPartMap->listElements[i];
|
|
if (list.boundary[0]->ismaxvalue) {
|
|
hasDefault = true;
|
|
defaultPartitionIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
if (hasDefault) {
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
result->isPbeSinlePartition = true;
|
|
result->bm_rangeSelectedPartitions = bms_make_singleton(defaultPartitionIndex);
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (expr->nulltesttype != IS_NULL) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
} else {
|
|
result->state = PRUNING_RESULT_EMPTY;
|
|
}
|
|
result->isPbeSinlePartition = false;
|
|
|
|
return result;
|
|
}
|
|
|
|
static PruningResult* intersectChildPruningResult(const List* resultList, PruningContext* context)
|
|
{
|
|
PruningResult* iteratorResult = NULL;
|
|
PruningBoundary* tempBoundary = NULL;
|
|
PruningResult* result = NULL;
|
|
Bitmapset* bmsRange = NULL;
|
|
int intervalOffset = -1;
|
|
ListCell* cell = NULL;
|
|
|
|
result = makeNode(PruningResult);
|
|
|
|
result->state = PRUNING_RESULT_FULL;
|
|
|
|
AssertEreport(resultList, MOD_OPT, "");
|
|
|
|
foreach (cell, resultList) {
|
|
iteratorResult = (PruningResult*)lfirst(cell);
|
|
|
|
AssertEreport(iteratorResult, MOD_OPT, "iteratorResult context is NNULL.");
|
|
if (iteratorResult->state == PRUNING_RESULT_EMPTY) {
|
|
result->state = PRUNING_RESULT_EMPTY;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
} else if (iteratorResult->state == PRUNING_RESULT_FULL) {
|
|
continue;
|
|
}
|
|
if (PruningResultIsFull(result)) {
|
|
result->boundary = copyBoundary(iteratorResult->boundary);
|
|
result->intervalOffset = iteratorResult->intervalOffset;
|
|
result->bm_rangeSelectedPartitions = bms_copy(iteratorResult->bm_rangeSelectedPartitions);
|
|
result->intervalSelectedPartitions = bms_copy(iteratorResult->intervalSelectedPartitions);
|
|
result->state = iteratorResult->state;
|
|
result->paramArg = (Param *)copyObject(iteratorResult->paramArg);
|
|
|
|
} else if (result != NULL) {
|
|
if (intervalOffset == -1 && iteratorResult->intervalOffset >= 0) {
|
|
intervalOffset = iteratorResult->intervalOffset;
|
|
}
|
|
|
|
if (intervalOffset >= 0 && iteratorResult->intervalOffset >= 0 &&
|
|
intervalOffset != iteratorResult->intervalOffset) {
|
|
ereport(ERROR, (errmodule(MOD_OPT),
|
|
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
|
errmsg("For every node in same expression, pruning result's intervalOffset MUST be same")));
|
|
}
|
|
|
|
if (result->bm_rangeSelectedPartitions == NULL) {
|
|
result->bm_rangeSelectedPartitions = bms_copy(iteratorResult->bm_rangeSelectedPartitions);
|
|
} else if (iteratorResult->bm_rangeSelectedPartitions != NULL) {
|
|
bmsRange =
|
|
bms_intersect(result->bm_rangeSelectedPartitions, iteratorResult->bm_rangeSelectedPartitions);
|
|
bms_free_ext(result->bm_rangeSelectedPartitions);
|
|
result->bm_rangeSelectedPartitions = bmsRange;
|
|
}
|
|
|
|
if (result->boundary == NULL) {
|
|
result->boundary = copyBoundary(iteratorResult->boundary);
|
|
} else {
|
|
tempBoundary = mergeBoundary(result->boundary, iteratorResult->boundary);
|
|
destroyPruningBoundary(result->boundary);
|
|
result->boundary = tempBoundary;
|
|
}
|
|
|
|
if (BoundaryIsEmpty(result->boundary)) {
|
|
result->state = PRUNING_RESULT_EMPTY;
|
|
result->isPbeSinlePartition = false;
|
|
break;
|
|
}
|
|
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
}
|
|
if (result->state != PRUNING_RESULT_EMPTY && iteratorResult->isPbeSinlePartition) {
|
|
result->isPbeSinlePartition = true;
|
|
}
|
|
}
|
|
|
|
if (PruningResultIsEmpty(result)) {
|
|
destroyPruningResult(result);
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_EMPTY;
|
|
result->isPbeSinlePartition = false;
|
|
result->intervalOffset = -1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static PruningResult* unionChildPruningResult(const List* resultList, PruningContext* context)
|
|
{
|
|
PruningResult* iteratorResult = NULL;
|
|
PruningResult* result = NULL;
|
|
Bitmapset* bmsRange = NULL;
|
|
Bitmapset* bmsInterval = NULL;
|
|
int intervalOffset = -1;
|
|
ListCell* cell = NULL;
|
|
|
|
result = makeNode(PruningResult);
|
|
|
|
if (list_length(resultList) == 0) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
return result;
|
|
}
|
|
|
|
foreach (cell, resultList) {
|
|
iteratorResult = (PruningResult*)lfirst(cell);
|
|
if (!PointerIsValid(iteratorResult)) {
|
|
continue;
|
|
}
|
|
|
|
partitionPruningFromBoundary(context, iteratorResult);
|
|
|
|
if (iteratorResult->state == PRUNING_RESULT_EMPTY) {
|
|
continue;
|
|
} else if (iteratorResult->state == PRUNING_RESULT_FULL || iteratorResult->paramArg != NULL) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
return result;
|
|
} else {
|
|
if (intervalOffset == -1 && iteratorResult->intervalOffset >= 0) {
|
|
intervalOffset = iteratorResult->intervalOffset;
|
|
}
|
|
|
|
if (intervalOffset >= 0 && iteratorResult->intervalOffset >= 0 &&
|
|
intervalOffset != iteratorResult->intervalOffset) {
|
|
ereport(ERROR,
|
|
(errmodule(MOD_OPT),
|
|
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
|
errmsg("For every node in same expression, pruning result's intervalOffset MUST be same")));
|
|
}
|
|
|
|
bmsRange = bms_union(result->bm_rangeSelectedPartitions, iteratorResult->bm_rangeSelectedPartitions);
|
|
bms_free_ext(result->bm_rangeSelectedPartitions);
|
|
result->bm_rangeSelectedPartitions = bmsRange;
|
|
|
|
if (intervalOffset >= 0) {
|
|
bmsInterval = bms_union(result->intervalSelectedPartitions, iteratorResult->intervalSelectedPartitions);
|
|
|
|
bms_free_ext(result->intervalSelectedPartitions);
|
|
result->intervalSelectedPartitions = bmsInterval;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (bms_is_empty(result->intervalSelectedPartitions) && bms_is_empty(result->bm_rangeSelectedPartitions)) {
|
|
destroyPruningResult(result);
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_EMPTY;
|
|
result->intervalOffset = -1;
|
|
} else {
|
|
result->intervalOffset = intervalOffset;
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static PruningResult* partitionPruningFromScalarArrayOpExpr
|
|
(const ScalarArrayOpExpr* arrayExpr, PruningContext* pruningCtx)
|
|
{
|
|
OpExpr* expr = NULL;
|
|
Expr* larg = NULL;
|
|
Expr* rarg = NULL;
|
|
List* exprList = NULL;
|
|
PruningResult* result = NULL;
|
|
bool success = false;
|
|
|
|
AssertEreport(list_length(arrayExpr->args) == 2, MOD_OPT, "Expected two operands but get exception.");
|
|
|
|
larg = (Expr*)list_nth(arrayExpr->args, 0);
|
|
rarg = (Expr*)list_nth(arrayExpr->args, 1);
|
|
|
|
if (T_RelabelType == nodeTag(larg)) {
|
|
larg = ((RelabelType*)larg)->arg;
|
|
}
|
|
|
|
if (T_Var != nodeTag(larg) || (T_ArrayExpr != nodeTag(rarg) && T_Const != nodeTag(rarg))) {
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
|
|
if (T_ArrayExpr == nodeTag(rarg)) {
|
|
List* eleList = NULL;
|
|
ListCell* element = NULL;
|
|
|
|
eleList = ((ArrayExpr*)rarg)->elements;
|
|
|
|
foreach (element, eleList) {
|
|
Expr* eleExpr = (Expr*)lfirst(element);
|
|
List* eleArgs = NULL;
|
|
|
|
eleArgs = list_make2(copyObject(larg), copyObject(eleExpr));
|
|
|
|
expr = (OpExpr*)makeNode(OpExpr);
|
|
expr->args = eleArgs;
|
|
expr->inputcollid = arrayExpr->inputcollid;
|
|
expr->location = 0;
|
|
expr->opcollid = arrayExpr->opfuncid;
|
|
expr->opfuncid = arrayExpr->opfuncid;
|
|
expr->opno = arrayExpr->opno;
|
|
expr->opresulttype = BOOLOID;
|
|
expr->opretset = false;
|
|
|
|
exprList = lappend(exprList, expr);
|
|
}
|
|
|
|
if (arrayExpr->useOr) {
|
|
result = partitionPruningWalker(
|
|
(Expr*)makeBoolExpr(OR_EXPR, exprList, 0), pruningCtx);
|
|
} else {
|
|
result = partitionPruningWalker(
|
|
(Expr*)makeBoolExpr(AND_EXPR, exprList, 0), pruningCtx);
|
|
}
|
|
|
|
success = true;
|
|
} else if (T_Const == nodeTag(rarg)) {
|
|
Const* con = (Const*)rarg;
|
|
Oid eleType = get_element_type(con->consttype);
|
|
if (!OidIsValid(eleType))
|
|
ereport(
|
|
ERROR, (errmodule(MOD_OPT), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("invalid type. "))));
|
|
int16 typlen = get_typlen(eleType);
|
|
bool typbyval = get_typbyval(eleType);
|
|
|
|
/* Cannot be zero for an existing type */
|
|
AssertEreport(typlen, MOD_OPT, "Unexpected 0 type length.");
|
|
|
|
if (type_is_array(con->consttype) && PointerIsValid(con->constvalue) &&
|
|
ARR_NDIM((ArrayType*)con->constvalue) == 1) {
|
|
Datum value;
|
|
bool isnull = false;
|
|
Const* eleConst = NULL;
|
|
ArrayType* arrayValue = (ArrayType*)con->constvalue;
|
|
ArrayIterator itr = array_create_iterator(arrayValue, 0);
|
|
|
|
while (array_iterate(itr, &value, &isnull)) {
|
|
List* eleArgs = NULL;
|
|
|
|
eleConst = makeConst(eleType,
|
|
con->consttypmod,
|
|
con->constcollid,
|
|
typlen,
|
|
isnull ? PointerGetDatum(NULL) : value,
|
|
isnull,
|
|
typbyval);
|
|
eleArgs = list_make2(copyObject(larg), eleConst);
|
|
|
|
expr = (OpExpr*)makeNode(OpExpr);
|
|
expr->args = eleArgs;
|
|
expr->inputcollid = arrayExpr->inputcollid;
|
|
expr->location = 0;
|
|
expr->opcollid = arrayExpr->opfuncid;
|
|
expr->opfuncid = arrayExpr->opfuncid;
|
|
expr->opno = arrayExpr->opno;
|
|
expr->opresulttype = BOOLOID;
|
|
expr->opretset = false;
|
|
|
|
exprList = lappend(exprList, expr);
|
|
}
|
|
|
|
if (arrayExpr->useOr) {
|
|
result = partitionPruningWalker(
|
|
(Expr*)makeBoolExpr(OR_EXPR, exprList, 0), pruningCtx);
|
|
} else {
|
|
result = partitionPruningWalker(
|
|
(Expr*)makeBoolExpr(AND_EXPR, exprList, 0), pruningCtx);
|
|
}
|
|
success = true;
|
|
}
|
|
}
|
|
|
|
if (success) {
|
|
return result;
|
|
} else {
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
static PruningResult* partitionPruningFromScalarArrayOpExpr(PartitionType partType, const ScalarArrayOpExpr* arrayExpr,
|
|
PruningContext* pruningCtx)
|
|
{
|
|
OpExpr* expr = NULL;
|
|
Expr* larg = NULL;
|
|
Expr* rarg = NULL;
|
|
List* exprList = NULL;
|
|
PruningResult* result = NULL;
|
|
bool success = false;
|
|
|
|
AssertEreport(list_length(arrayExpr->args) == 2, MOD_OPT, "Expected two operands but get exception.");
|
|
|
|
larg = (Expr*)list_nth(arrayExpr->args, 0);
|
|
rarg = (Expr*)list_nth(arrayExpr->args, 1);
|
|
|
|
if (T_RelabelType == nodeTag(larg)) {
|
|
larg = ((RelabelType*)larg)->arg;
|
|
}
|
|
|
|
if (T_Var != nodeTag(larg) || (T_ArrayExpr != nodeTag(rarg) && T_Const != nodeTag(rarg))) {
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
|
|
if (T_ArrayExpr == nodeTag(rarg)) {
|
|
List* eleList = NULL;
|
|
ListCell* element = NULL;
|
|
|
|
eleList = ((ArrayExpr*)rarg)->elements;
|
|
|
|
foreach (element, eleList) {
|
|
Expr* eleExpr = (Expr*)lfirst(element);
|
|
List* eleArgs = NULL;
|
|
|
|
eleArgs = list_make2(copyObject(larg), copyObject(eleExpr));
|
|
|
|
expr = (OpExpr*)makeNode(OpExpr);
|
|
expr->args = eleArgs;
|
|
expr->inputcollid = arrayExpr->inputcollid;
|
|
expr->location = 0;
|
|
expr->opcollid = arrayExpr->opfuncid;
|
|
expr->opfuncid = arrayExpr->opfuncid;
|
|
expr->opno = arrayExpr->opno;
|
|
expr->opresulttype = BOOLOID;
|
|
expr->opretset = false;
|
|
|
|
exprList = lappend(exprList, expr);
|
|
}
|
|
|
|
if (arrayExpr->useOr) {
|
|
result = partitionEqualPruningWalker(partType, (Expr*)makeBoolExpr(OR_EXPR, exprList, 0), pruningCtx);
|
|
} else {
|
|
result = partitionEqualPruningWalker(partType, (Expr*)makeBoolExpr(AND_EXPR, exprList, 0), pruningCtx);
|
|
}
|
|
|
|
success = true;
|
|
} else if (T_Const == nodeTag(rarg)) {
|
|
Const* con = (Const*)rarg;
|
|
Oid eleType = get_element_type(con->consttype);
|
|
if (!OidIsValid(eleType))
|
|
ereport(
|
|
ERROR, (errmodule(MOD_OPT), (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), errmsg("invalid type. "))));
|
|
int16 typlen = get_typlen(eleType);
|
|
bool typbyval = get_typbyval(eleType);
|
|
|
|
/* Cannot be zero for an existing type */
|
|
AssertEreport(typlen, MOD_OPT, "Unexpected 0 type length.");
|
|
|
|
if (type_is_array(con->consttype) && PointerIsValid(con->constvalue) &&
|
|
ARR_NDIM((ArrayType*)con->constvalue) == 1) {
|
|
Datum value;
|
|
bool isnull = false;
|
|
Const* eleConst = NULL;
|
|
ArrayType* arrayValue = (ArrayType*)con->constvalue;
|
|
ArrayIterator itr = array_create_iterator(arrayValue, 0);
|
|
|
|
while (array_iterate(itr, &value, &isnull)) {
|
|
List* eleArgs = NULL;
|
|
|
|
eleConst = makeConst(eleType,
|
|
con->consttypmod,
|
|
con->constcollid,
|
|
typlen,
|
|
isnull ? PointerGetDatum(NULL) : value,
|
|
isnull,
|
|
typbyval);
|
|
eleArgs = list_make2(copyObject(larg), eleConst);
|
|
|
|
expr = (OpExpr*)makeNode(OpExpr);
|
|
expr->args = eleArgs;
|
|
expr->inputcollid = arrayExpr->inputcollid;
|
|
expr->location = 0;
|
|
expr->opcollid = arrayExpr->opfuncid;
|
|
expr->opfuncid = arrayExpr->opfuncid;
|
|
expr->opno = arrayExpr->opno;
|
|
expr->opresulttype = BOOLOID;
|
|
expr->opretset = false;
|
|
|
|
exprList = lappend(exprList, expr);
|
|
}
|
|
|
|
if (arrayExpr->useOr) {
|
|
result = partitionEqualPruningWalker(partType, (Expr*)makeBoolExpr(OR_EXPR, exprList, 0), pruningCtx);
|
|
} else {
|
|
result = partitionEqualPruningWalker(partType, (Expr*)makeBoolExpr(AND_EXPR, exprList, 0), pruningCtx);
|
|
}
|
|
success = true;
|
|
}
|
|
}
|
|
|
|
if (success) {
|
|
return result;
|
|
} else {
|
|
result = makeNode(PruningResult);
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
static Node* EvalExprValueWhenPruning(PruningContext* context, Node* node)
|
|
{
|
|
Node* result = NULL;
|
|
switch (context->pruningType) {
|
|
case PruningPartition:
|
|
result = estimate_expression_value(context->root, node, context->estate);
|
|
break;
|
|
case PruningSlice:
|
|
result = eval_const_expressions_params(NULL, node, context->boundParams);
|
|
break;
|
|
default:
|
|
Assert(false);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* @@GaussDB@@
|
|
* Brief
|
|
* Description : If expression node is OpExpr(>,>=,=,<=,<), this function would be triggered.
|
|
* This function fill PruningReslut's boundary with const from expression.
|
|
* return value: true if success, else false.
|
|
* This function will print log, if failed, however fill the PruningReslut with full set of partitions.
|
|
*/
|
|
static PruningResult* recordBoundaryFromOpExpr(const OpExpr* expr, PruningContext* context)
|
|
{
|
|
int partKeyNum = 0;
|
|
int attrOffset = -1;
|
|
char* opName = NULL;
|
|
Expr* leftArg = NULL;
|
|
Expr* rightArg = NULL;
|
|
|
|
Const* constArg = NULL;
|
|
Const* constMax = NULL;
|
|
Var* varArg = NULL;
|
|
RangePartitionMap* partMap = NULL;
|
|
PruningBoundary* boundary = NULL;
|
|
PruningResult* result = NULL;
|
|
bool rightArgIsConst = true;
|
|
Node* node = NULL;
|
|
Param* paramArg = NULL;
|
|
OpExpr* exprPart = NULL;
|
|
|
|
if (context != NULL) {
|
|
AssertEreport(PointerIsValid(context->relation), MOD_OPT, "Unexpected NULL pointer for context->relation.");
|
|
AssertEreport(PointerIsValid(GetPartitionMap(context)), MOD_OPT,
|
|
"Unexpected NULL pointer for context->relation->partMap.");
|
|
}
|
|
|
|
result = makeNode(PruningResult);
|
|
|
|
/* length of args MUST be 2 */
|
|
if (!PointerIsValid(expr) || list_length(expr->args) != 2 || !PointerIsValid(opName = get_opname(expr->opno))) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
|
|
|
|
leftArg = (Expr*)list_nth(expr->args, 0);
|
|
rightArg = (Expr*)list_nth(expr->args, 1);
|
|
|
|
/* In some case, there are several levels of relabel type */
|
|
while (leftArg && IsA(leftArg, RelabelType))
|
|
leftArg = ((RelabelType*)leftArg)->arg;
|
|
while (rightArg && IsA(rightArg, RelabelType))
|
|
rightArg = ((RelabelType*)rightArg)->arg;
|
|
|
|
if (leftArg == NULL || rightArg == NULL)
|
|
ereport(ERROR,
|
|
(errmodule(MOD_OPT),
|
|
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
(errmsg("Could not find enough valid args for Boundary From OpExpr"))));
|
|
|
|
if (IsA(leftArg, Var)) {
|
|
node = EvalExprValueWhenPruning(context, (Node*)rightArg);
|
|
if (node != NULL) {
|
|
rightArg = (Expr*)node;
|
|
}
|
|
} else if (IsA(rightArg, Var)) {
|
|
node = EvalExprValueWhenPruning(context, (Node*)leftArg);
|
|
if (node != NULL) {
|
|
leftArg = (Expr*)node;
|
|
}
|
|
}
|
|
|
|
/* one of args MUST be Const, and another argument Must be Var */
|
|
/* Be const or param, for PBE */
|
|
if (!(((T_Const == nodeTag(leftArg) || T_Param == nodeTag(leftArg)
|
|
|| T_OpExpr == nodeTag(leftArg)) && T_Var == nodeTag(rightArg)) ||
|
|
((T_Const == nodeTag(rightArg) || T_Param == nodeTag(rightArg)
|
|
|| T_OpExpr == nodeTag(rightArg)) && T_Var == nodeTag(leftArg)))) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
|
|
if (T_Var == nodeTag(rightArg)) {
|
|
if (T_Const == nodeTag(leftArg)) {
|
|
constArg = (Const*)leftArg;
|
|
} else if (T_Param == nodeTag(leftArg)) {
|
|
paramArg = (Param*)leftArg;
|
|
} else {
|
|
exprPart = (OpExpr*)leftArg;
|
|
}
|
|
varArg = (Var*)rightArg;
|
|
rightArgIsConst = false;
|
|
} else {
|
|
if (T_Const == nodeTag(rightArg)) {
|
|
constArg = (Const*)rightArg;
|
|
} else if (T_Param == nodeTag(rightArg)) {
|
|
paramArg = (Param*)rightArg;
|
|
} else {
|
|
exprPart = (OpExpr*)rightArg;
|
|
}
|
|
varArg = (Var*)leftArg;
|
|
}
|
|
|
|
/* Var MUST represents for current relation */
|
|
if (context->pruningType == PruningPartition) {
|
|
if (context->rte != NULL &&
|
|
context->rte->relid != context->relation->rd_id) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
} else {
|
|
/* list/range distributed table slice pruning */
|
|
if (varArg->varlevelsup != 0 ||
|
|
varArg->varno != context->varno ||
|
|
paramArg != NULL ||
|
|
exprPart != NULL) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
/* Var's column MUST belongs to parition key columns */
|
|
partMap = (RangePartitionMap*)(GetPartitionMap(context));
|
|
|
|
partKeyNum = partMap->partitionKey->dim1;
|
|
attrOffset = varIsInPartitionKey(varArg->varattno, partMap->partitionKey, partKeyNum);
|
|
if (attrOffset < 0) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
return result;
|
|
}
|
|
|
|
if (exprPart != NULL) {
|
|
if (!PartitionMapIsRange(partMap)) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
} else {
|
|
result->exprPart = exprPart;
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
} else if (paramArg != NULL) {
|
|
if (paramArg->paramkind != PARAM_EXTERN || !PartitionMapIsRange(partMap)) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
} else {
|
|
result->paramArg = paramArg;
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
if (0 == strcmp("=", opName)) {
|
|
result->isPbeSinlePartition = true;
|
|
}
|
|
return result;
|
|
}
|
|
}
|
|
|
|
if (constArg->constisnull) {
|
|
result->state = PRUNING_RESULT_EMPTY;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
|
|
/* initialize PruningBoundary */
|
|
result->boundary = makePruningBoundary(partKeyNum);
|
|
|
|
boundary = result->boundary;
|
|
result->isPbeSinlePartition = false;
|
|
|
|
/* decide the const is the top or bottom of boundary */
|
|
if ((0 == strcmp(">", opName) && rightArgIsConst) || (0 == strcmp("<", opName) && !rightArgIsConst)) {
|
|
if (constArg->constisnull) {
|
|
boundary->minClose[attrOffset] = false;
|
|
boundary->min[attrOffset] = PointerGetDatum(constArg);
|
|
boundary->state = PRUNING_RESULT_EMPTY;
|
|
result->state = PRUNING_RESULT_EMPTY;
|
|
} else {
|
|
boundary->minClose[attrOffset] = false;
|
|
boundary->min[attrOffset] = PointerGetDatum(constArg);
|
|
boundary->state = PRUNING_RESULT_SUBSET;
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
}
|
|
} else if ((0 == strcmp(">=", opName) && rightArgIsConst) || (0 == strcmp("<=", opName) && !rightArgIsConst)) {
|
|
boundary->minClose[attrOffset] = true;
|
|
boundary->min[attrOffset] = PointerGetDatum(constArg);
|
|
boundary->state = PRUNING_RESULT_SUBSET;
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
} else if (0 == strcmp("=", opName)) {
|
|
boundary->minClose[attrOffset] = true;
|
|
boundary->min[attrOffset] = PointerGetDatum(constArg);
|
|
boundary->maxClose[attrOffset] = true;
|
|
|
|
if (constArg->constisnull) {
|
|
boundary->max[attrOffset] = PointerGetDatum(constMax);
|
|
} else {
|
|
boundary->max[attrOffset] = PointerGetDatum(copyObject((void*)constArg));
|
|
}
|
|
|
|
boundary->state = PRUNING_RESULT_SUBSET;
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
result->isPbeSinlePartition = true;
|
|
} else if ((0 == strcmp("<=", opName) && rightArgIsConst) || (0 == strcmp(">=", opName) && !rightArgIsConst)) {
|
|
boundary->maxClose[attrOffset] = true;
|
|
boundary->max[attrOffset] = PointerGetDatum(constArg);
|
|
|
|
if (constArg->constisnull) {
|
|
boundary->state = PRUNING_RESULT_FULL;
|
|
result->state = PRUNING_RESULT_FULL;
|
|
} else {
|
|
boundary->state = PRUNING_RESULT_SUBSET;
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
}
|
|
} else if ((0 == strcmp("<", opName) && rightArgIsConst) || (0 == strcmp(">", opName) && !rightArgIsConst)) {
|
|
boundary->maxClose[attrOffset] = false;
|
|
boundary->max[attrOffset] = PointerGetDatum(constArg);
|
|
boundary->state = PRUNING_RESULT_SUBSET;
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
} else {
|
|
boundary->state = PRUNING_RESULT_FULL;
|
|
result->state = PRUNING_RESULT_FULL;
|
|
}
|
|
|
|
result->intervalOffset = -1;
|
|
|
|
return result;
|
|
}
|
|
|
|
static PruningResult* RecordEqualFromOpExprPart(const PartitionType partType, const PruningContext* context, const char* opName,
|
|
Const* constMax, const Var* varArg, int attrOffset, PruningResult* result, Param* paramArg,
|
|
OpExpr* exprPart, const Const* constArg, PruningBoundary* boundary)
|
|
{
|
|
int partKeyNum = 0;
|
|
|
|
/* Var's column MUST belongs to parition key columns */
|
|
if (partType == PART_TYPE_LIST) {
|
|
ListPartitionMap *partMap = (ListPartitionMap*)(context->relation->partMap);
|
|
partKeyNum = partMap->partitionKey->dim1;
|
|
attrOffset = varIsInPartitionKey(varArg->varattno, partMap->partitionKey, partKeyNum);
|
|
} else if (partType == PART_TYPE_HASH) {
|
|
HashPartitionMap *partMap = (HashPartitionMap*)(context->relation->partMap);
|
|
partKeyNum = partMap->partitionKey->dim1;
|
|
attrOffset = varIsInPartitionKey(varArg->varattno, partMap->partitionKey, partKeyNum);
|
|
}
|
|
|
|
if (attrOffset < 0) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
|
|
if (exprPart != NULL) {
|
|
result->exprPart = exprPart;
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
} else if (paramArg != NULL) {
|
|
result->paramArg = paramArg;
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
if (0 == strcmp("=", opName)) {
|
|
result->isPbeSinlePartition = true;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
if (constArg->constisnull) {
|
|
result->state = PRUNING_RESULT_EMPTY;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
|
|
/* initialize PruningBoundary */
|
|
result->boundary = makePruningBoundary(partKeyNum);
|
|
|
|
boundary = result->boundary;
|
|
|
|
/* decide the const is the top or bottom of boundary */
|
|
if (strcmp("=", opName) == 0) {
|
|
boundary->minClose[attrOffset] = true;
|
|
boundary->min[attrOffset] = PointerGetDatum(constArg);
|
|
boundary->maxClose[attrOffset] = true;
|
|
|
|
if (constArg->constisnull) {
|
|
boundary->max[attrOffset] = PointerGetDatum(constMax);
|
|
} else {
|
|
boundary->max[attrOffset] = PointerGetDatum(copyObject((void*)constArg));
|
|
}
|
|
|
|
boundary->state = PRUNING_RESULT_SUBSET;
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
result->isPbeSinlePartition = true;
|
|
} else {
|
|
boundary->state = PRUNING_RESULT_FULL;
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
}
|
|
|
|
result->intervalOffset = -1;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
static PruningResult* recordEqualFromOpExpr(PartitionType partType, const OpExpr* expr, PruningContext* context)
|
|
{
|
|
int attrOffset = -1;
|
|
char* opName = NULL;
|
|
Expr* leftArg = NULL;
|
|
Expr* rightArg = NULL;
|
|
Const* constArg = NULL;
|
|
Const* constMax = NULL;
|
|
Var* varArg = NULL;
|
|
PruningBoundary* boundary = NULL;
|
|
PruningResult* result = NULL;
|
|
bool rightArgIsConst = true;
|
|
Node* node = NULL;
|
|
Param* paramArg = NULL;
|
|
OpExpr* exprPart = NULL;
|
|
|
|
AssertEreport(PointerIsValid(context), MOD_OPT, "Unexpected NULL pointer for context.");
|
|
AssertEreport(PointerIsValid(context->relation), MOD_OPT, "Unexpected NULL pointer for context->relation.");
|
|
AssertEreport(
|
|
PointerIsValid(context->relation->partMap), MOD_OPT, "Unexpected NULL pointer for context->relation->partMap.");
|
|
|
|
result = makeNode(PruningResult);
|
|
|
|
/* length of args MUST be 2 */
|
|
if (!PointerIsValid(expr) || list_length(expr->args) != 2 || !PointerIsValid(opName = get_opname(expr->opno))) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
|
|
leftArg = (Expr*)list_nth(expr->args, 0);
|
|
rightArg = (Expr*)list_nth(expr->args, 1);
|
|
|
|
/* In some case, there are several levels of relabel type */
|
|
while (leftArg && IsA(leftArg, RelabelType))
|
|
leftArg = ((RelabelType*)leftArg)->arg;
|
|
while (rightArg && IsA(rightArg, RelabelType))
|
|
rightArg = ((RelabelType*)rightArg)->arg;
|
|
|
|
if (leftArg == NULL || rightArg == NULL)
|
|
ereport(ERROR,
|
|
(errmodule(MOD_OPT),
|
|
errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
(errmsg("Could not find enough valid args for Boundary From OpExpr"))));
|
|
|
|
if (IsA(leftArg, Var)) {
|
|
node = EvalExprValueWhenPruning(context, (Node*)rightArg);
|
|
if (node != NULL) {
|
|
rightArg = (Expr*)node;
|
|
}
|
|
} else if (IsA(rightArg, Var)) {
|
|
node = EvalExprValueWhenPruning(context, (Node*)leftArg);
|
|
if (node != NULL) {
|
|
leftArg = (Expr*)node;
|
|
}
|
|
}
|
|
|
|
/* one of args MUST be Const, and another argument Must be Var */
|
|
if (!(((T_Const == nodeTag(leftArg) || T_Param == nodeTag(leftArg)
|
|
|| T_OpExpr == nodeTag(leftArg)) && T_Var == nodeTag(rightArg)) ||
|
|
((T_Const == nodeTag(rightArg) || T_Param == nodeTag(rightArg)
|
|
|| T_OpExpr == nodeTag(rightArg)) && T_Var == nodeTag(leftArg)))) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
|
|
if (T_Var == nodeTag(rightArg)) {
|
|
if (T_Const == nodeTag(leftArg)) {
|
|
constArg = (Const*)leftArg;
|
|
} else if (T_Param == nodeTag(leftArg)) {
|
|
paramArg = (Param*)leftArg;
|
|
} else {
|
|
exprPart = (OpExpr*)leftArg;
|
|
}
|
|
varArg = (Var*)rightArg;
|
|
rightArgIsConst = false;
|
|
} else {
|
|
if (T_Const == nodeTag(rightArg)) {
|
|
constArg = (Const*)rightArg;
|
|
} else if (T_Param == nodeTag(rightArg)) {
|
|
paramArg = (Param*)rightArg;
|
|
} else {
|
|
exprPart = (OpExpr*)rightArg;
|
|
}
|
|
varArg = (Var*)leftArg;
|
|
}
|
|
|
|
/* Var MUST represents for current relation */
|
|
if (context->pruningType == PruningPartition) {
|
|
if (context->rte != NULL &&
|
|
context->rte->relid != context->relation->rd_id) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
} else {
|
|
/* list/range distributed table slice pruning */
|
|
if (varArg->varlevelsup != 0 || varArg->varno != context->varno) {
|
|
result->state = PRUNING_RESULT_FULL;
|
|
result->isPbeSinlePartition = false;
|
|
return result;
|
|
}
|
|
}
|
|
|
|
PruningResult* res = RecordEqualFromOpExprPart(partType, context, opName, constMax, varArg, attrOffset, result, paramArg, exprPart, constArg, boundary);
|
|
return res;
|
|
}
|
|
|
|
int varIsInPartitionKey(int attrNo, int2vector* partKeyAttrs, int partKeyNum)
|
|
{
|
|
int i = 0;
|
|
int nKeyColumn = 0;
|
|
char* cAttNums = NULL;
|
|
int16* attNums = NULL;
|
|
|
|
if (!PointerIsValid(partKeyAttrs) || attrNo <= 0) {
|
|
return -1;
|
|
}
|
|
|
|
nKeyColumn = ARR_DIMS(partKeyAttrs)[0];
|
|
|
|
AssertEreport(nKeyColumn == partKeyNum, MOD_OPT, "Partition key deminsion inconsistent.");
|
|
|
|
cAttNums = (char*)ARR_DATA_PTR(partKeyAttrs);
|
|
|
|
attNums = (int16*)cAttNums;
|
|
|
|
for (i = 0; i < nKeyColumn; i++) {
|
|
if (attNums[i] == attrNo) {
|
|
return i;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* @@GaussDB@@
|
|
* Brief
|
|
* Description : Get eliminated partitions according to boundary of PruningResult.
|
|
* Fill PruningResult bitmap attribute with bitmap for eliminated partitions
|
|
* return value: true if success, else false.
|
|
* if failed, This function will print log.
|
|
*/
|
|
int getRangeEnd(PruningContext* context)
|
|
{
|
|
int rangeEnd = -1;
|
|
PartitionMap* partMap = GetPartitionMap(context);
|
|
if (partMap->type == PART_TYPE_LIST) {
|
|
rangeEnd = ((ListPartitionMap*)partMap)->listElementsNum - 1;
|
|
} else if (partMap->type == PART_TYPE_HASH) {
|
|
rangeEnd = ((HashPartitionMap*)partMap)->hashElementsNum - 1;
|
|
} else {
|
|
rangeEnd = ((RangePartitionMap*)partMap)->rangeElementsNum - 1;
|
|
}
|
|
return rangeEnd;
|
|
}
|
|
|
|
void partitionPruningFromBoundary(PruningContext *context, PruningResult* pruningResult)
|
|
{
|
|
Const** bottomValue = NULL;
|
|
Const** topValue = NULL;
|
|
Bitmapset* rangeBms = NULL;
|
|
int rangeStart = -1;
|
|
int rangeEnd = -1;
|
|
int i = 0;
|
|
int compare = 0;
|
|
bool isTopClosed = true;
|
|
|
|
AssertEreport(PointerIsValid(context), MOD_OPT, "Unexpected NULL pointer for context.");
|
|
AssertEreport(PointerIsValid(pruningResult), MOD_OPT, "Unexpected NULL pointer for pruningResult.");
|
|
|
|
if (NoNeedPruning(pruningResult)) {
|
|
return;
|
|
}
|
|
|
|
bottomValue = (Const**)pruningResult->boundary->min;
|
|
topValue = (Const**)pruningResult->boundary->max;
|
|
|
|
for (i = 0; i < pruningResult->boundary->partitionKeyNum; i++) {
|
|
if (!PointerIsValid(topValue[i])) {
|
|
topValue[i] = makeMaxConst(InvalidOid, -1, InvalidOid);
|
|
}
|
|
|
|
if (!(pruningResult->boundary->maxClose[i])) {
|
|
isTopClosed = false;
|
|
}
|
|
}
|
|
|
|
compare = partitonKeyCompare(bottomValue, topValue, pruningResult->boundary->partitionKeyNum);
|
|
if (compare > 0) {
|
|
pruningResult->state = PRUNING_RESULT_EMPTY;
|
|
return;
|
|
}
|
|
|
|
// compare the bottom and the intervalMax, if the bottom is large than or equal than intervalMax, pruning result is
|
|
// empty.
|
|
if ((context->pruningType == PruningPartition) && (
|
|
(GetPartitionMap(context))->type == PART_TYPE_LIST ||
|
|
(GetPartitionMap(context))->type == PART_TYPE_HASH)) {
|
|
partitionRoutingForValueEqual(
|
|
context->relation, bottomValue, pruningResult->boundary->partitionKeyNum, true, u_sess->opt_cxt.bottom_seq);
|
|
u_sess->opt_cxt.top_seq->partArea = u_sess->opt_cxt.bottom_seq->partArea;
|
|
u_sess->opt_cxt.top_seq->partitionId = u_sess->opt_cxt.bottom_seq->partitionId;
|
|
u_sess->opt_cxt.top_seq->fileExist = u_sess->opt_cxt.bottom_seq->fileExist;
|
|
u_sess->opt_cxt.top_seq->partSeq = u_sess->opt_cxt.bottom_seq->partSeq;
|
|
} else {
|
|
partitionRoutingForValueRange(
|
|
context, bottomValue, pruningResult->boundary->partitionKeyNum, true, true, u_sess->opt_cxt.bottom_seq);
|
|
if (IsCleanPruningBottom(u_sess->opt_cxt.bottom_seq, pruningResult, bottomValue)) {
|
|
cleanPruningBottom(context, u_sess->opt_cxt.bottom_seq, bottomValue[0]);
|
|
}
|
|
|
|
partitionRoutingForValueRange(
|
|
context, topValue, pruningResult->boundary->partitionKeyNum, isTopClosed, false, u_sess->opt_cxt.top_seq);
|
|
if (IsCleanPruningTop(u_sess->opt_cxt.top_seq, pruningResult, topValue)) {
|
|
cleanPruningTop(context, u_sess->opt_cxt.top_seq, topValue[0]);
|
|
}
|
|
}
|
|
|
|
rangeEnd = getRangeEnd(context);
|
|
|
|
if (!PartitionLogicalExist(u_sess->opt_cxt.bottom_seq) && !PartitionLogicalExist(u_sess->opt_cxt.top_seq)) {
|
|
/* pruning failed or result contains all partition */
|
|
pruningResult->state = PRUNING_RESULT_EMPTY;
|
|
} else if (!PartitionLogicalExist(u_sess->opt_cxt.bottom_seq)) {
|
|
rangeStart = 0;
|
|
rangeEnd = u_sess->opt_cxt.top_seq->partSeq;
|
|
} else if (!PartitionLogicalExist(u_sess->opt_cxt.top_seq)) {
|
|
rangeStart = u_sess->opt_cxt.bottom_seq->partSeq;
|
|
} else {
|
|
rangeStart = u_sess->opt_cxt.bottom_seq->partSeq;
|
|
rangeEnd = u_sess->opt_cxt.top_seq->partSeq;
|
|
}
|
|
|
|
if (0 <= rangeStart) {
|
|
for (i = rangeStart; i <= rangeEnd; i++) {
|
|
rangeBms = bms_add_member(rangeBms, i);
|
|
}
|
|
|
|
if (PointerIsValid(pruningResult->bm_rangeSelectedPartitions)) {
|
|
Bitmapset* tempBms = NULL;
|
|
tempBms = bms_intersect(pruningResult->bm_rangeSelectedPartitions, rangeBms);
|
|
bms_free_ext(pruningResult->bm_rangeSelectedPartitions);
|
|
bms_free_ext(rangeBms);
|
|
pruningResult->bm_rangeSelectedPartitions = tempBms;
|
|
} else {
|
|
pruningResult->bm_rangeSelectedPartitions = rangeBms;
|
|
}
|
|
}
|
|
partitionFilter(context, pruningResult);
|
|
|
|
if (pruningResult->boundary) {
|
|
destroyPruningBoundary(pruningResult->boundary);
|
|
pruningResult->boundary = NULL;
|
|
}
|
|
}
|
|
|
|
/************************************************************************************
|
|
* pruning for multi-column partition key
|
|
************************************************************************************/
|
|
static void partitionFilter(PruningContext* context, PruningResult* pruningResult)
|
|
{
|
|
Bitmapset* resourceBms = NULL;
|
|
Bitmapset* result = NULL;
|
|
|
|
if (context->pruningType == PruningPartition) {
|
|
/* partition pruning */
|
|
if (!RELATION_IS_PARTITIONED(context->relation) || !PartitionMapIsRange(GetPartitionMap(context)) ||
|
|
pruningResult->boundary->partitionKeyNum == 1) {
|
|
return;
|
|
}
|
|
} else {
|
|
/* slice pruning */
|
|
if (!PartitionMapIsRange(GetPartitionMap(context)) ||
|
|
pruningResult->boundary->partitionKeyNum == 1) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
|
|
if (bms_is_empty(pruningResult->bm_rangeSelectedPartitions)) {
|
|
return;
|
|
}
|
|
|
|
resourceBms = bms_copy(pruningResult->bm_rangeSelectedPartitions);
|
|
|
|
while (true) {
|
|
int curPart;
|
|
|
|
curPart = bms_first_member(resourceBms);
|
|
if (curPart < 0) {
|
|
break;
|
|
}
|
|
|
|
if (!partitionShouldEliminated(context, curPart, pruningResult->boundary)) {
|
|
result = bms_add_member(result, curPart);
|
|
}
|
|
}
|
|
|
|
bms_free_ext(pruningResult->bm_rangeSelectedPartitions);
|
|
|
|
if (bms_is_empty(result)) {
|
|
pruningResult->bm_rangeSelectedPartitions = NULL;
|
|
} else {
|
|
pruningResult->bm_rangeSelectedPartitions = result;
|
|
}
|
|
|
|
bms_free_ext(resourceBms);
|
|
}
|
|
|
|
#define CONSTISMIN(value) ((value) == NULL || (value)->constisnull)
|
|
#define CONSTISMAX(value) ((value) != NULL && (value)->ismaxvalue)
|
|
|
|
static bool partitionShouldEliminated(PruningContext* context, int partSeq, PruningBoundary* boundary)
|
|
{
|
|
int i = 0;
|
|
PartKeyRange* partRange = NULL;
|
|
bool isSelected = true;
|
|
|
|
partRange = constructPartKeyRange(context, partSeq);
|
|
if (!PointerIsValid(partRange)) {
|
|
return false;
|
|
}
|
|
|
|
for (i = 0; i < partRange->num && isSelected; i++) {
|
|
PartKeyColumnRange* columnRange = &(partRange->columnRanges[i]);
|
|
int prevCompareMin = 0;
|
|
int prevCompareMax = 0;
|
|
int nextCompareMin = 0;
|
|
int nextCompareMax = 0;
|
|
bool frontSectionIntersected = false;
|
|
bool behindSectionIntersected = false;
|
|
|
|
switch (columnRange->mode) {
|
|
case PARTKEY_RANGE_MODE_POINT:
|
|
AssertEreport(0 == partitonKeyCompare(&(columnRange->prev), &(columnRange->next), 1) &&
|
|
!CONSTISMIN(columnRange->prev) && !CONSTISMAX(columnRange->prev),
|
|
MOD_OPT,
|
|
"");
|
|
|
|
prevCompareMin = partitonKeyCompare(&(columnRange->prev), ((Const**)(boundary->min + i)), 1);
|
|
prevCompareMax = partitonKeyCompare(&(columnRange->prev), ((Const**)(boundary->max + i)), 1);
|
|
if ((prevCompareMin > 0 && prevCompareMax < 0) || (prevCompareMin == 0 && boundary->minClose[i]) ||
|
|
(prevCompareMax == 0 && boundary->maxClose[i])) {
|
|
isSelected = true;
|
|
} else {
|
|
isSelected = false;
|
|
}
|
|
|
|
break;
|
|
|
|
case PARTKEY_RANGE_MODE_INCREASE:
|
|
case PARTKEY_RANGE_MODE_RANGE:
|
|
if ((CONSTISMIN(columnRange->prev) && CONSTISMIN((Const*)(boundary->min[i]))) ||
|
|
(CONSTISMAX(columnRange->next) && CONSTISMAX((Const*)(boundary->max[i])))) {
|
|
isSelected = true;
|
|
break;
|
|
}
|
|
|
|
prevCompareMin = partitonKeyCompare(&(columnRange->prev), ((Const**)(boundary->min + i)), 1);
|
|
prevCompareMax = partitonKeyCompare(&(columnRange->prev), ((Const**)(boundary->max + i)), 1);
|
|
nextCompareMin = partitonKeyCompare(&(columnRange->next), ((Const**)(boundary->min + i)), 1);
|
|
nextCompareMax = partitonKeyCompare(&(columnRange->next), ((Const**)(boundary->max + i)), 1);
|
|
if ((prevCompareMin > 0 && prevCompareMax < 0) || (prevCompareMin < 0 && nextCompareMin > 0) ||
|
|
(prevCompareMax == 0 && boundary->maxClose[i]) || (nextCompareMin == 0 && boundary->minClose[i]) ||
|
|
(prevCompareMin == 0) || (nextCompareMax == 0)) {
|
|
isSelected = true;
|
|
} else {
|
|
isSelected = false;
|
|
}
|
|
|
|
break;
|
|
|
|
case PARTKEY_RANGE_MODE_UNION:
|
|
AssertEreport(!CONSTISMIN(columnRange->prev) && !CONSTISMAX(columnRange->prev) &&
|
|
!CONSTISMIN(columnRange->next) && !CONSTISMAX(columnRange->next),
|
|
MOD_OPT,
|
|
"");
|
|
|
|
if (CONSTISMIN((Const*)(boundary->min[i])) || CONSTISMAX((Const*)(boundary->max[i]))) {
|
|
isSelected = true;
|
|
break;
|
|
}
|
|
|
|
nextCompareMin = partitonKeyCompare(&(columnRange->next), ((Const**)(boundary->min + i)), 1);
|
|
if (nextCompareMin > 0) {
|
|
frontSectionIntersected = true;
|
|
}
|
|
|
|
if (!frontSectionIntersected) {
|
|
prevCompareMax = partitonKeyCompare(&(columnRange->prev), ((Const**)(boundary->max + i)), 1);
|
|
if ((prevCompareMax < 0) || (prevCompareMax == 0 && boundary->maxClose[i])) {
|
|
behindSectionIntersected = true;
|
|
}
|
|
}
|
|
|
|
if (frontSectionIntersected || behindSectionIntersected) {
|
|
isSelected = true;
|
|
} else {
|
|
isSelected = false;
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
ereport(ERROR,
|
|
(errmodule(MOD_OPT),
|
|
(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
|
|
errmsg("unsupported partition key column range mode"))));
|
|
break;
|
|
}
|
|
}
|
|
|
|
pfree_ext(partRange);
|
|
|
|
return !isSelected;
|
|
}
|
|
|
|
static PartKeyRange* constructPartKeyRange(PruningContext* context, int partSeq)
|
|
{
|
|
RangeElement* prePartition = NULL;
|
|
RangeElement* nextPartition = NULL;
|
|
RangePartitionMap* partMap = NULL;
|
|
PartKeyRange* result = NULL;
|
|
Const* left = NULL;
|
|
Const* right = NULL;
|
|
int i = 0;
|
|
int partKeyNum = 0;
|
|
|
|
result = (PartKeyRange*)palloc0(sizeof(PartKeyRange));
|
|
|
|
partMap = (RangePartitionMap*)GetPartitionMap(context);
|
|
partKeyNum = partMap->partitionKey->dim1;
|
|
|
|
nextPartition = &(partMap->rangeElements[partSeq]);
|
|
|
|
if (partSeq == 0) {
|
|
result->num = 1;
|
|
result->columnRanges[0].mode = PARTKEY_RANGE_MODE_RANGE;
|
|
result->columnRanges[0].prev = NULL;
|
|
result->columnRanges[0].next = nextPartition->boundary[0];
|
|
|
|
return result;
|
|
}
|
|
|
|
prePartition = &(partMap->rangeElements[partSeq - 1]);
|
|
|
|
for (i = 0; i < partKeyNum; i++) {
|
|
bool typeIsNumerable = false;
|
|
PartKeyColumnRange* columnRange = NULL;
|
|
int compare = 0;
|
|
|
|
left = prePartition->boundary[i];
|
|
right = nextPartition->boundary[i];
|
|
|
|
if (left->consttype == INT2OID || left->consttype == INT4OID || left->consttype == INT8OID) {
|
|
typeIsNumerable = true;
|
|
} else {
|
|
typeIsNumerable = false;
|
|
}
|
|
|
|
compare = partitonKeyCompare(&left, &right, 1);
|
|
|
|
AssertEreport(compare <= 0, MOD_OPT, "");
|
|
|
|
columnRange = &(result->columnRanges[i]);
|
|
|
|
if (compare == 0) {
|
|
columnRange->mode = PARTKEY_RANGE_MODE_POINT;
|
|
columnRange->next = right;
|
|
columnRange->prev = left;
|
|
continue;
|
|
} else {
|
|
if (typeIsNumerable && !CONSTISMIN(left) && !CONSTISMAX(right) && right != NULL) {
|
|
int8 leftInt = (int8)left->constvalue;
|
|
int8 rightInt = (int8)right->constvalue;
|
|
|
|
if (leftInt + 1 == rightInt) {
|
|
columnRange->mode = PARTKEY_RANGE_MODE_INCREASE;
|
|
columnRange->prev = left;
|
|
columnRange->next = right;
|
|
result->num = i + 1;
|
|
if (i + 1 >= partKeyNum) {
|
|
break;
|
|
}
|
|
|
|
left = *(prePartition->boundary + i + 1);
|
|
right = *(nextPartition->boundary + i + 1);
|
|
|
|
columnRange = result->columnRanges + i + 1;
|
|
if (left->ismaxvalue) {
|
|
result->columnRanges[i].mode = PARTKEY_RANGE_MODE_POINT;
|
|
result->columnRanges[i].prev = result->columnRanges[i].next;
|
|
|
|
columnRange->mode = PARTKEY_RANGE_MODE_RANGE;
|
|
columnRange->prev = NULL;
|
|
columnRange->next = right;
|
|
result->num++;
|
|
} else if (partitonKeyCompare(&left, &right, 1) > 0) {
|
|
columnRange->mode = PARTKEY_RANGE_MODE_UNION;
|
|
columnRange->prev = left;
|
|
columnRange->next = right;
|
|
result->num++;
|
|
}
|
|
break;
|
|
} else {
|
|
columnRange->mode = PARTKEY_RANGE_MODE_RANGE;
|
|
columnRange->prev = left;
|
|
columnRange->next = right;
|
|
result->num = i + 1;
|
|
break;
|
|
}
|
|
} else {
|
|
columnRange->mode = PARTKEY_RANGE_MODE_RANGE;
|
|
columnRange->prev = left;
|
|
columnRange->next = right;
|
|
result->num = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static PruningBoundary* mergeBoundary(PruningBoundary* leftBoundary, PruningBoundary* rightBoundary)
|
|
{
|
|
int i = 0;
|
|
PruningBoundary* result = NULL;
|
|
Const* leftValue = NULL;
|
|
Const* rightValue = NULL;
|
|
|
|
if (!PointerIsValid(leftBoundary) && !PointerIsValid(rightBoundary)) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!PointerIsValid(leftBoundary)) {
|
|
return copyBoundary(leftBoundary);
|
|
}
|
|
|
|
if (!PointerIsValid(rightBoundary)) {
|
|
return copyBoundary(rightBoundary);
|
|
}
|
|
|
|
AssertEreport(leftBoundary->partitionKeyNum == rightBoundary->partitionKeyNum,
|
|
MOD_OPT,
|
|
"Expected two boundaries to have same number of partition key, run into exception.");
|
|
|
|
if (BoundaryIsFull(leftBoundary) || BoundaryIsEmpty(rightBoundary)) {
|
|
return copyBoundary(rightBoundary);
|
|
}
|
|
|
|
if (BoundaryIsFull(rightBoundary) || BoundaryIsEmpty(leftBoundary)) {
|
|
return copyBoundary(leftBoundary);
|
|
}
|
|
|
|
result = makePruningBoundary(leftBoundary->partitionKeyNum);
|
|
|
|
if (BoundaryIsEmpty(leftBoundary) || BoundaryIsEmpty(rightBoundary)) {
|
|
result->state = PRUNING_RESULT_EMPTY;
|
|
return result;
|
|
}
|
|
|
|
result->state = PRUNING_RESULT_SUBSET;
|
|
|
|
for (; i < result->partitionKeyNum; i++) {
|
|
int compare = 0;
|
|
|
|
/* merge bottom value */
|
|
leftValue = (Const*)DatumGetPointer(leftBoundary->min[i]);
|
|
rightValue = (Const*)DatumGetPointer(rightBoundary->min[i]);
|
|
if (!PointerIsValid(rightValue) && PointerIsValid(leftValue)) {
|
|
result->min[i] = PointerGetDatum(copyObject((void*)leftValue));
|
|
result->minClose[i] = leftBoundary->minClose[i];
|
|
} else if (!PointerIsValid(leftValue) && PointerIsValid(rightValue)) {
|
|
result->min[i] = PointerGetDatum(copyObject((void*)rightValue));
|
|
result->minClose[i] = rightBoundary->minClose[i];
|
|
} else if (PointerIsValid(leftValue) && PointerIsValid(rightValue)) {
|
|
compare = partitonKeyCompare(&leftValue, &rightValue, 1);
|
|
if (compare > 0) {
|
|
result->min[i] = PointerGetDatum(copyObject((void*)leftValue));
|
|
result->minClose[i] = leftBoundary->minClose[i];
|
|
} else if (compare < 0) {
|
|
result->min[i] = PointerGetDatum(copyObject((void*)rightValue));
|
|
result->minClose[i] = rightBoundary->minClose[i];
|
|
} else {
|
|
result->min[i] = PointerGetDatum(copyObject((void*)leftValue));
|
|
if (!leftBoundary->minClose[i] || !rightBoundary->minClose[i]) {
|
|
result->minClose[i] = false;
|
|
} else {
|
|
result->minClose[i] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* merge top value */
|
|
leftValue = (Const*)DatumGetPointer(leftBoundary->max[i]);
|
|
rightValue = (Const*)DatumGetPointer(rightBoundary->max[i]);
|
|
if (!PointerIsValid(rightValue) && PointerIsValid(leftValue)) {
|
|
result->max[i] = PointerGetDatum(copyObject((void*)leftValue));
|
|
result->maxClose[i] = leftBoundary->maxClose[i];
|
|
} else if (!PointerIsValid(leftValue) && PointerIsValid(rightValue)) {
|
|
result->max[i] = PointerGetDatum(copyObject((void*)rightValue));
|
|
result->maxClose[i] = rightBoundary->maxClose[i];
|
|
} else if (PointerIsValid(leftValue) && PointerIsValid(rightValue)) {
|
|
compare = partitonKeyCompare(&leftValue, &rightValue, 1);
|
|
if (compare > 0) {
|
|
result->max[i] = PointerGetDatum(copyObject((void*)rightValue));
|
|
result->maxClose[i] = rightBoundary->maxClose[i];
|
|
} else if (compare < 0) {
|
|
result->max[i] = PointerGetDatum(copyObject((void*)leftValue));
|
|
result->maxClose[i] = leftBoundary->maxClose[i];
|
|
} else {
|
|
result->max[i] = PointerGetDatum(copyObject((void*)leftValue));
|
|
if (!leftBoundary->maxClose[i] || !rightBoundary->maxClose[i]) {
|
|
result->maxClose[i] = false;
|
|
} else {
|
|
result->maxClose[i] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
leftValue = (Const*)DatumGetPointer(result->min[i]);
|
|
rightValue = (Const*)DatumGetPointer(result->max[i]);
|
|
if (leftValue != NULL && rightValue != NULL) {
|
|
compare = partitonKeyCompare(&leftValue, &rightValue, 1);
|
|
if (compare > 0 || (compare == 0 && !(result->minClose[i] && result->maxClose[i]))) {
|
|
result->state = PRUNING_RESULT_EMPTY;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void cleanPruningBottom(PruningContext *context, PartitionIdentifier* bottomSeq, Const* value)
|
|
{
|
|
int i = 0;
|
|
RangePartitionMap* partMap = NULL;
|
|
|
|
if (bottomSeq->partSeq < 0 || bottomSeq->partSeq >= ((RangePartitionMap*)GetPartitionMap(context))->rangeElementsNum ||
|
|
value == NULL) {
|
|
return;
|
|
}
|
|
|
|
incre_partmap_refcount(GetPartitionMap(context));
|
|
partMap = (RangePartitionMap*)(GetPartitionMap(context));
|
|
|
|
for (i = bottomSeq->partSeq; i < partMap->rangeElementsNum; i++) {
|
|
RangeElement* range = partMap->rangeElements + i;
|
|
int compare = 0;
|
|
|
|
compare = partitonKeyCompare(&value, range->boundary, 1);
|
|
if (compare >= 0) {
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i >= partMap->rangeElementsNum) {
|
|
i = -1;
|
|
}
|
|
|
|
bottomSeq->partSeq = i;
|
|
decre_partmap_refcount(GetPartitionMap(context));
|
|
}
|
|
|
|
static void cleanPruningTop(PruningContext *context, PartitionIdentifier* topSeq, Const* value)
|
|
{
|
|
int i = 0;
|
|
RangePartitionMap* partMap = NULL;
|
|
|
|
if (topSeq->partSeq < 0 || topSeq->partSeq >= ((RangePartitionMap*)GetPartitionMap(context))->rangeElementsNum ||
|
|
value == NULL) {
|
|
return;
|
|
}
|
|
|
|
incre_partmap_refcount(GetPartitionMap(context));
|
|
partMap = (RangePartitionMap*)(GetPartitionMap(context));
|
|
|
|
for (i = topSeq->partSeq; i >= 0; i--) {
|
|
RangeElement* range = partMap->rangeElements + i;
|
|
int compare = 0;
|
|
|
|
compare = partitonKeyCompare(&value, range->boundary, 1);
|
|
if (compare <= 0) {
|
|
continue;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i < topSeq->partSeq) {
|
|
i++;
|
|
}
|
|
|
|
topSeq->partSeq = i;
|
|
decre_partmap_refcount(GetPartitionMap(context));
|
|
}
|
|
|
|
void destroyPruningResult(PruningResult* pruningResult)
|
|
{
|
|
if (!PointerIsValid(pruningResult)) {
|
|
return;
|
|
}
|
|
|
|
if (PointerIsValid(pruningResult->bm_rangeSelectedPartitions)) {
|
|
bms_free_ext(pruningResult->bm_rangeSelectedPartitions);
|
|
pruningResult->bm_rangeSelectedPartitions = NULL;
|
|
}
|
|
|
|
if (PointerIsValid(pruningResult->intervalSelectedPartitions)) {
|
|
bms_free_ext(pruningResult->intervalSelectedPartitions);
|
|
pruningResult->intervalSelectedPartitions = NULL;
|
|
}
|
|
|
|
if (PointerIsValid(pruningResult->boundary)) {
|
|
destroyPruningBoundary(pruningResult->boundary);
|
|
pruningResult->boundary = NULL;
|
|
}
|
|
|
|
if (PointerIsValid(pruningResult->ls_rangeSelectedPartitions)) {
|
|
list_free_ext(pruningResult->ls_rangeSelectedPartitions);
|
|
pruningResult->ls_rangeSelectedPartitions = NIL;
|
|
}
|
|
if (PointerIsValid(pruningResult->expr)) {
|
|
pfree(pruningResult->expr);
|
|
pruningResult->expr = NULL;
|
|
}
|
|
if (PointerIsValid(pruningResult->exprPart)) {
|
|
pfree(pruningResult->exprPart);
|
|
pruningResult->exprPart = NULL;
|
|
}
|
|
if (PointerIsValid(pruningResult->paramArg)) {
|
|
pfree(pruningResult->paramArg);
|
|
pruningResult->paramArg = NULL;
|
|
}
|
|
if (PointerIsValid(pruningResult->partMap)) {
|
|
DestroyPartitionMap(pruningResult->partMap);
|
|
pruningResult->partMap = NULL;
|
|
}
|
|
|
|
pfree_ext(pruningResult);
|
|
}
|
|
|
|
static void destroyPruningResultList(List* resultList)
|
|
{
|
|
if (PointerIsValid(resultList)) {
|
|
ListCell* cell = NULL;
|
|
PruningResult* item = NULL;
|
|
foreach (cell, resultList) {
|
|
item = (PruningResult*)lfirst(cell);
|
|
if (PointerIsValid(item)) {
|
|
destroyPruningResult(item);
|
|
}
|
|
}
|
|
list_free_ext(resultList);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* @@GaussDB@@
|
|
* Target : data partition
|
|
* Brief : get PartitionIdentifier for special SN in pruning result
|
|
* Description :
|
|
* Notes : start with 0
|
|
*/
|
|
Oid getPartitionOidFromSequence(Relation relation, int partSeq, PartitionMap *oldmap)
|
|
{
|
|
/* if the partmap which copied before static pruning exists, it will replace the rel->partMap */
|
|
PartitionMap *partmap = oldmap ? oldmap : relation->partMap;
|
|
|
|
Oid result = InvalidOid;
|
|
AssertEreport(PointerIsValid(relation), MOD_OPT, "Unexpected NULL pointer for relation.");
|
|
AssertEreport(PointerIsValid(partmap), MOD_OPT, "Unexpected NULL pointer for relation->partMap.");
|
|
if (partmap->type == PART_TYPE_RANGE || partmap->type == PART_TYPE_INTERVAL) {
|
|
int rangeElementsNum = ((RangePartitionMap*)partmap)->rangeElementsNum;
|
|
if (partSeq < rangeElementsNum) {
|
|
result = ((RangePartitionMap*)partmap)->rangeElements[partSeq].partitionOid;
|
|
} else {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
errmsg("partSeq: %d out range of current relation partMap element num: %d.",
|
|
partSeq,
|
|
rangeElementsNum)));
|
|
}
|
|
/* do simple check, as rangeElements already be sorted */
|
|
if (partSeq > 0 &&
|
|
result == ((RangePartitionMap*)partmap)->rangeElements[partSeq - 1].partitionOid) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
errmsg("Duplicate range partition map oids: %u, please try again.", result)));
|
|
}
|
|
} else if (PART_TYPE_LIST == partmap->type) {
|
|
int listElementsNum = ((ListPartitionMap*)partmap)->listElementsNum;
|
|
if (partSeq < listElementsNum) {
|
|
result = ((ListPartitionMap*)partmap)->listElements[partSeq].partitionOid;
|
|
} else {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
errmsg("partSeq: %d out range of current relation partMap element num: %d.",
|
|
partSeq,
|
|
listElementsNum)));
|
|
}
|
|
/* do simple check, as rangeElements already be sorted */
|
|
if (partSeq > 0 &&
|
|
result == ((ListPartitionMap*)partmap)->listElements[partSeq - 1].partitionOid) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
errmsg("Duplicate range partition map oids: %u, please try again.", result)));
|
|
}
|
|
} else if (PART_TYPE_HASH == partmap->type) {
|
|
int hashElementsNum = ((HashPartitionMap*)partmap)->hashElementsNum;
|
|
if (partSeq < hashElementsNum) {
|
|
result = ((HashPartitionMap*)partmap)->hashElements[partSeq].partitionOid;
|
|
} else {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
|
|
errmsg("partSeq: %d out range of current relation partMap element num: %d.",
|
|
partSeq,
|
|
hashElementsNum)));
|
|
}
|
|
/* do simple check, as rangeElements already be sorted */
|
|
if (partSeq > 0 &&
|
|
result == ((HashPartitionMap*)partmap)->hashElements[partSeq - 1].partitionOid) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
|
errmsg("Duplicate range partition map oids: %u, please try again.", result)));
|
|
}
|
|
} else {
|
|
ereport(ERROR,
|
|
(errmodule(MOD_OPT),
|
|
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Unupport partition strategy \"%d\"", partmap->type)));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/*
|
|
* note that consts and constPointers are in and out parameter, and are provided by caller.
|
|
*/
|
|
void ConstructConstFromValues(Datum* datums, const bool* nulls, Oid* attrs, const int* colMap, int len,
|
|
Const* consts, Const** constPointers)
|
|
{
|
|
HeapTuple tp;
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
int col = colMap[i];
|
|
tp = SearchSysCache1((int)TYPEOID, ObjectIdGetDatum(attrs[col]));
|
|
if (HeapTupleIsValid(tp)) {
|
|
Form_pg_type typtup = (Form_pg_type)GETSTRUCT(tp);
|
|
consts[i].xpr.type = T_Const;
|
|
consts[i].consttype = attrs[col];
|
|
consts[i].consttypmod = typtup->typtypmod;
|
|
consts[i].constcollid = typtup->typcollation;
|
|
consts[i].constlen = typtup->typlen;
|
|
consts[i].constvalue = nulls[col] ? 0 : datums[col];
|
|
consts[i].constisnull = nulls[col];
|
|
consts[i].constbyval = typtup->typbyval;
|
|
consts[i].location = -1;
|
|
consts[i].ismaxvalue = false;
|
|
constPointers[i] = &consts[i];
|
|
|
|
ReleaseSysCache(tp);
|
|
} else {
|
|
ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("invalid type")));
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
SubPartitionPruningResult* GetSubPartitionPruningResult(List* selectedSubPartitions, int partSeq)
|
|
{
|
|
ListCell* cell = NULL;
|
|
foreach (cell, selectedSubPartitions) {
|
|
SubPartitionPruningResult* subPartPruningResult = (SubPartitionPruningResult*)lfirst(cell);
|
|
if (subPartPruningResult->partSeq == partSeq) {
|
|
return subPartPruningResult;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* @@GaussDB@@
|
|
* Brief : delete from partition (partition_name, subpartition_name, ...)
|
|
* Description : eliminate partitions and subpartitions which not in the RTE (sub)partitionOidList.
|
|
* return value : non-eliminated partitions and subpartitions.
|
|
*/
|
|
PruningResult* PartitionPruningForPartitionList(RangeTblEntry* rte, Relation rel)
|
|
{
|
|
Oid partOid;
|
|
Oid subpartOid;
|
|
PruningResult* pruningRes = NULL;
|
|
|
|
if (rte->partitionOidList->length == 1) {
|
|
partOid = list_nth_oid(rte->partitionOidList, 0);
|
|
if (rte->isContainSubPartition) {
|
|
subpartOid = list_nth_oid(rte->subpartitionOidList, 0);
|
|
return SingleSubPartitionPruningForRestrictInfo(subpartOid, rel, partOid);
|
|
} else {
|
|
return singlePartitionPruningForRestrictInfo(partOid, rel);
|
|
}
|
|
}
|
|
/* shouldn't happen */
|
|
if (!PointerIsValid(rel->partMap)) {
|
|
ereport(ERROR, (errmodule(MOD_OPT),
|
|
errcode(ERRCODE_OPTIMIZER_INCONSISTENT_STATE),
|
|
errmsg("relation of oid=\"%u\" is not partitioned table", rel->rd_id)));
|
|
}
|
|
|
|
pruningRes = makeNode(PruningResult);
|
|
pruningRes->state = PRUNING_RESULT_SUBSET;
|
|
MergePartitionListsForPruning(rte, rel, pruningRes);
|
|
return pruningRes;
|
|
} |