forked from Cachuela/openGauss-server
1215 lines
44 KiB
C++
1215 lines
44 KiB
C++
/* -------------------------------------------------------------------------
|
|
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
|
* Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* veccstore.cpp
|
|
* Support routines for sequential scans of column stores.
|
|
*
|
|
* IDENTIFICATION
|
|
* Code/src/gausskernel/runtime/vecexecutor/vecnode/veccstore.cpp
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
|
|
/*
|
|
* INTERFACE ROUTINES
|
|
* ExecCStoreScan sequentially scans a column store.
|
|
* ExecCStoreNext retrieve next tuple in sequential order.
|
|
* ExecInitCStoreScan creates and initializes a cstorescan node.
|
|
* ExecEndCStoreScan releases any storage allocated.
|
|
* ExecReScanCStoreScan rescans the column store
|
|
*/
|
|
#include "codegen/gscodegen.h"
|
|
#include "codegen/vecexprcodegen.h"
|
|
#include "postgres.h"
|
|
#include "knl/knl_variable.h"
|
|
|
|
#include "access/relscan.h"
|
|
#include "access/tableam.h"
|
|
#include "executor/execdebug.h"
|
|
#include "vecexecutor/vecnodecstorescan.h"
|
|
#include "vecexecutor/vecnoderowtovector.h"
|
|
#include "executor/nodeModifyTable.h"
|
|
#include "executor/nodeSeqscan.h"
|
|
#include "storage/cstore/cstore_compress.h"
|
|
#include "access/cstore_am.h"
|
|
#include "optimizer/clauses.h"
|
|
#include "nodes/params.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/datum.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/rel_gs.h"
|
|
#include "utils/syscache.h"
|
|
#include "executor/nodeSamplescan.h"
|
|
#include "executor/nodeSeqscan.h"
|
|
#include "access/cstoreskey.h"
|
|
#include "catalog/pg_cast.h"
|
|
#include "catalog/pg_operator.h"
|
|
#include "catalog/pg_proc.h"
|
|
#include "access/genam.h"
|
|
#include "access/heapam.h"
|
|
#include "access/sysattr.h"
|
|
#include "catalog/indexing.h"
|
|
#include "access/heapam.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/snapmgr.h"
|
|
#include "catalog/pg_partition_fn.h"
|
|
#include "pgxc/redistrib.h"
|
|
#include "optimizer/pruning.h"
|
|
|
|
|
|
extern bool CodeGenThreadObjectReady();
|
|
extern bool CodeGenPassThreshold(double rows, int dn_num, int dop);
|
|
|
|
static CStoreStrategyNumber GetCStoreScanStrategyNumber(Oid opno);
|
|
static Datum GetParamExternConstValue(Oid left_type, Expr* expr, PlanState* ps, uint16* flag);
|
|
static void ExecInitNextPartitionForCStoreScan(CStoreScanState* node);
|
|
static void ExecCStoreBuildScanKeys(CStoreScanState* scan_stat, List* quals, CStoreScanKey* scan_keys, int* num_scan_keys,
|
|
CStoreScanRunTimeKeyInfo** runtime_key_info, int* runtime_keys_num);
|
|
static void ExecCStoreScanEvalRuntimeKeys(
|
|
ExprContext* expr_ctx, CStoreScanRunTimeKeyInfo* runtime_keys, int num_runtime_keys);
|
|
|
|
/* the same to CStore::SetTiming() */
|
|
#define TIMING_VECCSTORE_SCAN(_node) (NULL != (_node)->ps.instrument && (_node)->ps.instrument->need_timer)
|
|
|
|
#define VECCSTORE_SCAN_TRACE_START(_node, _desc_id) \
|
|
do { \
|
|
if (unlikely(TIMING_VECCSTORE_SCAN(_node))) { \
|
|
TRACK_START((_node)->ps.plan->plan_node_id, (_desc_id)); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define VECCSTORE_SCAN_TRACE_END(_node, _desc_id) \
|
|
do { \
|
|
if (unlikely(TIMING_VECCSTORE_SCAN(_node))) { \
|
|
TRACK_END((_node)->ps.plan->plan_node_id, (_desc_id)); \
|
|
} \
|
|
} while (0)
|
|
|
|
/*
|
|
* type conversion for cu min/max filter
|
|
* see also: cstore_roughcheck_func.cpp
|
|
*/
|
|
static FORCE_INLINE Datum convert_scan_key_int64_if_need(Oid left_type, Oid right_type, Datum right_value)
|
|
{
|
|
Datum v = right_value;
|
|
switch (right_type) {
|
|
/* int family to INT64 */
|
|
case INT4OID: {
|
|
v = Int64GetDatum((int64)DatumGetInt32(right_value));
|
|
break;
|
|
}
|
|
case INT2OID: {
|
|
v = Int64GetDatum((int64)DatumGetInt16(right_value));
|
|
break;
|
|
}
|
|
case INT1OID: {
|
|
v = Int64GetDatum((int64)DatumGetChar(right_value));
|
|
break;
|
|
}
|
|
case DATEOID:
|
|
case TIMEOID:
|
|
case TIMESTAMPOID:
|
|
case TIMESTAMPTZOID: {
|
|
/* date, time etc. make right type same as left */
|
|
if (right_type != left_type) {
|
|
/* get type conversion function */
|
|
HeapTuple cast_tuple =
|
|
SearchSysCache2(CASTSOURCETARGET, ObjectIdGetDatum(right_type), ObjectIdGetDatum(left_type));
|
|
|
|
if (!HeapTupleIsValid(cast_tuple))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("can not cast from type %s to type %s ",
|
|
format_type_be(right_type),
|
|
format_type_be(left_type))));
|
|
|
|
Form_pg_cast cast_form = (Form_pg_cast)GETSTRUCT(cast_tuple);
|
|
Oid func_id = cast_form->castfunc;
|
|
char cast_method = cast_form->castmethod;
|
|
|
|
ReleaseSysCache(cast_tuple);
|
|
|
|
if (!OidIsValid(func_id) || cast_method != COERCION_METHOD_FUNCTION)
|
|
break;
|
|
|
|
/* check function args */
|
|
HeapTuple proc_tuple = SearchSysCache1(PROCOID, ObjectIdGetDatum(func_id));
|
|
|
|
if (!HeapTupleIsValid(proc_tuple))
|
|
ereport(ERROR,
|
|
(errmodule(MOD_VEC_EXECUTOR),
|
|
errcode(ERRCODE_CACHE_LOOKUP_FAILED),
|
|
errmsg("cache lookup failed for function %u", func_id)));
|
|
|
|
Form_pg_proc proc_form = (Form_pg_proc)GETSTRUCT(proc_tuple);
|
|
int proc_nargs = proc_form->pronargs;
|
|
|
|
ReleaseSysCache(proc_tuple);
|
|
|
|
/* execute type conversion */
|
|
if (proc_nargs == 1)
|
|
v = OidFunctionCall1(func_id, right_value);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
void OptimizeProjectionAndFilter(CStoreScanState* node)
|
|
{
|
|
ProjectionInfo* proj = NULL;
|
|
bool simple_map = false;
|
|
|
|
proj = node->ps.ps_ProjInfo;
|
|
|
|
// Check if it is simple without need to invoke projection code
|
|
//
|
|
simple_map = proj->pi_directMap && (node->m_pCurrentBatch->m_cols == proj->pi_numSimpleVars);
|
|
|
|
node->m_fSimpleMap = simple_map;
|
|
}
|
|
|
|
VectorBatch* ApplyProjectionAndFilter(CStoreScanState* node, VectorBatch* p_scan_batch, ExprDoneCond* done)
|
|
{
|
|
List* qual = NIL;
|
|
ExprContext* econtext = NULL;
|
|
ProjectionInfo* proj = node->ps.ps_ProjInfo;
|
|
VectorBatch* p_out_batch = NULL;
|
|
bool simple_map = false;
|
|
int late_read_ctid = 0;
|
|
uint64 input_rows = p_scan_batch->m_rows;
|
|
|
|
VECCSTORE_SCAN_TRACE_START(node, CSTORE_PROJECT);
|
|
|
|
qual = node->ps.qual;
|
|
econtext = node->ps.ps_ExprContext;
|
|
p_out_batch = node->m_pCurrentBatch;
|
|
simple_map = node->m_fSimpleMap;
|
|
|
|
if (node->jitted_vecqual) {
|
|
if (HAS_INSTR(node, false)) {
|
|
node->ps.instrument->isLlvmOpt = true;
|
|
}
|
|
}
|
|
|
|
if (p_scan_batch->m_rows != 0) {
|
|
ResetExprContext(econtext);
|
|
initEcontextBatch(p_scan_batch, NULL, NULL, NULL);
|
|
// Evaluate the qualification clause if any.
|
|
//
|
|
if (qual != NULL) {
|
|
ScalarVector* p_vector = NULL;
|
|
|
|
if (node->jitted_vecqual)
|
|
p_vector = node->jitted_vecqual(econtext);
|
|
else
|
|
p_vector = ExecVecQual(qual, econtext, false);
|
|
|
|
// If no matched rows, fetch again.
|
|
//
|
|
if (p_vector == NULL) {
|
|
p_out_batch->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) {
|
|
late_read_ctid = node->m_CStore->GetLateReadCtid();
|
|
if (node->ss_deltaScan || late_read_ctid == -1) {
|
|
p_scan_batch->OptimizePack(econtext->ecxt_scanbatch->m_sel, proj->pi_PackTCopyVars);
|
|
} else {
|
|
p_scan_batch->OptimizePackForLateRead(
|
|
econtext->ecxt_scanbatch->m_sel, proj->pi_PackLateAccessVarNumbers, late_read_ctid);
|
|
}
|
|
} else
|
|
p_scan_batch->Pack(econtext->ecxt_scanbatch->m_sel);
|
|
}
|
|
}
|
|
|
|
// Late read these columns for non-delta data
|
|
// Now we have finished filter check, and then we can read other columns
|
|
//
|
|
if (!node->ss_deltaScan) {
|
|
VECCSTORE_SCAN_TRACE_START(node, FILL_LATER_BATCH);
|
|
node->m_CStore->FillScanBatchLateIfNeed(p_scan_batch);
|
|
VECCSTORE_SCAN_TRACE_END(node, FILL_LATER_BATCH);
|
|
} else {
|
|
node->ss_deltaScan = false;
|
|
}
|
|
|
|
// Project the final result
|
|
//
|
|
if (!simple_map) {
|
|
p_out_batch = ExecVecProject(proj, true, done);
|
|
} else {
|
|
// 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.
|
|
//
|
|
p_out_batch->m_rows = p_scan_batch->m_rows;
|
|
for (int i = 0; i < p_out_batch->m_cols; i++) {
|
|
AttrNumber att = proj->pi_varNumbers[i];
|
|
errno_t rc;
|
|
|
|
Assert(att > 0 && att <= node->m_pScanBatch->m_cols);
|
|
|
|
rc = memcpy_s(
|
|
&p_out_batch->m_arr[i], sizeof(ScalarVector), &p_scan_batch->m_arr[att - 1], sizeof(ScalarVector));
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (proj->pi_exprContext->have_vec_set_fun == false) {
|
|
p_out_batch->m_rows = Min(p_out_batch->m_rows, p_scan_batch->m_rows);
|
|
p_out_batch->FixRowCount();
|
|
}
|
|
|
|
done:
|
|
|
|
VECCSTORE_SCAN_TRACE_END(node, CSTORE_PROJECT);
|
|
|
|
// collect information of removed rows
|
|
InstrCountFiltered1(node, input_rows - p_out_batch->m_rows);
|
|
|
|
// Check fullness of return batch and refill it does not contain enough?
|
|
return p_out_batch;
|
|
}
|
|
|
|
TupleDesc BuildTupleDescByTargetList(List* tlist)
|
|
{
|
|
ListCell* lc = NULL;
|
|
TupleDesc desc = CreateTemplateTupleDesc(list_length(tlist), false);
|
|
int att_num = 1;
|
|
foreach (lc, tlist) {
|
|
TargetEntry* tle = (TargetEntry*)lfirst(lc);
|
|
Var* var = (Var*)tle->expr;
|
|
TupleDescInitEntry(desc, (AttrNumber)att_num, tle->resname, var->vartype, var->vartypmod, 0);
|
|
att_num++;
|
|
}
|
|
|
|
return desc;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecCStoreScan(node)
|
|
*
|
|
* Scans the relation sequentially and returns the next qualifying
|
|
* tuple.
|
|
* We call the ExecScan() routine and pass it the appropriate
|
|
* access method functions.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
VectorBatch* ExecCStoreScan(CStoreScanState* node)
|
|
{
|
|
VectorBatch* p_out_batch = NULL;
|
|
VectorBatch* p_scan_batch = NULL;
|
|
|
|
// If we have runtime keys and they've not already been set up, do it now.
|
|
//
|
|
// evaluate the runtime key
|
|
if (node->m_ScanRunTimeKeysNum && !node->m_ScanRunTimeKeysReady) {
|
|
ExecCStoreScanEvalRuntimeKeys(node->ps.ps_ExprContext, node->m_pScanRunTimeKeys, node->m_ScanRunTimeKeysNum);
|
|
node->m_ScanRunTimeKeysReady = true;
|
|
}
|
|
|
|
p_out_batch = node->m_pCurrentBatch;
|
|
p_scan_batch = node->m_pScanBatch;
|
|
|
|
// update cstore scan timing flag
|
|
node->m_CStore->SetTiming(node);
|
|
|
|
ExprDoneCond done = ExprSingleResult;
|
|
/*
|
|
* for function-returning-set.
|
|
*/
|
|
if (node->ps.ps_TupFromTlist) {
|
|
Assert(node->ps.ps_ProjInfo);
|
|
p_out_batch = ExecVecProject(node->ps.ps_ProjInfo, true, &done);
|
|
if (p_out_batch->m_rows > 0) {
|
|
return p_out_batch;
|
|
}
|
|
|
|
node->ps.ps_TupFromTlist = false;
|
|
}
|
|
|
|
restart:
|
|
|
|
// We don't go through the regular ExecScan interface as we will handle all
|
|
// common code ourselves here
|
|
//
|
|
p_scan_batch->Reset(true);
|
|
p_out_batch->Reset(true);
|
|
node->ps.ps_ProjInfo->pi_exprContext->current_row = 0;
|
|
|
|
if (!node->isSampleScan) {
|
|
node->m_CStore->RunScan(node, p_scan_batch);
|
|
} else {
|
|
/*
|
|
* Sample scan for column table.
|
|
*/
|
|
(((ColumnTableSample*)node->sampleScanInfo.tsm_state)->scanVecSample)(p_scan_batch);
|
|
}
|
|
|
|
if (node->m_CStore->IsEndScan() && p_scan_batch->m_rows == 0) {
|
|
// scan delta store table if has data
|
|
ScanDeltaStore(node, p_scan_batch, NULL);
|
|
if (p_scan_batch->m_rows == 0)
|
|
return p_out_batch;
|
|
}
|
|
|
|
p_scan_batch->FixRowCount();
|
|
|
|
// Apply quals and projections
|
|
//
|
|
p_out_batch = ApplyProjectionAndFilter(node, p_scan_batch, &done);
|
|
|
|
if (done != ExprEndResult) {
|
|
node->ps.ps_TupFromTlist = (done == ExprMultipleResult);
|
|
}
|
|
|
|
/* Response to the stop query flag. */
|
|
if (unlikely(executorEarlyStop()))
|
|
return NULL;
|
|
|
|
if (BatchIsNull(p_out_batch)) {
|
|
CHECK_FOR_INTERRUPTS();
|
|
goto restart;
|
|
}
|
|
|
|
return p_out_batch;
|
|
}
|
|
|
|
void ReScanDeltaRelation(CStoreScanState* node)
|
|
{
|
|
if (node->ss_currentDeltaScanDesc) {
|
|
tableam_scan_rescan((TableScanDesc)(node->ss_currentDeltaScanDesc), NULL);
|
|
}
|
|
node->ss_deltaScan = false;
|
|
node->ss_deltaScanEnd = false;
|
|
}
|
|
|
|
void InitCStoreRelation(CStoreScanState* node, EState* estate, bool idx_flag, Relation parent_rel)
|
|
{
|
|
Relation curr_rel;
|
|
Relation curr_part_rel = NULL;
|
|
CStoreScan* plan = (CStoreScan*)node->ps.plan;
|
|
bool is_target_rel = false;
|
|
LOCKMODE lock_mode = AccessShareLock;
|
|
TableScanDesc curr_scan_desc = NULL;
|
|
|
|
is_target_rel = ExecRelationIsTargetRelation(estate, plan->scanrelid);
|
|
|
|
if (!node->isPartTbl) {
|
|
/*
|
|
* get the relation object id from the relid'th entry in the range table,
|
|
* open that relation and acquire appropriate lock on it.
|
|
*/
|
|
if (!idx_flag) {
|
|
curr_rel = ExecOpenScanRelation(estate, plan->scanrelid);
|
|
} else {
|
|
curr_rel = index_open(plan->scanrelid, AccessShareLock);
|
|
Oid p_sort_idx_rel_oid = curr_rel->rd_rel->relcudescrelid;
|
|
Assert(p_sort_idx_rel_oid != InvalidOid);
|
|
index_close(curr_rel, NoLock);
|
|
curr_rel = heap_open(p_sort_idx_rel_oid, AccessShareLock);
|
|
}
|
|
/* add qual for redis */
|
|
if (u_sess->attr.attr_sql.enable_cluster_resize && RelationInRedistribute(curr_rel)) {
|
|
List* new_qual = NIL;
|
|
|
|
new_qual = eval_ctid_funcs(curr_rel, 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);
|
|
}
|
|
} else {
|
|
/* get the relation object id from the relid'th entry in the range table */
|
|
if (!idx_flag)
|
|
curr_rel = ExecOpenScanRelation(estate, plan->scanrelid);
|
|
else
|
|
curr_rel = index_open(plan->scanrelid, AccessShareLock);
|
|
|
|
/* initiate partition list */
|
|
node->partitions = NULL;
|
|
|
|
if (!is_target_rel) {
|
|
lock_mode = AccessShareLock;
|
|
} else {
|
|
switch (estate->es_plannedstmt->commandType) {
|
|
case CMD_UPDATE:
|
|
case CMD_DELETE:
|
|
case CMD_MERGE:
|
|
lock_mode = RowExclusiveLock;
|
|
break;
|
|
|
|
case CMD_SELECT:
|
|
lock_mode = AccessShareLock;
|
|
break;
|
|
|
|
default:
|
|
ereport(ERROR,
|
|
(errmodule(MOD_VEC_EXECUTOR),
|
|
errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
|
|
errmsg("invalid operation on partition, allowed are UPDATE/DELETE/SELECT/MERGE")));
|
|
}
|
|
}
|
|
node->lockMode = lock_mode;
|
|
|
|
if (plan->itrs > 0) {
|
|
Partition part = NULL;
|
|
Partition curr_part = NULL;
|
|
|
|
PruningResult* resultPlan = NULL;
|
|
if (plan->pruningInfo->expr != NULL) {
|
|
resultPlan = GetPartitionInfo(plan->pruningInfo, estate, curr_rel);
|
|
} else {
|
|
resultPlan = plan->pruningInfo;
|
|
}
|
|
|
|
ListCell* cell = NULL;
|
|
List* part_seqs = resultPlan->ls_rangeSelectedPartitions;
|
|
|
|
if (!idx_flag) {
|
|
foreach (cell, part_seqs) {
|
|
Oid tbl_part_id = InvalidOid;
|
|
int part_seq = lfirst_int(cell);
|
|
|
|
tbl_part_id = getPartitionOidFromSequence(curr_rel, part_seq);
|
|
part = partitionOpen(curr_rel, tbl_part_id, lock_mode);
|
|
node->partitions = lappend(node->partitions, part);
|
|
}
|
|
if (resultPlan->ls_rangeSelectedPartitions != NULL) {
|
|
node->part_id = resultPlan->ls_rangeSelectedPartitions->length;
|
|
} else {
|
|
node->part_id = 0;
|
|
}
|
|
if (node->partitions == NULL)
|
|
ereport(ERROR,
|
|
(errmodule(MOD_VEC_EXECUTOR),
|
|
errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
|
errmsg("Fail to find partition from sequence.")));
|
|
|
|
/* construct HeapScanDesc for first partition */
|
|
curr_part = (Partition)list_nth(node->partitions, 0);
|
|
curr_part_rel = partitionGetRelation(curr_rel, curr_part);
|
|
node->ss_currentPartition = curr_part_rel;
|
|
|
|
/* add qual for redis */
|
|
if (u_sess->attr.attr_sql.enable_cluster_resize && RelationInRedistribute(curr_part_rel)) {
|
|
List* new_qual = NIL;
|
|
|
|
new_qual = eval_ctid_funcs(curr_part_rel, 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);
|
|
}
|
|
|
|
if (!node->isSampleScan) {
|
|
curr_scan_desc = tableam_scan_begin(curr_part_rel, estate->es_snapshot, 0, NULL);
|
|
} else {
|
|
curr_scan_desc = (TableScanDesc)InitSampleScanDesc((ScanState*)node, curr_part_rel);
|
|
}
|
|
} else {
|
|
Assert(parent_rel);
|
|
|
|
// Now curr_rel is the index relation of logical table (parent relation)
|
|
// parent_rel is the logical heap relation
|
|
//
|
|
foreach (cell, part_seqs) {
|
|
Oid tbl_part_id = InvalidOid;
|
|
int part_seq = lfirst_int(cell);
|
|
|
|
tbl_part_id = getPartitionOidFromSequence(parent_rel, part_seq);
|
|
part = partitionOpen(parent_rel, tbl_part_id, lock_mode);
|
|
Oid part_idx_oid = getPartitionIndexOid(plan->scanrelid, part->pd_id);
|
|
Assert(OidIsValid(part_idx_oid));
|
|
partitionClose(parent_rel, part, NoLock);
|
|
|
|
Partition part_idx = partitionOpen(curr_rel, part_idx_oid, lock_mode);
|
|
Oid idx_oid = part_idx->pd_part->relcudescrelid;
|
|
partitionClose(curr_rel, part_idx, NoLock);
|
|
|
|
Relation idx_rel = heap_open(idx_oid, lock_mode);
|
|
node->partitions = lappend(node->partitions, idx_rel);
|
|
}
|
|
|
|
if (resultPlan->ls_rangeSelectedPartitions != NULL) {
|
|
node->part_id = resultPlan->ls_rangeSelectedPartitions->length;
|
|
} else {
|
|
node->part_id = 0;
|
|
}
|
|
curr_part_rel = (Relation)list_nth(node->partitions, 0);
|
|
/* add qual for redis */
|
|
if (u_sess->attr.attr_sql.enable_cluster_resize && RelationInRedistribute(curr_part_rel)) {
|
|
List* new_qual = NIL;
|
|
|
|
new_qual = eval_ctid_funcs(curr_part_rel, 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);
|
|
}
|
|
// Remove it ??
|
|
//
|
|
node->ss_currentPartition = curr_part_rel;
|
|
}
|
|
} else {
|
|
node->ss_currentPartition = NULL;
|
|
node->ps.qual = (List*)ExecInitVecExpr((Expr*)node->ps.plan->qual, (PlanState*)&node->ps);
|
|
}
|
|
}
|
|
|
|
if (node->isPartTbl) {
|
|
node->ss_currentRelation = curr_part_rel;
|
|
node->ss_partition_parent = curr_rel;
|
|
} else {
|
|
node->ss_currentRelation = curr_rel;
|
|
node->ss_partition_parent = NULL;
|
|
}
|
|
|
|
node->ss_currentScanDesc = curr_scan_desc;
|
|
ExecAssignScanType(node, RelationGetDescr(curr_rel));
|
|
}
|
|
|
|
void InitScanDeltaRelation(CStoreScanState* node, EState* estate)
|
|
{
|
|
Relation delta_rel;
|
|
TableScanDesc delta_scan_desc;
|
|
Relation cstore_rel = node->ss_currentRelation;
|
|
|
|
if (node->ss_currentRelation == NULL)
|
|
return;
|
|
|
|
delta_rel = heap_open(cstore_rel->rd_rel->reldeltarelid, AccessShareLock);
|
|
delta_scan_desc = tableam_scan_begin(delta_rel, estate->es_snapshot, 0, NULL);
|
|
|
|
node->ss_currentDeltaRelation = delta_rel;
|
|
node->ss_currentDeltaScanDesc = delta_scan_desc;
|
|
node->ss_deltaScanEnd = false;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecInitCStoreScan
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
CStoreScanState* ExecInitCStoreScan(
|
|
CStoreScan* node, Relation parent_heap_rel, EState* estate, int eflags, bool idx_flag, bool codegen_in_up_level)
|
|
{
|
|
CStoreScanState* scan_stat = NULL;
|
|
PlanState* plan_stat = NULL;
|
|
ScalarDesc unknown_desc;
|
|
TableSampleClause* tsc = NULL;
|
|
|
|
// Column store can only be a leaf node
|
|
//
|
|
Assert(outerPlan(node) == NULL);
|
|
Assert(innerPlan(node) == NULL);
|
|
|
|
// There is no reverse scan with column store
|
|
//
|
|
Assert(!ScanDirectionIsBackward(estate->es_direction));
|
|
|
|
// Create state structure
|
|
//
|
|
scan_stat = makeNode(CStoreScanState);
|
|
scan_stat->ps.plan = (Plan*)node;
|
|
scan_stat->ps.state = estate;
|
|
scan_stat->ps.vectorized = true;
|
|
scan_stat->isPartTbl = node->isPartTbl;
|
|
plan_stat = &scan_stat->ps;
|
|
scan_stat->partScanDirection = node->partScanDirection;
|
|
scan_stat->m_isReplicaTable = node->is_replica_table;
|
|
scan_stat->rangeScanInRedis = {false,0,0};
|
|
if (!node->tablesample) {
|
|
scan_stat->isSampleScan = false;
|
|
} else {
|
|
scan_stat->isSampleScan = true;
|
|
tsc = node->tablesample;
|
|
}
|
|
|
|
/*
|
|
* Miscellaneous initialization
|
|
*
|
|
* create expression context for node
|
|
*/
|
|
ExecAssignExprContext(estate, &scan_stat->ps);
|
|
|
|
// Allocate vector for qualification results
|
|
//
|
|
ExecAssignVectorForExprEval(scan_stat->ps.ps_ExprContext);
|
|
|
|
// initialize child expressions
|
|
//
|
|
scan_stat->ps.targetlist = (List*)ExecInitVecExpr((Expr*)node->plan.targetlist, (PlanState*)scan_stat);
|
|
|
|
if (node->tablesample) {
|
|
scan_stat->sampleScanInfo.args = (List*)ExecInitExpr((Expr*)tsc->args, (PlanState*)scan_stat);
|
|
scan_stat->sampleScanInfo.repeatable = ExecInitExpr(tsc->repeatable, (PlanState*)scan_stat);
|
|
|
|
scan_stat->sampleScanInfo.sampleType = tsc->sampleType;
|
|
|
|
/*
|
|
* Initialize ColumnTableSample
|
|
*/
|
|
if (scan_stat->sampleScanInfo.tsm_state == NULL) {
|
|
scan_stat->sampleScanInfo.tsm_state = New(CurrentMemoryContext) ColumnTableSample(scan_stat);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* tuple table initialization
|
|
*/
|
|
ExecInitResultTupleSlot(estate, &scan_stat->ps);
|
|
ExecInitScanTupleSlot(estate, (ScanState*)scan_stat);
|
|
|
|
/*
|
|
* initialize scan relation
|
|
*/
|
|
InitCStoreRelation(scan_stat, estate, idx_flag, parent_heap_rel);
|
|
scan_stat->ps.ps_TupFromTlist = false;
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
scan_stat->jitted_vecqual = NULL;
|
|
llvm::Function* jitted_vecqual = NULL;
|
|
dorado::GsCodeGen* llvm_code_gen = (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 codegen_in_up_level is true, we do not even have to do codegen for target list.
|
|
*/
|
|
if (!codegen_in_up_level) {
|
|
consider_codegen =
|
|
CodeGenThreadObjectReady() &&
|
|
CodeGenPassThreshold(((Plan*)node)->plan_rows, estate->es_plannedstmt->num_nodes, ((Plan*)node)->dop);
|
|
if (consider_codegen) {
|
|
jitted_vecqual = dorado::VecExprCodeGen::QualCodeGen(scan_stat->ps.qual, (PlanState*)scan_stat);
|
|
if (jitted_vecqual != NULL)
|
|
llvm_code_gen->addFunctionToMCJit(jitted_vecqual, reinterpret_cast<void**>(&(scan_stat->jitted_vecqual)));
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Initialize result tuple type and projection info.
|
|
*/
|
|
ExecAssignResultTypeFromTL(
|
|
&scan_stat->ps,
|
|
scan_stat->ss_ScanTupleSlot->tts_tupleDescriptor->tdTableAmType);
|
|
|
|
if (node->isPartTbl && scan_stat->ss_currentRelation == NULL) {
|
|
// no data ,just return;
|
|
return scan_stat;
|
|
}
|
|
|
|
/*
|
|
* 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 scan_stat;
|
|
|
|
// 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.
|
|
//
|
|
scan_stat->m_pCurrentBatch = New(CurrentMemoryContext)
|
|
VectorBatch(CurrentMemoryContext, scan_stat->ps.ps_ResultTupleSlot->tts_tupleDescriptor);
|
|
scan_stat->m_pScanBatch =
|
|
New(CurrentMemoryContext) VectorBatch(CurrentMemoryContext, scan_stat->ss_currentRelation->rd_att);
|
|
for (int vecIndex = 0; vecIndex < scan_stat->m_pScanBatch->m_cols; vecIndex++) {
|
|
FormData_pg_attribute* attr = NULL;
|
|
attr = scan_stat->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;
|
|
}
|
|
}
|
|
|
|
plan_stat->ps_ProjInfo = ExecBuildVecProjectionInfo(plan_stat->targetlist,
|
|
node->plan.qual,
|
|
plan_stat->ps_ExprContext,
|
|
plan_stat->ps_ResultTupleSlot,
|
|
scan_stat->ss_ScanTupleSlot->tts_tupleDescriptor);
|
|
|
|
/* Set min/max optimization info to ProjectionInfo's pi_maxOrmin. */
|
|
plan_stat->ps_ProjInfo->pi_maxOrmin = node->minMaxInfo;
|
|
|
|
/* If exist sysattrlist, not consider LLVM optimization, while the codegen process will be terminated in
|
|
* VarJittable */
|
|
if (plan_stat->ps_ProjInfo->pi_sysAttrList) {
|
|
scan_stat->m_pScanBatch->CreateSysColContainer(CurrentMemoryContext, plan_stat->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 = NULL;
|
|
if (consider_codegen && plan_stat->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(plan_stat->ps_ProjInfo->pi_targetlist, (PlanState*)scan_stat);
|
|
if (jitted_vectarget != NULL)
|
|
llvm_code_gen->addFunctionToMCJit(
|
|
jitted_vectarget, reinterpret_cast<void**>(&(plan_stat->ps_ProjInfo->jitted_vectarget)));
|
|
}
|
|
|
|
scan_stat->m_pScanRunTimeKeys = NULL;
|
|
scan_stat->m_ScanRunTimeKeysNum = 0;
|
|
scan_stat->m_ScanRunTimeKeysReady = false;
|
|
scan_stat->csss_ScanKeys = NULL;
|
|
scan_stat->csss_NumScanKeys = 0;
|
|
|
|
ExecCStoreBuildScanKeys(scan_stat,
|
|
node->cstorequal,
|
|
&scan_stat->csss_ScanKeys,
|
|
&scan_stat->csss_NumScanKeys,
|
|
&scan_stat->m_pScanRunTimeKeys,
|
|
&scan_stat->m_ScanRunTimeKeysNum);
|
|
|
|
scan_stat->m_CStore = New(CurrentMemoryContext) CStore();
|
|
scan_stat->m_CStore->InitScan(scan_stat, GetActiveSnapshot());
|
|
OptimizeProjectionAndFilter(scan_stat);
|
|
|
|
/*
|
|
* initialize delta relation
|
|
*/
|
|
InitScanDeltaRelation(scan_stat, estate->es_snapshot);
|
|
|
|
return scan_stat;
|
|
}
|
|
|
|
/* ----------------------------------------------------------------
|
|
* ExecEndCStoreScan
|
|
*
|
|
* frees any storage allocated through C routines.
|
|
* ----------------------------------------------------------------
|
|
*/
|
|
void ExecEndCStoreScan(CStoreScanState* node, bool idx_flag)
|
|
{
|
|
Relation relation;
|
|
TableScanDesc scan_desc;
|
|
|
|
/*
|
|
* get information from node
|
|
*/
|
|
if (node->isPartTbl) {
|
|
node->ss_currentRelation = node->ss_partition_parent;
|
|
}
|
|
|
|
relation = node->ss_currentRelation;
|
|
scan_desc = (TableScanDesc)(node->ss_currentScanDesc);
|
|
/*
|
|
* Free the exprcontext
|
|
*/
|
|
ExecFreeExprContext(&node->ps);
|
|
|
|
/*
|
|
* clean out the tuple table
|
|
*/
|
|
(void)ExecClearTuple(node->ps.ps_ResultTupleSlot);
|
|
(void)ExecClearTuple(node->ss_ScanTupleSlot);
|
|
|
|
/*
|
|
* release CStoreScan
|
|
*/
|
|
if (node->m_CStore) {
|
|
DELETE_EX(node->m_CStore);
|
|
}
|
|
|
|
/*
|
|
* close heap scan
|
|
*/
|
|
if (node->isPartTbl) {
|
|
if (PointerIsValid(node->partitions)) {
|
|
if (!idx_flag) {
|
|
Assert(scan_desc);
|
|
tableam_scan_end(scan_desc);
|
|
Assert(node->ss_currentPartition);
|
|
releaseDummyRelation(&(node->ss_currentPartition));
|
|
releasePartitionList(node->ss_currentRelation, &(node->partitions), node->lockMode);
|
|
} else {
|
|
ListCell* cell = NULL;
|
|
Relation rel;
|
|
|
|
foreach (cell, node->partitions) {
|
|
rel = (Relation)lfirst(cell);
|
|
if (rel->rd_refcnt > 0) {
|
|
// destory partition ssd cache
|
|
relation_close(rel, NoLock);
|
|
}
|
|
}
|
|
list_free_ext(node->partitions);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* close the heap relation.
|
|
*/
|
|
ExecCloseScanRelation(relation);
|
|
|
|
EndScanDeltaRelation(node);
|
|
}
|
|
|
|
/* Build the cstore scan keys from the qual. */
|
|
static void ExecCStoreBuildScanKeys(CStoreScanState* scan_stat, List* quals, CStoreScanKey* scan_keys, int* num_scan_keys,
|
|
CStoreScanRunTimeKeyInfo** runtime_key_info, int* runtime_keys_num)
|
|
{
|
|
ListCell* lc = NULL;
|
|
CStoreScanKey tmp_scan_keys;
|
|
int n_scan_keys;
|
|
int j;
|
|
List* accessed_varnos = NIL;
|
|
CStoreScanRunTimeKeyInfo* runtime_info = NULL;
|
|
int runtime_keys;
|
|
int max_runtime_keys;
|
|
|
|
n_scan_keys = list_length(quals);
|
|
// only if the indexFlag is flase and n_scan_keys is zero, we should reinit scan_keys and num_scan_keys
|
|
//
|
|
if (n_scan_keys == 0) {
|
|
return;
|
|
}
|
|
|
|
Assert(*scan_keys == NULL);
|
|
Assert(*num_scan_keys == 0);
|
|
tmp_scan_keys = (CStoreScanKey)palloc0(n_scan_keys * sizeof(CStoreScanKeyData));
|
|
|
|
accessed_varnos = scan_stat->ps.ps_ProjInfo->pi_acessedVarNumbers;
|
|
|
|
runtime_info = *runtime_key_info;
|
|
runtime_keys = *runtime_keys_num;
|
|
max_runtime_keys = runtime_keys;
|
|
j = 0;
|
|
foreach (lc, quals) {
|
|
Expr* clause = (Expr*)lfirst(lc);
|
|
CStoreScanKey this_scan_key = &tmp_scan_keys[j++];
|
|
Oid opno;
|
|
RegProcedure opfunc_id;
|
|
|
|
Expr* leftop = NULL;
|
|
Expr* rightop = NULL;
|
|
AttrNumber varattno;
|
|
|
|
if (IsA(clause, OpExpr)) {
|
|
uint16 flags = 0; // no use,default 0
|
|
Datum scan_val = 0;
|
|
CStoreStrategyNumber strategy = InvalidCStoreStrategy;
|
|
ListCell* lcell = NULL;
|
|
AttrNumber count_no = 0;
|
|
Oid left_type = InvalidOid;
|
|
|
|
opno = ((OpExpr*)clause)->opno;
|
|
opfunc_id = ((OpExpr*)clause)->opfuncid;
|
|
|
|
/* Leftop should be var. Has been checked */
|
|
leftop = (Expr*)get_leftop(clause);
|
|
if (leftop && IsA(leftop, RelabelType))
|
|
leftop = ((RelabelType*)leftop)->arg;
|
|
if (leftop == NULL)
|
|
ereport(ERROR,
|
|
(errmodule(MOD_VEC_EXECUTOR),
|
|
errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
|
errmsg("The left value of the expression should not be NULL")));
|
|
|
|
/* The attribute numbers of column in cstore scan is a sequence.begin with 0. */
|
|
varattno = ((Var*)leftop)->varattno;
|
|
foreach (lcell, accessed_varnos) {
|
|
if ((int)varattno == lfirst_int(lcell)) {
|
|
varattno = count_no;
|
|
break;
|
|
}
|
|
count_no++;
|
|
}
|
|
|
|
left_type = ((Var*)leftop)->vartype;
|
|
|
|
/* Rightop should be const.Has been checked */
|
|
rightop = (Expr*)get_rightop(clause);
|
|
if (rightop == NULL)
|
|
ereport(ERROR,
|
|
(errmodule(MOD_VEC_EXECUTOR),
|
|
errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
|
|
errmsg("Fail to get the right value of the expression")));
|
|
|
|
if (IsA(rightop, Const)) {
|
|
scan_val =
|
|
convert_scan_key_int64_if_need(left_type, ((Const*)rightop)->consttype, ((Const*)rightop)->constvalue);
|
|
flags = ((Const*)rightop)->constisnull;
|
|
} else if (IsA(rightop, RelabelType)) {
|
|
rightop = ((RelabelType*)rightop)->arg;
|
|
Assert(rightop != NULL);
|
|
scan_val =
|
|
convert_scan_key_int64_if_need(left_type, ((Const*)rightop)->consttype, ((Const*)rightop)->constvalue);
|
|
flags = ((Const*)rightop)->constisnull;
|
|
} else if (nodeTag(rightop) == T_Param && ((Param*)rightop)->paramkind == PARAM_EXTERN) {
|
|
scan_val = GetParamExternConstValue(left_type, rightop, &(scan_stat->ps), &flags);
|
|
} else if (nodeTag(rightop) == T_Param) {
|
|
// when the rightop is T_Param, the scan_val will be filled until rescan happens.
|
|
//
|
|
if (runtime_keys >= max_runtime_keys) {
|
|
if (max_runtime_keys == 0) {
|
|
max_runtime_keys = 8;
|
|
runtime_info =
|
|
(CStoreScanRunTimeKeyInfo*)palloc(max_runtime_keys * sizeof(CStoreScanRunTimeKeyInfo));
|
|
} else {
|
|
max_runtime_keys *= 2;
|
|
runtime_info = (CStoreScanRunTimeKeyInfo*)repalloc(
|
|
runtime_info, max_runtime_keys * sizeof(CStoreScanRunTimeKeyInfo));
|
|
}
|
|
}
|
|
runtime_info[runtime_keys].scan_key = this_scan_key;
|
|
runtime_info[runtime_keys].key_expr = ExecInitExpr(rightop, NULL);
|
|
runtime_keys++;
|
|
scan_val = (Datum)0;
|
|
} else
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
|
|
errmsg("Not support pushing predicate with none-const external param")));
|
|
/* Get strategy number. */
|
|
strategy = GetCStoreScanStrategyNumber(opno);
|
|
/* initialize the scan key's fields appropriately */
|
|
CStoreScanKeyInit(this_scan_key,
|
|
flags,
|
|
varattno,
|
|
strategy,
|
|
((OpExpr*)clause)->inputcollid,
|
|
opfunc_id,
|
|
scan_val,
|
|
left_type);
|
|
} else {
|
|
pfree_ext(tmp_scan_keys);
|
|
tmp_scan_keys = NULL;
|
|
n_scan_keys = 0;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
*scan_keys = tmp_scan_keys;
|
|
*num_scan_keys = n_scan_keys;
|
|
*runtime_key_info = runtime_info;
|
|
*runtime_keys_num = runtime_keys;
|
|
}
|
|
|
|
/* No metadata for the operator strategy. The followings are temporary codes.
|
|
*/
|
|
static CStoreStrategyNumber GetCStoreScanStrategyNumber(Oid opno)
|
|
{
|
|
CStoreStrategyNumber strategy_number = InvalidCStoreStrategy;
|
|
Relation hdesc;
|
|
ScanKeyData skey[1];
|
|
SysScanDesc sysscan;
|
|
HeapTuple tuple;
|
|
Form_pg_operator fpo;
|
|
|
|
ScanKeyInit(&skey[0], ObjectIdAttributeNumber, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(opno));
|
|
hdesc = heap_open(OperatorRelationId, AccessShareLock);
|
|
sysscan = systable_beginscan(hdesc, OperatorOidIndexId, true, NULL, 1, skey);
|
|
|
|
tuple = systable_getnext(sysscan);
|
|
if (!HeapTupleIsValid(tuple))
|
|
ereport(ERROR,
|
|
(errmodule(MOD_VEC_EXECUTOR),
|
|
errcode(ERRCODE_DATA_EXCEPTION),
|
|
errmsg("could not find tuple for operator %u", opno)));
|
|
|
|
fpo = (Form_pg_operator)GETSTRUCT(tuple);
|
|
|
|
if (strncmp(NameStr(fpo->oprname), "<", NAMEDATALEN) == 0)
|
|
strategy_number = CStoreLessStrategyNumber;
|
|
else if (strncmp(NameStr(fpo->oprname), ">", NAMEDATALEN) == 0)
|
|
strategy_number = CStoreGreaterStrategyNumber;
|
|
else if (strncmp(NameStr(fpo->oprname), "=", NAMEDATALEN) == 0)
|
|
strategy_number = CStoreEqualStrategyNumber;
|
|
else if (strncmp(NameStr(fpo->oprname), ">=", NAMEDATALEN) == 0)
|
|
strategy_number = CStoreGreaterEqualStrategyNumber;
|
|
else if (strncmp(NameStr(fpo->oprname), "<=", NAMEDATALEN) == 0)
|
|
strategy_number = CStoreLessEqualStrategyNumber;
|
|
else
|
|
strategy_number = InvalidCStoreStrategy;
|
|
|
|
systable_endscan(sysscan);
|
|
heap_close(hdesc, AccessShareLock);
|
|
|
|
return strategy_number;
|
|
}
|
|
|
|
static Datum GetParamExternConstValue(Oid left_type, Expr* expr, PlanState* ps, uint16* flag)
|
|
{
|
|
Param* expression = (Param*)expr;
|
|
int param_id = expression->paramid;
|
|
ParamListInfo param_info = ps->state->es_param_list_info;
|
|
|
|
/*
|
|
* PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
|
|
*/
|
|
if (param_info && param_id > 0 && param_id <= param_info->numParams) {
|
|
ParamExternData* prm = ¶m_info->params[param_id - 1];
|
|
*flag = prm->isnull;
|
|
if (OidIsValid(prm->ptype)) {
|
|
return convert_scan_key_int64_if_need(left_type, prm->ptype, prm->value);
|
|
}
|
|
|
|
} else {
|
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("no value found for parameter %d", param_id)));
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void ExecReCStoreSeqScan(CStoreScanState* node)
|
|
{
|
|
node->m_CStore->InitReScan();
|
|
}
|
|
|
|
void ExecCStoreScanEvalRuntimeKeys(ExprContext* expr_ctx, CStoreScanRunTimeKeyInfo* runtime_keys, int num_runtime_keys)
|
|
{
|
|
int j;
|
|
MemoryContext old_ctx;
|
|
|
|
/* We want to keep the key values in per-tuple memory */
|
|
old_ctx = MemoryContextSwitchTo(expr_ctx->ecxt_per_tuple_memory);
|
|
|
|
for (j = 0; j < num_runtime_keys; j++) {
|
|
CStoreScanKey scan_key = runtime_keys[j].scan_key;
|
|
ExprState* key_expr = runtime_keys[j].key_expr;
|
|
Datum scan_val;
|
|
bool is_null = false;
|
|
|
|
/*
|
|
* For each run-time key, extract the run-time expression and evaluate
|
|
* it with respect to the current context. We then stick the result
|
|
* into the proper scan key.
|
|
*
|
|
* Note: the result of the eval could be a pass-by-ref value that's
|
|
* stored in some outer scan's tuple, not in
|
|
* expr_ctx->ecxt_per_tuple_memory. We assume that the outer tuple
|
|
* will stay put throughout our scan. If this is wrong, we could copy
|
|
* the result into our context explicitly, but I think that's not
|
|
* necessary.
|
|
*
|
|
* It's also entirely possible that the result of the eval is a
|
|
* toasted value. In this case we should forcibly detoast it, to
|
|
* avoid repeat detoastings each time the value is examined by an
|
|
* index support function.
|
|
*/
|
|
scan_val = ExecEvalExpr(key_expr, expr_ctx, &is_null, NULL);
|
|
if (is_null)
|
|
scan_key->cs_flags |= SK_ISNULL;
|
|
else
|
|
scan_key->cs_flags &= ~SK_ISNULL;
|
|
|
|
scan_key->cs_argument =
|
|
convert_scan_key_int64_if_need(scan_key->cs_left_type, ((Param*)key_expr->expr)->paramtype, scan_val);
|
|
}
|
|
|
|
(void)MemoryContextSwitchTo(old_ctx);
|
|
}
|
|
|
|
void ExecReSetRuntimeKeys(CStoreScanState* node)
|
|
{
|
|
ExprContext* expr_ctx = node->ps.ps_ExprContext;
|
|
ExecCStoreScanEvalRuntimeKeys(expr_ctx, node->m_pScanRunTimeKeys, node->m_ScanRunTimeKeysNum);
|
|
}
|
|
|
|
void ExecReScanCStoreScan(CStoreScanState* node)
|
|
{
|
|
TableScanDesc scan;
|
|
|
|
if (node->isSampleScan) {
|
|
/* Remember we need to do BeginSampleScan again (if we did it at all) */
|
|
(((ColumnTableSample*)node->sampleScanInfo.tsm_state)->resetVecSampleScan)();
|
|
}
|
|
|
|
if (node->m_ScanRunTimeKeysNum != 0) {
|
|
ExecReSetRuntimeKeys(node);
|
|
}
|
|
node->m_ScanRunTimeKeysReady = true;
|
|
|
|
scan = (TableScanDesc)(node->ss_currentScanDesc);
|
|
if (node->isPartTbl) {
|
|
if (PointerIsValid(node->partitions)) {
|
|
/* end scan the prev partition first, */
|
|
tableam_scan_end(scan);
|
|
EndScanDeltaRelation(node);
|
|
|
|
/* finally init Scan for the next partition */
|
|
ExecInitNextPartitionForCStoreScan(node);
|
|
}
|
|
}
|
|
|
|
ExecReCStoreSeqScan(node);
|
|
ReScanDeltaRelation(node);
|
|
}
|
|
|
|
static void ExecInitNextPartitionForCStoreScan(CStoreScanState* node)
|
|
{
|
|
Partition curr_part = NULL;
|
|
Relation curr_part_rel = NULL;
|
|
TableScanDesc curr_scan_desc = NULL;
|
|
int param_no = -1;
|
|
ParamExecData* param = NULL;
|
|
CStoreScan* plan = NULL;
|
|
|
|
plan = (CStoreScan*)node->ps.plan;
|
|
|
|
/* get partition sequnce */
|
|
param_no = plan->plan.paramno;
|
|
param = &(node->ps.state->es_param_exec_vals[param_no]);
|
|
node->currentSlot = (int)param->value;
|
|
|
|
/* construct HeapScanDesc for new partition */
|
|
curr_part = (Partition)list_nth(node->partitions, node->currentSlot);
|
|
curr_part_rel = partitionGetRelation(node->ss_partition_parent, curr_part);
|
|
|
|
releaseDummyRelation(&(node->ss_currentPartition));
|
|
node->ss_currentPartition = curr_part_rel;
|
|
node->ss_currentRelation = curr_part_rel;
|
|
|
|
/* add qual for redis */
|
|
if (u_sess->attr.attr_sql.enable_cluster_resize && RelationInRedistribute(curr_part_rel)) {
|
|
List* new_qual = NIL;
|
|
|
|
new_qual = eval_ctid_funcs(curr_part_rel, node->ps.plan->qual, &node->rangeScanInRedis);
|
|
node->ps.qual = (List*)ExecInitVecExpr((Expr*)new_qual, (PlanState*)&node->ps);
|
|
}
|
|
|
|
if (!node->isSampleScan) {
|
|
curr_scan_desc = tableam_scan_begin(curr_part_rel, node->ps.state->es_snapshot, 0, NULL);
|
|
} else {
|
|
curr_scan_desc = InitSampleScanDesc((ScanState*)node, curr_part_rel);
|
|
}
|
|
|
|
/* update partition scan-related fileds in SeqScanState */
|
|
node->ss_currentScanDesc = curr_scan_desc;
|
|
node->m_CStore->InitPartReScan(node->ss_currentRelation);
|
|
|
|
/* reinit delta scan */
|
|
InitScanDeltaRelation(node, node->ps.state->es_snapshot);
|
|
}
|