Files
openGauss-server/src/gausskernel/runtime/executor/opfusion_scan.cpp
syj 23b2a58551 分区表代码优化。
1.修复分区表剪枝比例的计算。
  2.删除GetPartitionInfo函数中对传入的剪枝结果进行深拷贝的操作。
  3.优化分区表SQLByPass对分区IndexOid的获取。
2021-03-13 16:47:08 +08:00

892 lines
29 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.
* ---------------------------------------------------------------------------------------
*
* opfusion_scan.cpp
* The implementation for the scan operator of bypass executor.
*
* IDENTIFICATION
* src/gausskernel/runtime/executor/opfusion_scan.cpp
*
* ---------------------------------------------------------------------------------------
*/
#include "opfusion/opfusion_scan.h"
#include "catalog/pg_partition_fn.h"
#include "opfusion/opfusion.h"
#include "access/tableam.h"
#include "access/visibilitymap.h"
#include "executor/nodeIndexscan.h"
#include "optimizer/clauses.h"
#include "parser/parsetree.h"
#include "utils/lsyscache.h"
#include "utils/snapmgr.h"
#include "access/tableam.h"
ScanFusion::ScanFusion(ParamListInfo params, PlannedStmt* planstmt)
{
m_params = params;
m_planstmt = planstmt;
m_rel = NULL;
m_parentRel = NULL;
m_tupDesc = NULL;
m_reslot = NULL;
m_direction = NULL;
m_partRel = NULL;
};
ScanFusion* ScanFusion::getScanFusion(Node* node, PlannedStmt* planstmt, ParamListInfo params)
{
ScanFusion* scan = NULL;
switch (nodeTag(node)) {
case T_IndexScan:
scan = New(CurrentMemoryContext) IndexScanFusion((IndexScan*)node, planstmt, params);
break;
case T_IndexOnlyScan:
scan = New(CurrentMemoryContext) IndexOnlyScanFusion((IndexOnlyScan*)node, planstmt, params);
break;
default:
ereport(ERROR,
(errmodule(MOD_EXECUTOR),
errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unrecognized node type: %d when executing executor node.", (int)nodeTag(node))));
break;
}
return scan;
}
void ScanFusion::refreshParameter(ParamListInfo params)
{
m_params = params;
}
/* IndexFetchPart */
IndexFusion::IndexFusion(ParamListInfo params, PlannedStmt* planstmt) : ScanFusion(params, planstmt)
{
m_direction = NULL;
m_attrno = NULL;
m_targetList = NULL;
m_epq_indexqual = NULL;
m_tmpvals = NULL;
m_isnull = NULL;
m_values = NULL;
m_reloid = 0;
m_paramNum = 0;
m_tmpisnull = NULL;
m_keyNum = 0;
m_paramLoc = NULL;
m_scandesc = NULL;
m_scanKeys = NULL;
m_index = NULL;
m_parentRel = NULL;
m_partRel = NULL;
m_parentIndex = NULL;
m_partIndex = NULL;
m_keyInit = false;
}
void IndexFusion::refreshParameterIfNecessary()
{
for (int i = 0; i < m_paramNum; i++) {
m_scanKeys[m_paramLoc[i].scanKeyIndx].sk_argument = m_params->params[m_paramLoc[i].paramId - 1].value;
if (m_params->params[m_paramLoc[i].paramId - 1].isnull) {
m_scanKeys[m_paramLoc[i].scanKeyIndx].sk_flags |= SK_ISNULL;
}
}
}
void IndexFusion::BuildNullTestScanKey(Expr* clause, Expr* leftop, ScanKey this_scan_key)
{
/* indexkey IS NULL or indexkey IS NOT NULL */
NullTest* ntest = (NullTest*)clause;
int flags;
leftop = ntest->arg;
if (leftop != NULL && IsA(leftop, RelabelType))
leftop = ((RelabelType*)leftop)->arg;
Assert(leftop != NULL);
if (!(IsA(leftop, Var) && ((Var*)leftop)->varno == INDEX_VAR)) {
ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED), errmsg("NullTest indexqual has wrong key")));
}
AttrNumber varattno = ((Var*)leftop)->varattno;
/*
* initialize the scan key's fields appropriately
*/
switch (ntest->nulltesttype) {
case IS_NULL:
flags = SK_ISNULL | SK_SEARCHNULL;
break;
case IS_NOT_NULL:
flags = SK_ISNULL | SK_SEARCHNOTNULL;
break;
default:
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unrecognized nulltesttype: %d", (int)ntest->nulltesttype)));
flags = 0; /* keep compiler quiet */
break;
}
ScanKeyEntryInitialize(this_scan_key,
flags,
varattno, /* attribute number to scan */
InvalidStrategy, /* no strategy */
InvalidOid, /* no strategy subtype */
InvalidOid, /* no collation */
InvalidOid, /* no reg proc for this */
(Datum)0); /* constant */
}
void IndexFusion::IndexBuildScanKey(List* indexqual)
{
ListCell* qual_cell = NULL;
int i = 0;
foreach (qual_cell, indexqual) {
Expr* clause = (Expr*)lfirst(qual_cell);
ScanKey this_scan_key = &m_scanKeys[i++];
Oid opno; /* operator's OID */
RegProcedure opfuncid; /* operator proc id used in scan */
Oid opfamily; /* opfamily of index column */
int op_strategy; /* operator's strategy number */
Oid op_lefttype; /* operator's declared input types */
Oid op_righttype;
Expr* leftop = NULL; /* expr on lhs of operator */
Expr* rightop = NULL; /* expr on rhs ... */
AttrNumber varattno; /* att number used in scan */
if (IsA(clause, NullTest)) {
BuildNullTestScanKey(clause, leftop, this_scan_key);
continue;
}
Assert(IsA(clause, OpExpr));
/* indexkey op const or indexkey op expression */
uint32 flags = 0;
Datum scan_value;
opno = ((OpExpr*)clause)->opno;
opfuncid = ((OpExpr*)clause)->opfuncid;
/*
* leftop should be the index key Var, possibly relabeled
*/
leftop = (Expr*)get_leftop(clause);
if (leftop && IsA(leftop, RelabelType))
leftop = ((RelabelType*)leftop)->arg;
Assert(leftop != NULL);
if (!(IsA(leftop, Var) && ((Var*)leftop)->varno == INDEX_VAR)) {
ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED), errmsg("indexqual doesn't have key on left side")));
}
varattno = ((Var*)leftop)->varattno;
if (varattno < 1 || varattno > m_index->rd_index->indnatts) {
ereport(ERROR, (errcode(ERRCODE_INDEX_CORRUPTED), errmsg("bogus index qualification")));
}
/*
* We have to look up the operator's strategy number. This
* provides a cross-check that the operator does match the index.
*/
opfamily = m_index->rd_opfamily[varattno - 1];
get_op_opfamily_properties(opno, opfamily, false, &op_strategy, &op_lefttype, &op_righttype);
/*
* rightop is the constant or variable comparison value
*/
rightop = (Expr*)get_rightop(clause);
if (rightop != NULL && IsA(rightop, RelabelType)) {
rightop = ((RelabelType*)rightop)->arg;
}
Assert(rightop != NULL);
if (IsA(rightop, Const)) {
/* OK, simple constant comparison value */
scan_value = ((Const*)rightop)->constvalue;
if (((Const*)rightop)->constisnull) {
flags |= SK_ISNULL;
}
} else {
/* runtime refresh */
scan_value = 0;
}
/*
* initialize the scan key's fields appropriately
*/
ScanKeyEntryInitialize(this_scan_key,
flags,
varattno, /* attribute number to scan */
op_strategy, /* op's strategy */
op_righttype, /* strategy subtype */
((OpExpr*)clause)->inputcollid, /* collation */
opfuncid, /* reg proc to use */
scan_value); /* constant */
}
}
bool IndexFusion::EpqCheck(Datum* values, const bool* isnull)
{
ListCell* lc = NULL;
int i = 0;
AttrNumber att_num = 0;
foreach (lc, m_epq_indexqual) {
if (IsA(lfirst(lc), NullTest)) {
NullTest* nullexpr = (NullTest*)lfirst(lc);
NullTestType nulltype = nullexpr->nulltesttype;
Expr* leftop = nullexpr->arg;
if (leftop != NULL && IsA(leftop, RelabelType))
leftop = ((RelabelType*)leftop)->arg;
Assert(leftop != NULL);
att_num = ((Var*)leftop)->varattno - 1;
switch (nulltype) {
case IS_NULL:
if (isnull[att_num] == false) {
return false;
}
break;
case IS_NOT_NULL:
if (isnull[att_num] == true) {
return false;
}
break;
default:
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unrecognized nulltesttype: %d", (int)nulltype)));
break;
}
} else if (IsA(lfirst(lc), OpExpr)) {
OpExpr* opexpr = (OpExpr*)lfirst(lc);
Expr* leftop = (Expr*)linitial(opexpr->args);
if (leftop != NULL && IsA(leftop, RelabelType))
leftop = ((RelabelType*)leftop)->arg;
Assert(IsA(leftop, Var));
att_num = ((Var*)leftop)->varattno - 1;
if (OidFunctionCall2(opexpr->opfuncid, values[att_num], m_scanKeys[i].sk_argument) == false) {
return false;
}
} else {
Assert(0);
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unsupport bypass indexqual type: %d", (int)(((Node*)(lfirst(lc)))->type))));
}
i++;
}
return true;
}
void IndexFusion::setAttrNo()
{
ListCell* lc = NULL;
int cur_resno = 1;
foreach (lc, m_targetList) {
TargetEntry *res = (TargetEntry*)lfirst(lc);
if (res->resjunk) {
continue;
}
Var *var = (Var*)res->expr;
m_attrno[cur_resno - 1] = var->varattno;
cur_resno++;
}
}
void IndexFusion::UpdateCurrentRel(Relation* rel)
{
IndexScanDesc indexScan = GetIndexScanDesc(m_scandesc);
/* Just update rel for global partition index */
if (!RelationIsPartitioned(m_rel)) {
return;
}
if (indexScan->xs_gpi_scan != NULL) {
*rel = indexScan->xs_gpi_scan->fakePartRelation;
} else {
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("partitioned relation dose not use global partition index")));
}
}
/* init the Partition Oid in construct */
Oid GetRelOidForPartitionTable(Scan scan, const Relation rel, ParamListInfo params)
{
Oid relOid = InvalidOid;
if (params != NULL) {
Param* paramArg = scan.pruningInfo->paramArg;
relOid = GetPartitionOidByParam(rel, paramArg, &(params->params[paramArg->paramid - 1]));
} else {
Assert((list_length(scan.pruningInfo->ls_rangeSelectedPartitions) != 0));
int partId = lfirst_int(list_head(scan.pruningInfo->ls_rangeSelectedPartitions));
relOid = getPartitionOidFromSequence(rel, partId);
}
return relOid;
}
/* init the index in construct */
Relation InitPartitionIndexInFusion(Oid parentIndexOid, Oid partOid, Partition* partIndex, Relation* parentIndex, Relation rel)
{
*parentIndex = relation_open(parentIndexOid, AccessShareLock);
Oid partIndexOid;
if (list_length(rel->rd_indexlist) == 1) {
partIndexOid = lfirst_oid(rel->rd_indexlist->head);
} else {
partIndexOid = getPartitionIndexOid(parentIndexOid, partOid);
}
*partIndex = partitionOpen(*parentIndex, partIndexOid, AccessShareLock);
Relation index = partitionGetRelation(*parentIndex, *partIndex);
return index;
}
/* init the Partition in construct */
void InitPartitionRelationInFusion(Oid partOid, Relation parentRel, Partition* partRel, Relation* rel)
{
*partRel = partitionOpen(parentRel, partOid, AccessShareLock);
(void)PartitionGetPartIndexList(*partRel);
*rel = partitionGetRelation(parentRel, *partRel);
}
/* execute the process of done in construct */
static void ExeceDoneInIndexFusionConstruct(bool isPartTbl, Relation* parentRel, Partition* part,
Relation* index, Relation* rel)
{
if (isPartTbl) {
partitionClose(*parentRel, *part, AccessShareLock);
*part = NULL;
if (*index != NULL) {
releaseDummyRelation(index);
*index = NULL;
}
releaseDummyRelation(rel);
heap_close(*parentRel, AccessShareLock);
*parentRel = NULL;
*rel = NULL;
} else {
heap_close((*index == NULL) ? *rel : *index, AccessShareLock);
*index = NULL;
*rel = NULL;
}
}
/* IndexScanPart */
IndexScanFusion::IndexScanFusion(IndexScan* node, PlannedStmt* planstmt, ParamListInfo params)
: IndexFusion(params, planstmt)
{
m_isnull = NULL;
m_values = NULL;
m_epq_indexqual = NULL;
m_index = NULL;
m_tmpvals = NULL;
m_scandesc = NULL;
m_tmpisnull = NULL;
m_parentRel = NULL;
m_partRel = NULL;
m_node = node;
m_keyInit = false;
m_keyNum = list_length(node->indexqual);
m_scanKeys = (ScanKey)palloc0(m_keyNum * sizeof(ScanKeyData));
/* init params */
m_paramLoc = NULL;
m_paramNum = 0;
if (params != NULL) {
m_paramLoc = (ParamLoc*)palloc0(m_keyNum * sizeof(ParamLoc));
ListCell* lc = NULL;
int i = 0;
foreach (lc, node->indexqual) {
if (IsA(lfirst(lc), NullTest)) {
i++;
continue;
}
Assert(IsA(lfirst(lc), OpExpr));
OpExpr* opexpr = (OpExpr*)lfirst(lc);
Expr* var = (Expr*)lsecond(opexpr->args);
if (IsA(var, RelabelType)) {
var = ((RelabelType*)var)->arg;
}
if (IsA(var, Param)) {
Param* param = (Param*)var;
m_paramLoc[m_paramNum].paramId = param->paramid;
m_paramLoc[m_paramNum++].scanKeyIndx = i;
}
i++;
}
}
if (m_node->scan.isPartTbl) {
Oid parentRelOid = getrelid(m_node->scan.scanrelid, planstmt->rtable);
m_parentRel = heap_open(parentRelOid, AccessShareLock);
m_reloid = GetRelOidForPartitionTable(m_node->scan, m_parentRel, params);
InitPartitionRelationInFusion(m_reloid, m_parentRel, &m_partRel, &m_rel);
} else {
m_reloid = getrelid(m_node->scan.scanrelid, planstmt->rtable);
m_rel = heap_open(m_reloid, AccessShareLock);
}
m_targetList = m_node->scan.plan.targetlist;
m_direction = (ScanDirection*)palloc0(sizeof(ScanDirection));
Relation rel = m_rel;
m_tupDesc = ExecCleanTypeFromTL(m_targetList, false, rel->rd_tam_type);
m_attrno = (int16*)palloc(m_tupDesc->natts * sizeof(int16));
m_values = (Datum*)palloc(RelationGetDescr(rel)->natts * sizeof(Datum));
m_tmpvals = (Datum*)palloc(m_tupDesc->natts * sizeof(Datum));
m_isnull = (bool*)palloc(RelationGetDescr(rel)->natts * sizeof(bool));
m_tmpisnull = (bool*)palloc(m_tupDesc->natts * sizeof(bool));
setAttrNo();
Relation dummyIndex = NULL;
ExeceDoneInIndexFusionConstruct(m_node->scan.isPartTbl, &m_parentRel, &m_partRel, &dummyIndex, &m_rel);
}
void IndexScanFusion::Init(long max_rows)
{
if (m_node->scan.isPartTbl) {
/* get parent Relation */
Oid parent_relOid = getrelid(m_node->scan.scanrelid, m_planstmt->rtable);
m_parentRel = heap_open(parent_relOid, AccessShareLock);
m_reloid = GetRelOidForPartitionTable(m_node->scan, m_parentRel, m_params);
/* get partition relation */
InitPartitionRelationInFusion(m_reloid, m_parentRel, &m_partRel, &m_rel);
/* get partition index */
Oid parentIndexOid = m_node->indexid;
m_index = InitPartitionIndexInFusion(parentIndexOid, m_reloid, &m_partIndex, &m_parentIndex, m_rel);
} else {
m_rel = heap_open(m_reloid, AccessShareLock);
m_index = index_open(m_node->indexid, AccessShareLock);
}
if (unlikely(!m_keyInit)) {
IndexFusion::IndexBuildScanKey(m_node->indexqual);
m_keyInit = true;
}
if (m_params != NULL) {
refreshParameterIfNecessary();
}
*m_direction = ForwardScanDirection;
if (max_rows > 0) {
if (ScanDirectionIsBackward(m_node->indexorderdir)) {
*m_direction = BackwardScanDirection;
}
} else {
*m_direction = NoMovementScanDirection;
}
ScanState* scanstate = makeNode(ScanState); // need release
scanstate->ps.plan = (Plan *)m_node;
/*
* for hash bucket pruning with Global Plan cache, we should build a temp EState object
* to passing param info which is used to cal buckets in further hbkt_idx_beginscan.
*/
if (ENABLE_GPC && RELATION_CREATE_BUCKET(m_rel)) {
EState tmpstate;
tmpstate.es_param_list_info = m_params;
scanstate->ps.state = &tmpstate;
/* add scanstate pointer ? */
m_scandesc = scan_handler_idx_beginscan(m_rel, m_index, GetActiveSnapshot(), m_keyNum, 0, scanstate);
scan_handler_idx_rescan_local(m_scandesc, m_keyNum > 0 ? m_scanKeys : NULL, m_keyNum, NULL, 0);
scanstate->ps.state = NULL;
} else {
/* add scanstate pointer ? */
m_scandesc = scan_handler_idx_beginscan(m_rel, m_index, GetActiveSnapshot(), m_keyNum, 0, scanstate);
scan_handler_idx_rescan_local(m_scandesc, m_keyNum > 0 ? m_scanKeys : NULL, m_keyNum, NULL, 0);
}
if (m_scandesc) {
scan_handler_idx_rescan_local(m_scandesc,
m_keyNum > 0 ? m_scanKeys : NULL, m_keyNum, NULL, 0);
}
m_epq_indexqual = m_node->indexqualorig;
m_reslot = MakeSingleTupleTableSlot(m_tupDesc, false, m_rel->rd_tam_type);
}
HeapTuple IndexScanFusion::getTuple()
{
return scan_handler_idx_getnext(m_scandesc, *m_direction);
}
TupleTableSlot* IndexScanFusion::getTupleSlot()
{
do {
if (m_scandesc == NULL) {
return NULL;
}
Relation rel = m_rel;
HeapTuple tuple = getTuple();
if (tuple == NULL) {
return NULL;
}
IndexScanDesc indexScan = GetIndexScanDesc(m_scandesc);
tableam_tops_deform_tuple(tuple, RelationGetDescr(rel), m_values, m_isnull);
if (indexScan->xs_recheck && EpqCheck(m_values, m_isnull)) {
continue;
}
/* mapping */
for (int i = 0; i < m_tupDesc->natts; i++) {
Assert(m_attrno[i] > 0);
m_tmpvals[i] = m_values[m_attrno[i] - 1];
m_tmpisnull[i] = m_isnull[m_attrno[i] - 1];
}
Tuple tmptup = tableam_tops_form_tuple(m_tupDesc, m_tmpvals, m_tmpisnull, HEAP_TUPLE);
Assert(tmptup != NULL);
{
(void)ExecStoreTuple((HeapTuple)tmptup, /* tuple to store */
m_reslot, /* slot to store in */
InvalidBuffer, /* TO DO: survey */
false); /* don't pfree this pointer */
tableam_tslot_getsomeattrs(m_reslot, m_tupDesc->natts);
return m_reslot;
}
} while (1);
return NULL;
}
void IndexScanFusion::End(bool isCompleted)
{
if (!isCompleted)
return;
if (m_reslot != NULL) {
(void)ExecClearTuple(m_reslot);
m_reslot = NULL;
}
if (m_scandesc != NULL) {
scan_handler_idx_endscan(m_scandesc);
m_scandesc = NULL;
}
if (m_index != NULL) {
if (m_node->scan.isPartTbl) {
partitionClose(m_parentIndex, m_partIndex, AccessShareLock);
releaseDummyRelation(&m_index);
index_close(m_parentIndex, AccessShareLock);
} else {
index_close(m_index, AccessShareLock);
}
m_index = NULL;
}
if (m_rel != NULL) {
if (m_node->scan.isPartTbl) {
partitionClose(m_parentRel, m_partRel, AccessShareLock);
releaseDummyRelation(&m_rel);
heap_close(m_parentRel, AccessShareLock);
} else {
heap_close(m_rel, AccessShareLock);
}
m_rel = NULL;
}
}
/* index only scan section */
IndexOnlyScanFusion::IndexOnlyScanFusion(IndexOnlyScan* node, PlannedStmt* planstmt, ParamListInfo params)
: IndexFusion(params, planstmt)
{
m_isnull = NULL;
m_values = NULL;
m_epq_indexqual = NULL;
m_index = NULL;
m_tmpvals = NULL;
m_scandesc = NULL;
m_tmpisnull = NULL;
m_parentRel = NULL;
m_partRel = NULL;
m_node = node;
m_keyInit = false;
m_keyNum = list_length(node->indexqual);
m_scanKeys = (ScanKey)palloc0(m_keyNum * sizeof(ScanKeyData));
/* init params */
m_paramLoc = NULL;
m_paramNum = 0;
if (params != NULL) {
m_paramLoc = (ParamLoc*)palloc0(m_keyNum * sizeof(ParamLoc));
ListCell* lc = NULL;
int i = 0;
foreach (lc, node->indexqual) {
if (IsA(lfirst(lc), NullTest)) {
i++;
continue;
}
Assert(IsA(lfirst(lc), OpExpr));
OpExpr* opexpr = (OpExpr*)lfirst(lc);
Expr* var = (Expr*)lsecond(opexpr->args);
if (IsA(var, RelabelType)) {
var = ((RelabelType*)var)->arg;
}
if (IsA(var, Param)) {
Param* param = (Param*)var;
m_paramLoc[m_paramNum].paramId = param->paramid;
m_paramLoc[m_paramNum++].scanKeyIndx = i;
}
i++;
}
}
if (m_node->scan.isPartTbl) {
Oid parentRelOid = getrelid(m_node->scan.scanrelid, planstmt->rtable);
m_parentRel = heap_open(parentRelOid, AccessShareLock);
m_reloid = GetRelOidForPartitionTable(m_node->scan, m_parentRel, params);
/* get partition relation */
InitPartitionRelationInFusion(m_reloid, m_parentRel, &m_partRel, &m_rel);
/* get Partition index */
Oid parentIndexOid = m_node->indexid;
m_index = InitPartitionIndexInFusion(parentIndexOid, m_reloid, &m_partIndex, &m_parentIndex, m_rel);
} else {
m_reloid = getrelid(m_node->scan.scanrelid, planstmt->rtable);
m_index = index_open(m_node->indexid, AccessShareLock);
}
m_targetList = m_node->scan.plan.targetlist;
m_tupDesc = ExecCleanTypeFromTL(m_targetList, false);
m_reslot = MakeSingleTupleTableSlot(m_tupDesc);
m_direction = (ScanDirection*)palloc0(sizeof(ScanDirection));
m_VMBuffer = InvalidBuffer;
Relation rel = m_index;
m_attrno = (int16*)palloc(m_tupDesc->natts * sizeof(int16));
m_values = (Datum*)palloc(RelationGetDescr(rel)->natts * sizeof(Datum));
m_tmpvals = (Datum*)palloc(m_tupDesc->natts * sizeof(Datum));
m_isnull = (bool*)palloc(RelationGetDescr(rel)->natts * sizeof(bool));
m_tmpisnull = (bool*)palloc(m_tupDesc->natts * sizeof(bool));
setAttrNo();
ExeceDoneInIndexFusionConstruct(m_node->scan.isPartTbl, &m_parentRel, &m_partRel, &m_index, &m_rel);
if (m_node->scan.isPartTbl) {
partitionClose(m_parentIndex, m_partIndex, AccessShareLock);
index_close(m_parentIndex, AccessShareLock);
}
}
void IndexOnlyScanFusion::Init(long max_rows)
{
if (m_node->scan.isPartTbl) {
/* get parent Relation */
Oid parent_relOid = getrelid(m_node->scan.scanrelid, m_planstmt->rtable);
m_parentRel = heap_open(parent_relOid, AccessShareLock);
m_reloid = GetRelOidForPartitionTable(m_node->scan, m_parentRel, m_params);
/* get partition relation */
InitPartitionRelationInFusion(m_reloid, m_parentRel, &m_partRel, &m_rel);
/* get partition index */
Oid parentIndexOid = m_node->indexid;
m_index = InitPartitionIndexInFusion(parentIndexOid, m_reloid, &m_partIndex, &m_parentIndex, m_rel);
} else {
m_rel = heap_open(m_reloid, AccessShareLock);
m_index = index_open(m_node->indexid, AccessShareLock);
}
if (unlikely(!m_keyInit)) {
IndexFusion::IndexBuildScanKey(m_node->indexqual);
m_keyInit = true;
}
if (m_params != NULL) {
refreshParameterIfNecessary();
}
*m_direction = ForwardScanDirection;
if (max_rows > 0) {
if (ScanDirectionIsBackward(m_node->indexorderdir)) {
*m_direction = BackwardScanDirection;
}
} else {
*m_direction = NoMovementScanDirection;
}
m_reslot = MakeSingleTupleTableSlot(m_tupDesc, false, m_rel->rd_tam_type);
ScanState* scanstate = makeNode(ScanState); // need release
scanstate->ps.plan = (Plan *)m_node;
/*
* for hash bucket pruning with Global Plan cache, we should build a temp EState object
* to passing param info which is used to cal buckets in further hbkt_idx_beginscan.
*/
if (ENABLE_GPC && RELATION_CREATE_BUCKET(m_rel)) {
EState tmpstate;
tmpstate.es_param_list_info = m_params;
scanstate->ps.state = &tmpstate;
m_scandesc = scan_handler_idx_beginscan(m_rel, m_index, GetActiveSnapshot(), m_keyNum, 0, scanstate); // add scanstate pointer ? // add scanstate pointer ?
scanstate->ps.state = NULL;
} else {
m_scandesc = scan_handler_idx_beginscan(m_rel, m_index, GetActiveSnapshot(), m_keyNum, 0, scanstate); // add scanstate pointer ?
}
if (PointerIsValid(m_scandesc)) {
m_VMBuffer = InvalidBuffer;
IndexScanDesc indexdesc = GetIndexScanDesc(m_scandesc);
indexdesc->xs_want_itup = true;
}
scan_handler_idx_rescan_local(m_scandesc, m_keyNum > 0 ? m_scanKeys : NULL, m_keyNum, NULL, 0);
m_epq_indexqual = m_node->indexqual;
}
HeapTuple IndexOnlyScanFusion::getTuple()
{
/*
* You will never need this function cause select operator gets tuple through get
* TupleSlot, and the other operators like update, insert, and delete will directly
* change the values on heap, there is no need for them to get tuple from index.
*/
Assert(0);
return NULL;
}
TupleTableSlot* IndexOnlyScanFusion::getTupleSlot()
{
ItemPointer tid;
if (m_scandesc == NULL) {
return NULL;
}
Relation rel = m_index;
while ((tid = scan_handler_idx_getnext_tid(m_scandesc, *m_direction)) != NULL) {
HeapTuple tuple = NULL;
IndexScanDesc indexdesc = GetIndexScanDesc(m_scandesc);
if (IndexScanNeedSwitchPartRel(indexdesc)) {
/*
* Change the heapRelation in indexScanDesc to Partition Relation of current index
*/
if (!GPIGetNextPartRelation(indexdesc->xs_gpi_scan, CurrentMemoryContext, AccessShareLock)) {
continue;
}
indexdesc->heapRelation = indexdesc->xs_gpi_scan->fakePartRelation;
}
if (!visibilitymap_test(indexdesc->heapRelation, ItemPointerGetBlockNumber(tid), &m_VMBuffer)) {
tuple = index_fetch_heap(indexdesc);
if (tuple == NULL) {
continue; /* no visible tuple, try next index entry */
}
if (indexdesc->xs_continue_hot) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("non-MVCC snapshots are not supported in index-only scans")));
}
}
/*
* Fill the scan tuple slot with data from the index.
*/
IndexTuple tmptup = NULL;
index_deform_tuple(indexdesc->xs_itup, RelationGetDescr(rel), m_values, m_isnull);
if (indexdesc->xs_recheck && EpqCheck(m_values, m_isnull)) {
continue;
}
/* mapping */
for (int i = 0; i < m_tupDesc->natts; i++) {
Assert(m_attrno[i] > 0);
m_tmpvals[i] = m_values[m_attrno[i] - 1];
m_tmpisnull[i] = m_isnull[m_attrno[i] - 1];
}
tmptup = index_form_tuple(m_tupDesc, m_tmpvals, m_tmpisnull);
Assert(tmptup != NULL);
StoreIndexTuple(m_reslot, tmptup, m_tupDesc);
tableam_tslot_getsomeattrs(m_reslot, m_tupDesc->natts);
return m_reslot;
}
return NULL;
}
void IndexOnlyScanFusion::End(bool isCompleted)
{
if (m_VMBuffer != InvalidBuffer) {
ReleaseBuffer(m_VMBuffer);
m_VMBuffer = InvalidBuffer;
}
if (!isCompleted)
return;
if (m_scandesc != NULL) {
scan_handler_idx_endscan(m_scandesc);
m_scandesc = NULL;
}
if (m_index != NULL) {
if (m_node->scan.isPartTbl) {
partitionClose(m_parentIndex, m_partIndex, AccessShareLock);
releaseDummyRelation(&m_index);
index_close(m_parentIndex, AccessShareLock);
} else {
index_close(m_index, AccessShareLock);
}
m_index = NULL;
}
if (m_rel != NULL) {
if (m_node->scan.isPartTbl) {
partitionClose(m_parentRel, m_partRel, AccessShareLock);
releaseDummyRelation(&m_rel);
heap_close(m_parentRel, AccessShareLock);
} else {
heap_close(m_rel, AccessShareLock);
}
m_rel = NULL;
}
if (m_reslot != NULL) {
(void)ExecClearTuple(m_reslot);
m_reslot = NULL;
}
}