/* * 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(&(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(&(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; } }