892 lines
29 KiB
C++
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;
|
|
}
|
|
|
|
}
|