Files
openGauss-server/src/gausskernel/runtime/vecexecutor/vecnode/vectsstorescan.cpp
2023-03-12 19:16:08 -07:00

770 lines
30 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.
* ---------------------------------------------------------------------------------------
*
* vectsstorescan.cpp
* tsstore scan
*
* IDENTIFICATION
* Code/src/gausskernel/runtime/vecexecutor/vecnode/vectsstorescan.cpp
*
* ---------------------------------------------------------------------------------------
*/
#include "codegen/gscodegen.h"
#include "codegen/vecexprcodegen.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_partition_fn.h"
#include "catalog/pg_proc.h"
#include "vecexecutor/vectsstorescan.h"
#include "executor/executor.h"
#include "executor/node/nodeModifyTable.h"
#include "nodes/plannodes.h"
#include "nodes/nodeFuncs.h"
#include "utils/snapmgr.h"
#include "utils/lsyscache.h"
#include "utils/timestamp.h"
#include "vecexecutor/vecnodes.h"
#include "vecexecutor/vecexecutor.h"
#include "vecexecutor/vecnodecstorescan.h"
#include "access/tableam.h"
#include "optimizer/pruning.h"
#ifdef ENABLE_MULTIPLE_NODES
#include "tsdb/cache/queryid_cachemgr.h"
#include "tsdb/storage/ts_store_search.h"
#include "tsdb/common/ts_tablecmds.h"
#endif /* ENABLE_MULTIPLE_NODES */
extern bool CodeGenThreadObjectReady();
extern bool CodeGenPassThreshold(double rows, int dn_num, int dop);
#ifdef ENABLE_MULTIPLE_NODES
static void init_next_partition_for_tsstore_scan(TsStoreScanState* node);
static void init_tsstore_relation(TsStoreScanState* node, EState* estate);
static TsStoreScanState* build_tsstore_scan_state(TsStoreScan* node, EState* estate);
static void opt_orderby(TsStoreScan* node, TsStoreScanState* scanstate);
static LOCKMODE check_command_type(EState* estate);
#endif /* ENABLE_MULTIPLE_NODES */
#define TIMING_VECTSSTORE_SCAN(_node) (NULL != (_node)->ps.instrument && (_node)->ps.instrument->need_timer)
#define VECTSSTORE_SCAN_TRACE_START(_node, _desc_id) \
do { \
if (unlikely(TIMING_VECTSSTORE_SCAN(_node))) { \
TRACK_START((_node)->ps.plan->plan_node_id, (_desc_id)); \
} \
} while (0)
#define VECTSSTORE_SCAN_TRACE_END(_node, _desc_id) \
do { \
if (unlikely(TIMING_VECTSSTORE_SCAN(_node))) { \
TRACK_END((_node)->ps.plan->plan_node_id, (_desc_id)); \
} \
} while (0)
#ifdef ENABLE_MULTIPLE_NODES
/*
* Obtain current partitions, partition_parent, ScanDesc info.
*/
static void init_tsstore_relation(TsStoreScanState* node, EState* estate)
{
Relation currentRelation;
Relation currentPartRel = NULL;
TsStoreScan* plan = (TsStoreScan*)node->ps.plan;
bool isTargetRel = false;
LOCKMODE lockmode = AccessShareLock;
TableScanDesc currentScanDesc = NULL;
isTargetRel = ExecRelationIsTargetRelation(estate, plan->scanrelid);
/* get the relation object id from the relid'th entry in the range table */
currentRelation = ExecOpenScanRelation(estate, plan->scanrelid);
/* initiate partition list */
node->partitions = NULL;
if (!isTargetRel) {
lockmode = AccessShareLock;
} else {
lockmode = check_command_type(estate);
}
node->lockMode = lockmode;
if (plan->itrs > 0) {
Partition part = NULL;
Partition currentPart = NULL;
PruningResult* resultPlan = NULL;
if (plan->pruningInfo->expr != NULL) {
resultPlan = GetPartitionInfo(plan->pruningInfo, estate, currentRelation);
} else {
resultPlan = plan->pruningInfo;
}
ListCell* cell1 = NULL;
ListCell* cell2 = NULL;
List* part_seqs = resultPlan->ls_rangeSelectedPartitions;
List* partitionnos = resultPlan->ls_selectedPartitionnos;
Assert(list_length(part_seqs) == list_length(partitionnos));
/* partitions info is initialized */
forboth (cell1, part_seqs, cell2, partitionnos) {
Oid tablepartitionid = InvalidOid;
int partSeq = lfirst_int(cell1);
int partitionno = lfirst_int(cell2);
tablepartitionid = getPartitionOidFromSequence(currentRelation, partSeq, partitionno);
part = PartitionOpenWithPartitionno(currentRelation, tablepartitionid, partitionno, lockmode);
node->partitions = lappend(node->partitions, part);
}
if (resultPlan->ls_rangeSelectedPartitions != NULL) {
node->part_id = resultPlan->ls_rangeSelectedPartitions->length;
} else {
node->part_id = 0;
}
if (NULL == node->partitions)
ereport(ERROR,
(errmodule(MOD_VEC_EXECUTOR),
errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
errmsg("Fail to find partition from sequence.")));
/* construct HeapScanDesc for first partition */
currentPart = (Partition)list_nth(node->partitions, 0);
currentPartRel = partitionGetRelation(currentRelation, currentPart);
node->ss_currentPartition = currentPartRel;
/* add qual for redis */
if (u_sess->attr.attr_sql.enable_cluster_resize && RelationInRedistribute(currentPartRel)) {
List* new_qual = NIL;
new_qual = eval_ctid_funcs(currentPartRel, node->ps.plan->qual, &node->rangeScanInRedis);
node->ps.qual = (List*)ExecInitVecExpr((Expr*)new_qual, (PlanState*)&node->ps);
} else {
node->ps.qual = (List*)ExecInitVecExpr((Expr*)node->ps.plan->qual, (PlanState*)&node->ps);
}
currentScanDesc = tableam_scan_begin(currentPartRel, estate->es_snapshot, 0, NULL);
} else {
node->ss_currentPartition = NULL;
node->ps.qual = (List*)ExecInitVecExpr((Expr*)node->ps.plan->qual, (PlanState*)&node->ps);
}
node->ss_currentRelation = currentPartRel;
node->ss_partition_parent = currentRelation;
node->ss_currentScanDesc = currentScanDesc;
ExecAssignScanType(node, RelationGetDescr(currentRelation));
}
/*
* Obtain the time range in the query filter for timeseries table to which
* may facilitate subsequent CU query optimization.
*/
static TimeRange* get_time_range(TsStoreScanState* node)
{
ListCell *cell = NULL;
TimeRange* time_range = New(CurrentMemoryContext) TimeRange();
time_range->start_timestamp = 0;
time_range->end_timestamp = GetCurrentTimestamp();
foreach(cell, node->ps.plan->qual) {
if (nodeTag(lfirst(cell)) != T_OpExpr) {
continue;
}
ListCell *inner_cell = NULL;
Oid optr_oid = ((OpExpr*)lfirst(cell))->opno;
/* qualification of time less than end_timestamp */
if (optr_oid == TIMESTAMPLTOID || optr_oid == TIMESTAMPLEOID ||
optr_oid == TIMESTAMPTZLTOID || optr_oid == TIMESTAMPTZLEOID) {
TimestampTz end_timestamp = GetCurrentTimestamp();
foreach(inner_cell, ((OpExpr*)lfirst(cell))->args) {
Oid data_oid = ((Const*)lfirst(inner_cell))->consttype;
/*
* There is no need to distinguish timestamp and timestamptz, just deal with it as
* timestamptz because data processed in system or stored in backend(for timeseries is in CU)
* is still of type timestamptz.
*/
if (nodeTag(lfirst(inner_cell)) == T_Const && (data_oid == TIMESTAMPOID ||
data_oid == TIMESTAMPTZOID)) {
end_timestamp = ((Const*)lfirst(inner_cell))->constvalue;
} else if (nodeTag(lfirst(inner_cell)) == T_OpExpr &&
(((OpExpr*)lfirst(inner_cell))->opfuncid == TIMESTAMPTZMIINTERVALFUNCOID
|| ((OpExpr*)lfirst(inner_cell))->opfuncid == TIMESTAMPTZPLINTERVALFUNCOID)) {
OpExpr* expr = (OpExpr*)lfirst(inner_cell);
Expr* value = evaluate_expr((Expr*)expr, expr->opresulttype, -1, 0);
end_timestamp = DatumGetTimestampTz(((Const*)value)->constvalue);
}
time_range->end_timestamp =
end_timestamp < time_range->end_timestamp ? end_timestamp : time_range->end_timestamp;
}
/* qualification of time greater than start_timestamp */
} else if (optr_oid == TIMESTAMPGTOID || optr_oid == TIMESTAMPGEOID ||
optr_oid == TIMESTAMPTZGTOID || optr_oid == TIMESTAMPTZGEOID) {
TimestampTz start_timestamp = 0;
foreach(inner_cell, ((OpExpr*)lfirst(cell))->args) {
Oid data_oid = ((Const*)lfirst(inner_cell))->consttype;
/*
* There is no need to distinguish timestamp and timestamptz, just deal with it as
* timestamptz because data processed in system or stored in backend(for timeseries is in CU)
* is still of type timestamptz.
*/
if (nodeTag(lfirst(inner_cell)) == T_Const && (data_oid == TIMESTAMPOID ||
data_oid == TIMESTAMPTZOID)) {
start_timestamp = ((Const*)lfirst(inner_cell))->constvalue;
} else if (nodeTag(lfirst(inner_cell)) == T_OpExpr &&
(((OpExpr*)lfirst(inner_cell))->opfuncid == TIMESTAMPTZMIINTERVALFUNCOID
|| ((OpExpr*)lfirst(inner_cell))->opfuncid == TIMESTAMPTZPLINTERVALFUNCOID)) {
OpExpr* expr = (OpExpr*)lfirst(inner_cell);
Expr* value = evaluate_expr((Expr*)expr, expr->opresulttype, -1, 0);
start_timestamp = DatumGetTimestampTz(((Const*)value)->constvalue);
}
time_range->start_timestamp =
start_timestamp > time_range->start_timestamp ? start_timestamp : time_range->start_timestamp;
}
}
}
if (time_range->start_timestamp == 0 && time_range->end_timestamp == GetCurrentTimestamp()) {
delete time_range;
time_range = NULL;
}
return time_range;
}
static TsStoreScanState* build_tsstore_scan_state(TsStoreScan* node, EState* estate)
{
TsStoreScanState* scanstate = nullptr;
// Create state structure
scanstate = makeNode(TsStoreScanState);
scanstate->ps.plan = (Plan*)node;
scanstate->ps.state = estate;
scanstate->ps.vectorized = true;
scanstate->isPartTbl = node->isPartTbl;
scanstate->partScanDirection = node->partScanDirection;
scanstate->rangeScanInRedis = {false, 0, 0};
scanstate->sort_by_time_colidx = node->sort_by_time_colidx;
scanstate->has_sort = node->has_sort;
scanstate->limit = node->limit;
scanstate->scaned_tuples = 0;
scanstate->top_key_func_arg = node->top_key_func_arg;
scanstate->early_stop = false;
scanstate->is_simple_scan = node->is_simple_scan;
scanstate->tags_scan_done = true;
scanstate->first_scan = true;
scanstate->time_range = nullptr;
scanstate->ts_store_search = nullptr;
scanstate->tag_rows = nullptr;
scanstate->tag_id_num = 0;
scanstate->only_scan_tag = (node->series_func_calls > 0);
scanstate->ts_store_search = New(CurrentMemoryContext) TsStoreSearch();
scanstate->only_const_col = false;
if (!node->tablesample) {
scanstate->isSampleScan = false;
} else {
/* Current not support sample scan for tsdb table */
ereport(ERROR,
(errmodule(MOD_TIMESERIES), errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Un-support feature"),
errdetail("Current not support sample scan for tsdb table.")));
}
return scanstate;
}
static void init_next_partition_for_tsstore_scan(TsStoreScanState* node)
{
Partition currentpartition = NULL;
Relation currentpartitionrel = NULL;
TableScanDesc currentScanDesc = NULL;
int paramno = -1;
ParamExecData* param = NULL;
TsStoreScan* plan = NULL;
plan = (TsStoreScan*)node->ps.plan;
/* get partition sequnce */
paramno = plan->plan.paramno;
param = &(node->ps.state->es_param_exec_vals[paramno]);
node->currentSlot = (int)param->value;
/* construct HeapScanDesc for new partition */
currentpartition = (Partition)list_nth(node->partitions, node->currentSlot);
currentpartitionrel = partitionGetRelation(node->ss_partition_parent, currentpartition);
releaseDummyRelation(&(node->ss_currentPartition));
node->ss_currentPartition = currentpartitionrel;
node->ss_currentRelation = currentpartitionrel;
if (node->ts_store_search == NULL) {
node->ts_store_search = New(CurrentMemoryContext) TsStoreSearch();
}
node->ts_store_search->init_scan(node);
if (!node->isSampleScan) {
currentScanDesc = tableam_scan_begin(currentpartitionrel, node->ps.state->es_snapshot, 0, NULL);
} else {
/* Current not support sample scan for tsdb table */
ereport(ERROR,
(errmodule(MOD_TIMESERIES), errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Un-support feature"),
errdetail("Current not support sample scan for tsdb table.")));
}
/* update partition scan-related fileds in SeqScanState */
node->ss_currentScanDesc = currentScanDesc;
}
static void opt_orderby(TsStoreScan* node, TsStoreScanState* scanstate)
{
TupleDesc tuple_desc = RelationGetDescr(scanstate->ss_currentRelation);
TargetEntry* att_target = NULL;
List *target_list = node->plan.targetlist;
att_target = (TargetEntry*)list_nth(target_list, scanstate->sort_by_time_colidx - 1);
for(int i = 0; i < tuple_desc->natts; i++) {
if (IsA(att_target->expr, Var) && tuple_desc->attrs[i].attkvtype == ATT_KV_TIMETAG &&
((Var*)(att_target->expr))->varattno == tuple_desc->attrs[i].attnum) {
scanstate->early_stop = true;
}
}
}
static LOCKMODE check_command_type(EState* estate)
{
LOCKMODE lockmode = AccessShareLock;
switch (estate->es_plannedstmt->commandType) {
case CMD_UPDATE:
case CMD_DELETE:
case CMD_MERGE:
lockmode = RowExclusiveLock;
break;
case CMD_SELECT:
lockmode = AccessShareLock;
break;
default:
ereport(ERROR,
(errmodule(MOD_VEC_EXECUTOR),
errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("invalid operation on partition, allowed are UPDATE/DELETE/SELECT")));
}
return lockmode;
}
#endif /* ENABLE_MULTIPLE_NODES */
TsStoreScanState* ExecInitTsStoreScan(TsStoreScan* node, Relation parentHeapRel, EState* estate, int eflags,
bool indexFlag, bool codegenInUplevel)
{
TsStoreScanState* scanstate = nullptr;
#ifdef ENABLE_MULTIPLE_NODES
if (!g_instance.attr.attr_common.enable_tsdb) {
ereport(ERROR, (errcode(ERRCODE_INVALID_OPERATION), errmsg("Please enable timeseries first!")));
}
PlanState* planstate = nullptr;
ScalarDesc unknownDesc;
// ts store can only be a leaf node
Assert(outerPlan(node) == nullptr);
Assert(innerPlan(node) == nullptr);
// There is no reverse scan with column store
Assert(!ScanDirectionIsBackward(estate->es_direction));
instr_time start_time, end_time;
INSTR_TIME_SET_CURRENT(start_time);
// create TsStoreScanState struct
scanstate = build_tsstore_scan_state(node, estate);
/*
* create expression context for node
*/
ExecAssignExprContext(estate, &scanstate->ps);
// Allocate vector for qualification results
ExecAssignVectorForExprEval(scanstate->ps.ps_ExprContext);
// initialize child expressions
scanstate->ps.targetlist = (List*)ExecInitVecExpr((Expr*)node->plan.targetlist, (PlanState*)scanstate);
/*
* tuple table initialization
*/
ExecInitResultTupleSlot(estate, &scanstate->ps);
ExecInitScanTupleSlot(estate, (ScanState*)scanstate);
/*
* initialize scan relation
*/
init_tsstore_relation(scanstate, estate);
/* If operation is DELETE, get mark status to block compaction consumer workers */
if (estate->es_plannedstmt->commandType == CMD_DELETE) {
ereport(LOG, (errmodule(MOD_TIMESERIES), errcode(ERRCODE_LOG),
errmsg("Set delete query id(%lu)", u_sess->debug_query_id)));
Tsdb::TableStatus::GetInstance().add_query(u_sess->debug_query_id);
}
scanstate->time_range = get_time_range(scanstate);
scanstate->ps.ps_vec_TupFromTlist = false;
if (scanstate->limit > 0 && scanstate->ps.qual == NULL) {
/* if is simple sort by timestamp limit n , then earlier stop */
if (scanstate->sort_by_time_colidx >= 0) {
opt_orderby(node, scanstate);
} else if (scanstate->is_simple_scan && !scanstate->has_sort) {
/* if is simple limit n , then earlier stop */
scanstate->early_stop = true;
}
}
/*
* First, not only consider the LLVM native object, but also consider the cost of
* the LLVM compilation time. We will not use LLVM optimization if there is
* not enough number of row. Second, consider codegen after we get some information
* about the scanned relation.
*/
scanstate->jitted_vecqual = nullptr;
llvm::Function* jitted_vecqual = nullptr;
dorado::GsCodeGen* llvmCodeGen = (dorado::GsCodeGen*)t_thrd.codegen_cxt.thr_codegen_obj;
bool consider_codegen = false;
/*
* Check whether we should do codegen here and codegen is allowed for quallist expr.
* In case of codegenInUplevel is true, we do not even have to do codegen for target list.
*/
if (!codegenInUplevel) {
consider_codegen = CodeGenThreadObjectReady() &&
CodeGenPassThreshold(((Plan*)node)->plan_rows, estate->es_plannedstmt->num_nodes, ((Plan*)node)->dop);
if (consider_codegen) {
jitted_vecqual = dorado::VecExprCodeGen::QualCodeGen(scanstate->ps.qual, (PlanState*)scanstate);
if (jitted_vecqual != nullptr)
llvmCodeGen->addFunctionToMCJit(jitted_vecqual, reinterpret_cast<void**>(&(scanstate->jitted_vecqual)));
}
}
/*
* Initialize result tuple type and projection info.
*/
ExecAssignResultTypeFromTL(&scanstate->ps); // make descriptor
if (node->isPartTbl && scanstate->ss_currentRelation == nullptr) {
// no data ,just return;
return scanstate;
}
/*
* If we are just doing EXPLAIN (ie, aren't going to run the plan), stop
* here. This allows an index-advisor plugin to EXPLAIN a plan containing
* references to nonexistent indexes.
*/
if (eflags & EXEC_FLAG_EXPLAIN_ONLY)
return scanstate;
// Init result batch and work batch. Work batch has to contain all columns as qual is
// running against it.
// we shall avoid with this after we fix projection elimination.
scanstate->scanBatch = New(CurrentMemoryContext)
VectorBatch(CurrentMemoryContext, scanstate->ss_currentRelation->rd_att);
scanstate->currentBatch = New(CurrentMemoryContext)
VectorBatch(CurrentMemoryContext, scanstate->ps.ps_ResultTupleSlot->tts_tupleDescriptor);
for (int vecIndex = 0; vecIndex < scanstate->scanBatch->m_cols; vecIndex++) {
FormData_pg_attribute* attr = nullptr;
attr = scanstate->ss_currentRelation->rd_att->attrs[vecIndex];
// Hack!! move me out to update pg_attribute instead
if (attr->atttypid == TIDOID) {
attr->attlen = sizeof(int64);
attr->attbyval = true;
}
}
planstate = &scanstate->ps;
planstate->ps_ProjInfo = ExecBuildVecProjectionInfo(planstate->targetlist,
node->plan.qual,
planstate->ps_ExprContext,
planstate->ps_ResultTupleSlot,
scanstate->ss_ScanTupleSlot->tts_tupleDescriptor);
scanstate->only_const_col = planstate->ps_ProjInfo->pi_const;
/* If exist sysattrlist, not consider LLVM optimization, while the codegen process will be terminated in
* VarJittable*/
if (planstate->ps_ProjInfo->pi_sysAttrList) {
scanstate->scanBatch->CreateSysColContainer(CurrentMemoryContext, planstate->ps_ProjInfo->pi_sysAttrList);
}
/**
* Since we separate the target list elements into simple var references and
* generic expression, we only need to deal the generic expression with LLVM
* optimization.
*/
llvm::Function* jitted_vectarget = nullptr;
if (consider_codegen && planstate->ps_ProjInfo->pi_targetlist) {
/*
* check if codegen is allowed for generic targetlist expr.
* Since targetlist is evaluated in projection, add this function to
* ps_ProjInfo.
*/
jitted_vectarget =
dorado::VecExprCodeGen::TargetListCodeGen(planstate->ps_ProjInfo->pi_targetlist, (PlanState*)scanstate);
if (jitted_vectarget != nullptr)
llvmCodeGen->addFunctionToMCJit(
jitted_vectarget, reinterpret_cast<void**>(&(planstate->ps_ProjInfo->jitted_vectarget)));
}
INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
ereport(DEBUG2, (errmodule(MOD_TIMESERIES),
errmsg("TSDB LOG:: ExecInitTsStoreScan: %lfms", INSTR_TIME_GET_MILLISEC(end_time))));
#endif /* ENABLE_MULTIPLE_NODES */
return scanstate;
}
VectorBatch* ts_apply_projection_and_filter(TsStoreScanState* node, VectorBatch* pScanBatch, ExprDoneCond* isDone)
{
List* qual = NIL;
ExprContext* econtext = NULL;
ProjectionInfo* proj = node->ps.ps_ProjInfo;
VectorBatch* pOutBatch = NULL;
bool fSimpleMap = false;
uint64 inputRows = pScanBatch->m_rows;
VECTSSTORE_SCAN_TRACE_START(node, TSSTORE_PROJECT);
qual = node->ps.qual;
econtext = node->ps.ps_ExprContext;
pOutBatch = node->currentBatch;
fSimpleMap = proj->pi_directMap && (pOutBatch->m_cols == proj->pi_numSimpleVars);
if (node->jitted_vecqual) {
if (HAS_INSTR(node, false)) {
node->ps.instrument->isLlvmOpt = true;
}
}
if (pScanBatch->m_rows != 0) {
ResetExprContext(econtext);
initEcontextBatch(pScanBatch, NULL, NULL, NULL);
// Evaluate the qualification clause if any.
//
if (qual != NULL) {
ScalarVector* pVector = NULL;
if (node->jitted_vecqual)
pVector = node->jitted_vecqual(econtext);
else
pVector = ExecVecQual(qual, econtext, false);
// If no matched rows, fetch again.
//
if (NULL == pVector) {
pOutBatch->m_rows = 0;
goto done;
}
/*
* Call optimized PackT function when codegen is turned on.
*/
if (econtext->ecxt_scanbatch->m_sel) {
if (u_sess->attr.attr_sql.enable_codegen) {
pScanBatch->OptimizePack(econtext->ecxt_scanbatch->m_sel, proj->pi_PackTCopyVars);
} else {
pScanBatch->Pack(econtext->ecxt_scanbatch->m_sel);
}
}
}
// Copy the result to output batch. Note the output batch has different column set than
// the scan batch, so we have to remap them. Projection will handle all logics here, so
// for non simpleMap case, we don't need to do anything.
//
if (!fSimpleMap) {
pOutBatch = ExecVecProject(proj, true, isDone);
} else {
pOutBatch->m_rows = pScanBatch->m_rows;
for (int i = 0; i < pOutBatch->m_cols; i++) {
AttrNumber att = proj->pi_varNumbers[i];
errno_t rc;
Assert(att > 0 && att <= node->scanBatch->m_cols);
rc = memcpy_s(
&pOutBatch->m_arr[i], sizeof(ScalarVector), &pScanBatch->m_arr[att - 1], sizeof(ScalarVector));
securec_check(rc, "\0", "\0");
}
}
}
if (proj->pi_exprContext->have_vec_set_fun == false) {
pOutBatch->m_rows = Min(pOutBatch->m_rows, pScanBatch->m_rows);
pOutBatch->FixRowCount();
}
done:
VECTSSTORE_SCAN_TRACE_END(node, TSSTORE_PROJECT);
// collect information of removed rows
InstrCountFiltered1(node, inputRows - pOutBatch->m_rows);
// Check fullness of return batch and refill it does not contain enough?
return pOutBatch;
}
VectorBatch* ExecTsStoreScan(TsStoreScanState* node)
{
VectorBatch* pOutBatch = NULL;
#ifdef ENABLE_MULTIPLE_NODES
VectorBatch* pScanBatch = NULL;
pOutBatch = node->currentBatch;
pScanBatch = node->scanBatch;
ExprDoneCond isDone = ExprSingleResult;
instr_time start_time, end_time;
int loop_count = 0;
node->tag_id_num = (int)node->tag_rows->row_count;
rescan:
pScanBatch->Reset(true);
pOutBatch->Reset(true);
node->ps.ps_ProjInfo->pi_exprContext->current_row = 0;
loop_count++;
CHECK_FOR_INTERRUPTS();
INSTR_TIME_SET_CURRENT(start_time);
ereport(DEBUG2, (errmodule(MOD_TIMESERIES), errmsg("TSDB LOG: start ExecTsStoreScan[%d]", loop_count)));
reset_sys_vector(node->ps.ps_ProjInfo->pi_sysAttrList, pScanBatch);
/*
* init ts_store_search when first come into this function, seems the
* top_key_func logic should move to the upper layer, but leave it here for now.
*/
if (node->first_scan) {
node->first_scan = false;
node->ts_store_search->init_scan(node);
}
if (node->early_stop && node->scaned_tuples >= node->limit) {
/* if previous partition has load enough tuples then return */
return pOutBatch;
}
VECTSSTORE_SCAN_TRACE_START(node, TSSTORE_SEARCH);
node->tags_scan_done = node->ts_store_search->run_scan(node, pScanBatch);
VECTSSTORE_SCAN_TRACE_END(node, TSSTORE_SEARCH);
if (!BatchIsNull(pScanBatch)) {
pScanBatch->FixRowCount();
pOutBatch = ts_apply_projection_and_filter(node, pScanBatch, &isDone);
if (isDone != ExprEndResult) {
node->ps.ps_vec_TupFromTlist = (isDone == ExprMultipleResult);
}
if (node->early_stop) {
node->scaned_tuples += pScanBatch->m_rows;
}
/* we should not return null batch before all the tags has been scanned */
if (!BatchIsNull(pOutBatch)) {
INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
ereport(DEBUG2, (errmsg("TSDB LOG: finish ExecTsStoreScan: %lfms", INSTR_TIME_GET_MILLISEC(end_time))));
return pOutBatch;
} else if (!node->tags_scan_done) {
goto rescan;
}
} else if (!node->tags_scan_done) {
/* if we have done with current tagids */
goto rescan;
}
INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
ereport(DEBUG2, (errmodule(MOD_TIMESERIES),
errmsg("TSDB LOG: finish ExecTsStoreScan: %lfms", INSTR_TIME_GET_MILLISEC(end_time))));
#endif /* ENABLE_MULTIPLE_NODES */
return pOutBatch;
}
void ExecEndTsStoreScan(TsStoreScanState* node, bool indexFlag)
{
#ifdef ENABLE_MULTIPLE_NODES
Assert(!indexFlag);
Relation relation;
TableScanDesc scanDesc;
/* get information from node */
node->ss_currentRelation = node->ss_partition_parent;
relation = node->ss_currentRelation;
scanDesc = node->ss_currentScanDesc;
/* Free the exprcontext */
ExecFreeExprContext(&node->ps);
/* clean out the tuple table */
ExecClearTuple(node->ps.ps_ResultTupleSlot);
ExecClearTuple(node->ss_ScanTupleSlot);
/* to do : do we need free ts_store_search? */
/* close heap scan */
if (PointerIsValid(node->partitions)) {
Assert(scanDesc);
tableam_scan_end(scanDesc);
Assert(node->ss_currentPartition);
releaseDummyRelation(&(node->ss_currentPartition));
releasePartitionList(node->ss_currentRelation, &(node->partitions), node->lockMode);
}
/* close the heap relation. */
ExecCloseScanRelation(relation);
if (node->time_range != NULL) {
delete node->time_range;
node->time_range = NULL;
}
if(node->scanBatch != NULL) {
delete node->scanBatch;
node->scanBatch = NULL;
}
if(node->currentBatch != NULL) {
delete node->currentBatch;
node->currentBatch = NULL;
}
if (node->ts_store_search != NULL) {
delete node->ts_store_search;
node->ts_store_search = NULL;
}
#endif /* ENABLE_MULTIPLE_NODES */
}
void ExecReScanTsStoreScan(TsStoreScanState* node)
{
#ifdef ENABLE_MULTIPLE_NODES
instr_time start_time, end_time;
INSTR_TIME_SET_CURRENT(start_time);
TableScanDesc scan = node->ss_currentScanDesc;
heap_endscan(scan);
init_next_partition_for_tsstore_scan(node);
node->first_scan = true;
INSTR_TIME_SET_CURRENT(end_time);
INSTR_TIME_SUBTRACT(end_time, start_time);
ereport(DEBUG2, (errmodule(MOD_TIMESERIES),
errmsg("TSDB LOG:: ExecReScanTsStoreScan: %lfms", INSTR_TIME_GET_MILLISEC(end_time))));
#endif /* ENABLE_MULTIPLE_NODES */
}
/*
* @vectsstorescan.cpp
* @Description: reset system vector rows to zero
* @in - sys_attr_list: system attrribute list
* @in - vector
* @return - void
*/
void reset_sys_vector(const List* sys_attr_list, VectorBatch* vector)
{
if (sys_attr_list == NIL) {
return;
}
ListCell* cell = NULL;
foreach(cell, sys_attr_list) {
int col_idx = lfirst_int(cell);
ScalarVector* sys_vec = vector->GetSysVector(col_idx);
sys_vec->m_rows = 0;
}
}