forked from huawei/openGauss-server
867 lines
32 KiB
C++
867 lines
32 KiB
C++
/* ---------------------------------------------------------------------------------------
|
|
* Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd.
|
|
* Portions Copyright (c) 1996, 2003 VIA Networking Technologies, Inc.
|
|
*
|
|
* dfs_query_reader.cpp
|
|
*
|
|
* IDENTIFICATION
|
|
* src/gausskernel/storage/access/dfs/dfs_query_reader.cpp
|
|
*
|
|
* ---------------------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "access/dfs/dfs_common.h"
|
|
#include "access/dfs/dfs_insert.h"
|
|
#include "access/dfs/dfs_query.h"
|
|
#include "access/dfs/dfs_wrapper.h"
|
|
#include "access/sysattr.h"
|
|
#include "catalog/pg_am.h"
|
|
#include "catalog/pg_operator.h"
|
|
#include "catalog/indexing.h"
|
|
#include "catalog/catalog.h"
|
|
#include "catalog/pg_partition.h"
|
|
#include "catalog/pg_partition_fn.h"
|
|
#include "catalog/pgxc_node.h"
|
|
#include "catalog/dfsstore_ctlg.h"
|
|
#include "commands/defrem.h"
|
|
#include "foreign/fdwapi.h"
|
|
#include "foreign/foreign.h"
|
|
#include "nodes/makefuncs.h"
|
|
#include "optimizer/cost.h"
|
|
#include "optimizer/var.h"
|
|
#include "optimizer/restrictinfo.h"
|
|
#include "optimizer/planmain.h"
|
|
#include "optimizer/predtest.h"
|
|
#include "parser/parse_type.h"
|
|
#include "utils/bloom_filter.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/syscache.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/snapmgr.h"
|
|
#include "utils/int8.h"
|
|
#include "executor/executor.h"
|
|
|
|
/*
|
|
* Parse the datum value from extern param according to the expr.
|
|
*
|
|
* @_in param expr: The extern param expression.
|
|
* @_in param ps: PlanState from which we can get the value of ParamExternData.
|
|
* @return Return the point to ParamExternData.
|
|
*/
|
|
static ParamExternData *HdfsGetParamExtern(Expr *expr, PlanState *ps)
|
|
{
|
|
Param *expression = (Param *)expr;
|
|
int thisParamId = expression->paramid;
|
|
ParamListInfo paramInfo = ps->state->es_param_list_info;
|
|
|
|
/*
|
|
* PARAM_EXTERN parameters must be sought in ecxt_param_list_info.
|
|
*/
|
|
if (paramInfo && thisParamId > 0 && thisParamId <= paramInfo->numParams) {
|
|
ParamExternData *prm = ¶mInfo->params[thisParamId - 1];
|
|
|
|
if (NULL != prm && OidIsValid(prm->ptype)) {
|
|
return prm;
|
|
}
|
|
}
|
|
|
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmodule(MOD_DFS),
|
|
errmsg("no value found for parameter %d", thisParamId)));
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* @Description: Build the op expression according to the strategy and var.
|
|
* @IN strategy: the operator type
|
|
* @IN var: the column var
|
|
* @Return: the op expression
|
|
* @See also:
|
|
*/
|
|
static Expr *BuildExprByStrategy(HdfsQueryOperator strategy, Var *var)
|
|
{
|
|
Expr *expr = NULL;
|
|
switch (strategy) {
|
|
case HDFS_QUERY_EQ: {
|
|
expr = (Expr *)MakeOperatorExpression(var, BTEqualStrategyNumber);
|
|
break;
|
|
}
|
|
case HDFS_QUERY_LT: {
|
|
expr = (Expr *)MakeOperatorExpression(var, BTLessStrategyNumber);
|
|
break;
|
|
}
|
|
case HDFS_QUERY_GT: {
|
|
expr = (Expr *)MakeOperatorExpression(var, BTGreaterStrategyNumber);
|
|
break;
|
|
}
|
|
case HDFS_QUERY_LTE: {
|
|
expr = (Expr *)MakeOperatorExpression(var, BTLessEqualStrategyNumber);
|
|
break;
|
|
}
|
|
case HDFS_QUERY_GTE: {
|
|
expr = (Expr *)MakeOperatorExpression(var, BTGreaterEqualStrategyNumber);
|
|
break;
|
|
}
|
|
default: {
|
|
/* no process */
|
|
break;
|
|
}
|
|
}
|
|
|
|
return expr;
|
|
}
|
|
|
|
template <typename T, typename baseType>
|
|
bool HdfsScanPredicate<T, baseType>::BuildHdfsScanPredicateFromClause(Expr *expr, PlanState *ps, ScanState *scanstate,
|
|
AttrNumber varNoPos, int predicateArrPos)
|
|
{
|
|
Expr *rightop = NULL;
|
|
Expr *leftop = NULL;
|
|
Datum datumValue = (Datum)0;
|
|
Oid datumType = InvalidOid;
|
|
int32 typeMod = 0;
|
|
bool runningTimeSet = false;
|
|
|
|
if (IsA(expr, OpExpr)) {
|
|
/* Here the leftop must be not null and is either RelabelType or Var type. */
|
|
leftop = (Expr *)get_leftop(expr);
|
|
if (leftop == NULL) {
|
|
ereport(ERROR, (errmodule(MOD_DFS), errmsg("The leftop is null")));
|
|
}
|
|
|
|
if (IsA(leftop, RelabelType)) {
|
|
leftop = ((RelabelType *)leftop)->arg;
|
|
}
|
|
Assert(IsA(leftop, Var));
|
|
|
|
typeMod = ((Var *)leftop)->vartypmod;
|
|
|
|
/* Rightop should be const.Has been checked before. */
|
|
rightop = (Expr *)get_rightop(expr);
|
|
if (rightop == NULL) {
|
|
return runningTimeSet;
|
|
}
|
|
|
|
#ifdef ENABLE_LLVM_COMPILE
|
|
/* Build IR according to expr node. */
|
|
if (CodeGenThreadObjectReady()) {
|
|
(void)ForeignScanExprCodeGen(expr, NULL, &m_jittedFunc);
|
|
}
|
|
#endif
|
|
if (IsA(rightop, Const)) {
|
|
datumValue = ((Const *)rightop)->constvalue;
|
|
datumType = ((Const *)rightop)->consttype;
|
|
} else if (IsA(rightop, RelabelType)) {
|
|
rightop = ((RelabelType *)rightop)->arg;
|
|
Assert(rightop != NULL);
|
|
datumValue = ((Const *)rightop)->constvalue;
|
|
datumType = ((Const *)rightop)->consttype;
|
|
} else if (nodeTag(rightop) == T_Param && ((Param *)rightop)->paramkind == PARAM_EXTERN) {
|
|
ParamExternData *prm = HdfsGetParamExtern(rightop, ps);
|
|
datumValue = prm->value;
|
|
datumType = prm->ptype;
|
|
} else if (nodeTag(rightop) == T_Param && ((Param *)rightop)->paramkind == PARAM_EXEC) {
|
|
RunTimeParamPredicateInfo *runTimeParamPredicate =
|
|
(RunTimeParamPredicateInfo *)palloc0(sizeof(RunTimeParamPredicateInfo));
|
|
Param *parameter = (Param *)rightop;
|
|
runTimeParamPredicate->varNoPos = varNoPos;
|
|
runTimeParamPredicate->paraExecExpr = ExecInitExpr(rightop, NULL);
|
|
runTimeParamPredicate->opExpr = BuildExprByStrategy(m_strategy, (Var *)leftop);
|
|
runTimeParamPredicate->typeMod = typeMod;
|
|
runTimeParamPredicate->datumType = parameter->paramtype;
|
|
runTimeParamPredicate->paramPosition = predicateArrPos;
|
|
runTimeParamPredicate->varTypeOid = ((Var *)leftop)->vartype;
|
|
scanstate->runTimeParamPredicates = lappend(scanstate->runTimeParamPredicates, runTimeParamPredicate);
|
|
|
|
runningTimeSet = true;
|
|
} else if (nodeTag(rightop) == T_Param) {
|
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_DFS),
|
|
errmsg("Not support pushing predicate with sublink param now!")));
|
|
} else {
|
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_DFS),
|
|
errmsg("Not support pushing predicate with non-const")));
|
|
}
|
|
} else if (IsA(expr, NullTest)) {
|
|
} else {
|
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_DFS),
|
|
errmsg("We only support pushing down opExpr and null test predicates.")));
|
|
}
|
|
|
|
/*
|
|
* If the predicate is running time predicate, do not init it here.
|
|
* Must maintain the predicate in the future, if we add external logic.
|
|
*/
|
|
if (!runningTimeSet) {
|
|
Init(datumValue, datumType, typeMod);
|
|
}
|
|
|
|
return runningTimeSet;
|
|
}
|
|
|
|
/*
|
|
* Get the HdfsQueryOperator according to the opratorName, here we only support seven types of operators and return
|
|
* -1 for the unsupported operator.
|
|
*
|
|
* @_in param operatorName: the name of the operator like '<>'.
|
|
* @return Return an HdfsQueryOperator of which the meaning can be found in the defination of HdfsQueryOperator.
|
|
*/
|
|
HdfsQueryOperator HdfsGetQueryOperator(const char *OpName)
|
|
{
|
|
HdfsQueryOperator hdfsOpName = HDFS_QUERY_INVALID;
|
|
int32 OpNameIndex = 0;
|
|
int32 OpNameCount;
|
|
static const char *nameMappings[] = { "=", "<", ">", "<=", ">=", "<>", "!=" };
|
|
|
|
OpNameCount = sizeof(nameMappings) / sizeof(nameMappings[0]);
|
|
for (OpNameIndex = 0; OpNameIndex < OpNameCount; OpNameIndex++) {
|
|
const char *pgOpName = nameMappings[OpNameIndex];
|
|
if (strncmp(pgOpName, OpName, NAMEDATALEN) == 0) {
|
|
hdfsOpName = (HdfsQueryOperator)OpNameIndex;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hdfsOpName;
|
|
}
|
|
|
|
/*
|
|
* Search the pg_operate catalog by operation oid, and return the operator strategy.
|
|
*
|
|
* @_in param opno: the oid of the operator.
|
|
* @return Return an HdfsQueryOperator: HDFS_QUERY_INVALID means the current strategy is not supported,
|
|
* other value's meaning can be found in the defination of HdfsQueryOperator.
|
|
*/
|
|
static HdfsQueryOperator GetHdfsScanStrategyNumber(Oid opno)
|
|
{
|
|
HeapTuple tuple = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
|
|
if (!HeapTupleIsValid(tuple)) {
|
|
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmodule(MOD_DFS),
|
|
errmsg("could not find operator by oid %u", opno)));
|
|
}
|
|
Form_pg_operator fpo = (Form_pg_operator)GETSTRUCT(tuple);
|
|
|
|
HdfsQueryOperator strategy_number = HDFS_QUERY_INVALID;
|
|
strategy_number = HdfsGetQueryOperator(NameStr(fpo->oprname));
|
|
|
|
ReleaseSysCache(tuple);
|
|
|
|
return strategy_number;
|
|
}
|
|
|
|
/*
|
|
* @Description: Create one Restriction info for one partition, and also bind PartColValue
|
|
* in current split.
|
|
* @in rel: target relation
|
|
* @in partVar: partition column's var
|
|
* @in scanrelid: Index relid of the target relation
|
|
* @in partExpr: string format of this partition directory
|
|
* @out/in @si: current splitinfo
|
|
* @in equalExpr: the prepared equal expression.
|
|
* @return return one partition restriction.
|
|
*/
|
|
static Node *CreateOnePartitionRestriction(Relation rel, Var *partVar, Index scanrelid, const char *partExpr,
|
|
SplitInfo *si, Expr *equalExpr)
|
|
{
|
|
Node *baseRestriction = NULL;
|
|
Datum datumValue;
|
|
const char *pos = strchr(partExpr, '=');
|
|
|
|
if (pos == NULL) {
|
|
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmodule(MOD_DFS),
|
|
errmsg("Invalid partition expression:%s", partExpr)));
|
|
}
|
|
|
|
const char *partColValue = pos + 1;
|
|
/*
|
|
* If the value is NULL, means that no existence of predicate restriction.
|
|
*/
|
|
if (partVar == NULL) {
|
|
si->partContentList = lappend(si->partContentList, makeString(UriDecode(partColValue)));
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* First check if the partitioning column with value *NULL* and build
|
|
* IS_NULL restriction check
|
|
*/
|
|
if (strncmp(partColValue, DEFAULT_HIVE_NULL, DEFAULT_HIVE_NULL_LENGTH) == 0) {
|
|
baseRestriction = BuildNullTestConstraint(partVar, IS_NULL);
|
|
} else {
|
|
Assert(equalExpr != NULL);
|
|
datumValue = GetDatumFromString(partVar->vartype, partVar->vartypmod, UriDecode(partColValue));
|
|
BuildConstraintConst(equalExpr, datumValue, false);
|
|
baseRestriction = (Node *)equalExpr;
|
|
}
|
|
|
|
si->partContentList = lappend(si->partContentList, makeString(UriDecode(partColValue)));
|
|
|
|
return baseRestriction;
|
|
}
|
|
|
|
/*
|
|
* Sort the columns to read order by selectivity in asce.
|
|
*/
|
|
static void SortReadColsByWeight(dfs::reader::ReaderState *readerState, double *selectivity)
|
|
{
|
|
#define DFS_SWAP(T, A, B) do { \
|
|
T t_ = (A); \
|
|
A = (B); \
|
|
B = t_; \
|
|
} while (0)
|
|
|
|
if (selectivity == NULL) {
|
|
return;
|
|
}
|
|
|
|
for (uint32 i = 0; i < readerState->relAttrNum; i++) {
|
|
double minWeight = 1.0;
|
|
uint32 minCol = 0;
|
|
int flag = 0;
|
|
|
|
for (uint32 j = i; j < readerState->relAttrNum; j++) {
|
|
if (selectivity[j] != 0 && selectivity[j] < minWeight) {
|
|
minWeight = selectivity[j];
|
|
minCol = j;
|
|
flag = 1;
|
|
}
|
|
}
|
|
|
|
if (flag == 1) {
|
|
DFS_SWAP(float, selectivity[minCol], selectivity[i]);
|
|
DFS_SWAP(uint32, readerState->orderedCols[minCol], readerState->orderedCols[i]);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* @Description: Pruning unnecessary splits(partition) based on the restriction
|
|
* of scan clauses (Static Partition Pruning).
|
|
* @in ss: A ScanState struct.
|
|
* @in rs: A ReaderState struct.
|
|
* @in col_var_list: The var list of all the target columns and the
|
|
* restriction columns.
|
|
* @return return the remain file list.
|
|
*/
|
|
static List *PruningUnnecessaryPartitions(ScanState *ss, dfs::reader::ReaderState *rs, List *col_var_list)
|
|
{
|
|
Relation rel = ss->ss_currentRelation;
|
|
rs->partList = ((ValuePartitionMap *)rel->partMap)->partList;
|
|
List *splitList = rs->splitList;
|
|
List *prunedSplitList = NULL;
|
|
int total_partitions = list_length(rs->splitList);
|
|
ListCell *c = NULL;
|
|
int pruned_partitions = 0;
|
|
bool pruned = false;
|
|
Index scanrelid = ((DfsScan *)ss->ps.plan)->scanrelid;
|
|
char *partsigs = (char *)palloc(MAX_PARSIGS_LENGTH);
|
|
char *curPartExpr = (char *)palloc(MAX_PARSIG_LENGTH);
|
|
int32_t fileNameOffset = strlen(getDfsStorePath(rel)->data) + 1;
|
|
int partNum = list_length(rs->partList);
|
|
Expr **equalExpr = (Expr **)palloc0(sizeof(Expr *) * partNum);
|
|
Var **partVars = (Var **)palloc0(sizeof(Var *) * partNum);
|
|
|
|
/* prepare the equal expressions for all the partition columns. */
|
|
for (int i = 0; i < partNum; i++) {
|
|
AttrNumber partColId = list_nth_int(rs->partList, i);
|
|
partVars[i] = GetVarFromColumnList(col_var_list, partColId);
|
|
if (partVars[i] != NULL) {
|
|
equalExpr[i] = (Expr *)MakeOperatorExpression(partVars[i], BTEqualStrategyNumber);
|
|
}
|
|
}
|
|
|
|
errno_t rc;
|
|
/* Check each SplitInfo element from raw scanning list */
|
|
foreach (c, splitList) {
|
|
SplitInfo *si = (SplitInfo *)lfirst(c);
|
|
char *curpos = NULL;
|
|
Node *baseRestriction = NULL;
|
|
List *partRestriction = NIL;
|
|
char *fileName = si->filePath + fileNameOffset;
|
|
|
|
rc = memset_s(partsigs, MAX_PARSIGS_LENGTH, 0, MAX_PARSIGS_LENGTH);
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
/* create parsigs without last '/' */
|
|
int64 baseNameLen = basename_len(fileName, '/');
|
|
if (unlikely(baseNameLen < 0)) {
|
|
continue;
|
|
}
|
|
rc = strncpy_s(partsigs, MAX_PARSIGS_LENGTH, fileName, baseNameLen);
|
|
CHECK_PARTITION_SIGNATURE(rc, fileName);
|
|
curpos = partsigs;
|
|
|
|
/* Process each partition level */
|
|
for (int i = 0; i < partNum; i++) {
|
|
/* Process last partition */
|
|
if (i == partNum - 1) {
|
|
baseRestriction = CreateOnePartitionRestriction(rel, partVars[i], scanrelid, curpos, si, equalExpr[i]);
|
|
if (baseRestriction != NULL) {
|
|
partRestriction = lappend(partRestriction, baseRestriction);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
/* Buffer for current partition expression */
|
|
rc = memset_s(curPartExpr, MAX_PARSIG_LENGTH, 0, MAX_PARSIG_LENGTH);
|
|
securec_check(rc, "\0", "\0");
|
|
int slashPos = (int)strpos(curpos, "/");
|
|
if (unlikely(slashPos < 0)) {
|
|
continue;
|
|
}
|
|
rc = strncpy_s(curPartExpr, MAX_PARSIG_LENGTH, curpos, slashPos);
|
|
CHECK_PARTITION_SIGNATURE(rc, curpos);
|
|
|
|
/*
|
|
* Create predicate restriction for current value-partition level
|
|
* elimination
|
|
*/
|
|
baseRestriction = CreateOnePartitionRestriction(rel, partVars[i], scanrelid, curPartExpr, si, equalExpr[i]);
|
|
if (baseRestriction != NULL) {
|
|
partRestriction = lappend(partRestriction, baseRestriction);
|
|
}
|
|
|
|
curpos = (char *)curpos + strlen(curPartExpr) + 1;
|
|
}
|
|
|
|
/*
|
|
* Pruning unncessary partitions by evaluating parititon predicates
|
|
* through the primitive restrictions.
|
|
*/
|
|
pruned = predicate_refuted_by(partRestriction, rs->queryRestrictionList, true);
|
|
|
|
ereport(DEBUG2,
|
|
(errmodule(MOD_DFS), errmsg("partition:%s should be pruned[%s]", partsigs, pruned ? "YES" : "NO")));
|
|
|
|
if (pruned) {
|
|
pruned_partitions++;
|
|
} else {
|
|
prunedSplitList = lappend(prunedSplitList, si);
|
|
}
|
|
|
|
if (partRestriction != NIL) {
|
|
list_free(partRestriction);
|
|
partRestriction = NIL;
|
|
}
|
|
}
|
|
|
|
/* count the number of partitions which is pruned static */
|
|
rs->staticPruneFiles += pruned_partitions;
|
|
ereport(DEBUG1, (errmodule(MOD_DFS),
|
|
errmsg("Pruning partitions on relation %s with SPP optimization *%s* pruned:%d, total:%d",
|
|
RelationGetRelationName(ss->ss_currentRelation),
|
|
u_sess->attr.attr_sql.enable_valuepartition_pruning ? "ON" : "OFF", pruned_partitions,
|
|
total_partitions)));
|
|
|
|
pfree_ext(partsigs);
|
|
pfree_ext(curPartExpr);
|
|
pfree_ext(equalExpr);
|
|
pfree_ext(partVars);
|
|
|
|
/* If SPP is disabled, we return the original splitlist directly */
|
|
if (!u_sess->attr.attr_sql.enable_valuepartition_pruning) {
|
|
return splitList;
|
|
}
|
|
|
|
return prunedSplitList;
|
|
}
|
|
|
|
/*
|
|
* brief: The initialization process of partition table when scan begins.
|
|
* input param @readerState: Execution state for specific dfs scan.
|
|
*/
|
|
static void PartitionTblInit(dfs::reader::ReaderState *readerState)
|
|
{
|
|
uint32 *partColNoArr = NULL;
|
|
uint32 adapt = 0;
|
|
uint32 i = 0;
|
|
List *partList = readerState->partList;
|
|
|
|
/* Array to store mapping from relation column no to orc reader column index. */
|
|
readerState->colNoMapArr = (uint32 *)palloc0(sizeof(uint32) * readerState->relAttrNum);
|
|
|
|
/* Array to store partition column value if exists. */
|
|
readerState->partitionColValueArr = (char **)palloc0(sizeof(char *) * readerState->relAttrNum);
|
|
|
|
/* Fill the partColNoArr according to partList. */
|
|
readerState->partNum = list_length(partList);
|
|
|
|
/*
|
|
* partColNoArr is an array to store partition column info which is used to indicate
|
|
* which column is partition column(1 => partition column, 0 => non-partition column)
|
|
* when building colNoMapArr and removing partition column from column list.
|
|
* Remember to pfree it in the end of beginScan.
|
|
*/
|
|
partColNoArr = (uint32 *)palloc0(sizeof(uint32) * readerState->relAttrNum);
|
|
for (i = 0; i < readerState->partNum; i++) {
|
|
int IndexColNum = list_nth_int(partList, i);
|
|
partColNoArr[IndexColNum - 1] = 1;
|
|
}
|
|
|
|
/*
|
|
* Construct the colNoMapArr, when meet partition column all the columns after it need to decrease
|
|
* its mapping number. For example, the normal column array is [1,2,3,4,5,6,7], partition column is
|
|
* [3,5], then the colNoMapArr will be [1,2,3,3,4,4,5].
|
|
*/
|
|
for (i = 0; i < readerState->relAttrNum; i++) {
|
|
readerState->colNoMapArr[i] = i - adapt + 1;
|
|
if (partColNoArr[i] == 1) {
|
|
adapt++;
|
|
}
|
|
}
|
|
|
|
pfree_ext(partColNoArr);
|
|
}
|
|
|
|
/*
|
|
* @Description: Since the params of the dynamic restrictions will not be set here, so we pick them out.
|
|
* @IN opExpressionList: the complete restrictions' list.
|
|
* @Return: the filtered restrictions' list
|
|
* @See also:
|
|
*/
|
|
static List *ExtractNonParamRestriction(List *opExpressionList)
|
|
{
|
|
ListCell *lc = NULL;
|
|
Expr *expr = NULL;
|
|
List *retRestriction = NIL;
|
|
|
|
foreach (lc, opExpressionList) {
|
|
expr = (Expr *)lfirst(lc);
|
|
if (IsA(expr, OpExpr)) {
|
|
Node *leftop = get_leftop(expr);
|
|
Node *rightop = get_rightop(expr);
|
|
if (rightop == NULL) {
|
|
continue;
|
|
}
|
|
|
|
if ((IsVarNode(leftop) && IsParamConst(rightop)) || (IsVarNode(rightop) && IsParamConst(leftop))) {
|
|
continue;
|
|
}
|
|
}
|
|
retRestriction = lappend(retRestriction, expr);
|
|
}
|
|
|
|
return retRestriction;
|
|
}
|
|
|
|
/*
|
|
* Extract the attribute type, attribute no and operator from the expression.
|
|
* @_in param expr: The expression to be parsed.
|
|
* @_out param strategy: The operator strategy of the expression.
|
|
* return the var in the predicate.
|
|
*/
|
|
static Var *GetHdfsPrediacateVarType(Expr *expr, HdfsQueryOperator &strategy)
|
|
{
|
|
Expr *leftop = NULL;
|
|
Expr *arg = NULL;
|
|
Oid opno = InvalidOid;
|
|
Var *var = NULL;
|
|
|
|
if (IsA(expr, OpExpr)) {
|
|
opno = ((OpExpr *)expr)->opno;
|
|
|
|
/* Get strategy number. */
|
|
strategy = GetHdfsScanStrategyNumber(opno);
|
|
|
|
/* Leftop should be var. Has been checked */
|
|
leftop = (Expr *)get_leftop(expr);
|
|
if (leftop && IsA(leftop, RelabelType)) {
|
|
leftop = ((RelabelType *)leftop)->arg;
|
|
}
|
|
if (leftop == NULL) {
|
|
ereport(ERROR, (errmodule(MOD_DFS), errmsg("The leftop is null")));
|
|
}
|
|
|
|
var = (Var *)leftop;
|
|
} else if (IsA(expr, NullTest)) {
|
|
/* Leftop should be var. Has been checked */
|
|
arg = ((NullTest *)expr)->arg;
|
|
if (arg && IsA(arg, RelabelType)) {
|
|
arg = ((RelabelType *)arg)->arg;
|
|
}
|
|
if (arg == NULL) {
|
|
ereport(ERROR, (errmodule(MOD_DFS), errmsg("The arg is null")));
|
|
}
|
|
|
|
var = (Var *)arg;
|
|
|
|
if (((NullTest *)expr)->nulltesttype == IS_NULL) {
|
|
strategy = HDFS_QUERY_ISNULL;
|
|
} else {
|
|
strategy = HDFS_QUERY_ISNOTNULL;
|
|
}
|
|
} else {
|
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_DFS),
|
|
errmsg("We only support pushing down opExpr and null test predicate.")));
|
|
}
|
|
|
|
return var;
|
|
}
|
|
|
|
/*
|
|
* brief: Build Predicate for pushdown of dfs scan.
|
|
* input param @strategy:the operator strategy of the expression;
|
|
* input param @var: the information of the attribute;
|
|
* input param @expr: expression which has been filtered by the optimizer;
|
|
* input param @ps: execution state for specific scan;
|
|
* input_out param @readerState: includes colNoMap to adjust the column
|
|
* index with ORC file and store the predicate for each column;
|
|
*/
|
|
template <class typeClass, typename baseType>
|
|
static void BuildHdfsPredicate(HdfsQueryOperator strategy, Var *var, Expr *expr, PlanState *ps,
|
|
dfs::reader::ReaderState *readerState)
|
|
{
|
|
List **hdfsScanPredicateArr = readerState->hdfsScanPredicateArr;
|
|
uint32 *colNoMap = readerState->colNoMapArr;
|
|
AttrNumber attNo = var->varattno;
|
|
Oid attType = var->vartype;
|
|
Oid collation = var->varcollid;
|
|
int predicateArrPos = 0;
|
|
bool runningTimeSet = false;
|
|
if (colNoMap == NULL) {
|
|
predicateArrPos = attNo - 1;
|
|
} else {
|
|
predicateArrPos = colNoMap[attNo - 1] - 1;
|
|
}
|
|
|
|
HdfsScanPredicate<typeClass, baseType> *predicate =
|
|
New(CurrentMemoryContext) HdfsScanPredicate<typeClass, baseType>(attNo, attType, strategy, collation,
|
|
var->vartypmod);
|
|
|
|
runningTimeSet =
|
|
predicate->BuildHdfsScanPredicateFromClause(expr, ps, readerState->scanstate, predicateArrPos,
|
|
list_length(hdfsScanPredicateArr[predicateArrPos]) + 1);
|
|
hdfsScanPredicateArr[predicateArrPos] = lappend(hdfsScanPredicateArr[predicateArrPos], predicate);
|
|
|
|
/* For the equal operator, generate the bloomfilter used to check stride skipping. */
|
|
if (u_sess->attr.attr_sql.enable_bloom_filter && !runningTimeSet && strategy == HDFS_QUERY_EQ &&
|
|
SATISFY_BLOOM_FILTER(attType)) {
|
|
filter::BloomFilter *bloomFilter = filter::createBloomFilter(attType, var->vartypmod, collation,
|
|
EQUAL_BLOOM_FILTER,
|
|
DEFAULT_ORC_BLOOM_FILTER_ENTRIES, false);
|
|
if (SECUREC_UNLIKELY(bloomFilter == NULL)) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_DFS), errmsg("bloomFilter create failed")));
|
|
}
|
|
bloomFilter->addValue(predicate->m_argument->getValue());
|
|
readerState->bloomFilters[attNo - 1] = bloomFilter;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* brief: Initialize the hdfs predicate which is going to push down.
|
|
* input param @readerState: The state of the reader which includes the informations needed.
|
|
* input param @ps: The state of foreign scan which includes the all the information about plan.
|
|
* input param @hdfsQual: The list of predicate pushed down.
|
|
*/
|
|
static void InitHdfsScanPredicateArr(dfs::reader::ReaderState *readerState, PlanState *ps, List *hdfsQual)
|
|
{
|
|
ListCell *lc = NULL;
|
|
HdfsQueryOperator strategy = HDFS_QUERY_INVALID;
|
|
|
|
foreach (lc, hdfsQual) {
|
|
Expr *expr = (Expr *)lfirst(lc);
|
|
Var *var = GetHdfsPrediacateVarType(expr, strategy);
|
|
|
|
switch (var->vartype) {
|
|
case BOOLOID:
|
|
case INT1OID:
|
|
case INT2OID:
|
|
case INT4OID:
|
|
case INT8OID: {
|
|
BuildHdfsPredicate<Int64Wrapper, int64>(strategy, var, expr, ps, readerState);
|
|
break;
|
|
}
|
|
case NUMERICOID: {
|
|
/*
|
|
* PushDownSupportVar() make sure that precision <= 38 for numeric
|
|
*/
|
|
uint32 precision = uint32(((uint32)(var->vartypmod - VARHDRSZ) >> 16) & 0xffff);
|
|
if (precision <= 18) {
|
|
BuildHdfsPredicate<Int64Wrapper, int64>(strategy, var, expr, ps, readerState);
|
|
} else {
|
|
BuildHdfsPredicate<Int128Wrapper, int128>(strategy, var, expr, ps, readerState);
|
|
}
|
|
break;
|
|
}
|
|
case FLOAT4OID:
|
|
case FLOAT8OID: {
|
|
BuildHdfsPredicate<Float8Wrapper, double>(strategy, var, expr, ps, readerState);
|
|
break;
|
|
}
|
|
case VARCHAROID:
|
|
case CLOBOID:
|
|
case TEXTOID:
|
|
case BPCHAROID: {
|
|
BuildHdfsPredicate<StringWrapper, char *>(strategy, var, expr, ps, readerState);
|
|
break;
|
|
}
|
|
case DATEOID:
|
|
case TIMESTAMPOID: {
|
|
BuildHdfsPredicate<TimestampWrapper, Timestamp>(strategy, var, expr, ps, readerState);
|
|
break;
|
|
}
|
|
default: {
|
|
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmodule(MOD_DFS),
|
|
errmsg("Data type %u has not been supported for predicate push down.", var->vartype)));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Construct three arraies which indicate the required columns, target columns and
|
|
* restriction columns by each.
|
|
* _out_param readerState: The state for reading.
|
|
* _in_param qual: The predicates pushed down to reader.
|
|
* _in_param columnList: The list of all the required columns.
|
|
* _in_param targetList: The list of the target columns.
|
|
* _in_param restrictColList: The list of the restriction columns.
|
|
*/
|
|
static void InitRequiredCols(dfs::reader::ReaderState *readerState, List *qual, List *columnList, List *targetList,
|
|
List *restrictColList)
|
|
{
|
|
ListCell *lc = NULL;
|
|
Var *variable = NULL;
|
|
readerState->isRequired = (bool *)palloc0(sizeof(bool) * readerState->relAttrNum);
|
|
readerState->targetRequired = (bool *)palloc0(sizeof(bool) * readerState->relAttrNum);
|
|
readerState->restrictRequired = (bool *)palloc0(sizeof(bool) * readerState->relAttrNum);
|
|
|
|
foreach (lc, columnList) {
|
|
variable = (Var *)lfirst(lc);
|
|
readerState->isRequired[variable->varattno - 1] = true;
|
|
}
|
|
|
|
foreach (lc, targetList) {
|
|
variable = (Var *)lfirst(lc);
|
|
readerState->targetRequired[variable->varattno - 1] = true;
|
|
}
|
|
|
|
/* Add the restrict columns which can not be pushed down into the target list. */
|
|
if (list_length(qual) > 0) {
|
|
List *usedList = NIL;
|
|
foreach (lc, qual) {
|
|
Node *clause = (Node *)lfirst(lc);
|
|
List *clauseList = NIL;
|
|
|
|
/* recursively pull up any columns used in the restriction clause */
|
|
clauseList = pull_var_clause(clause, PVC_RECURSE_AGGREGATES, PVC_RECURSE_PLACEHOLDERS);
|
|
|
|
usedList = MergeList(usedList, clauseList, readerState->relAttrNum);
|
|
list_free_ext(clauseList);
|
|
}
|
|
foreach (lc, usedList) {
|
|
variable = (Var *)lfirst(lc);
|
|
readerState->targetRequired[variable->varattno - 1] = true;
|
|
}
|
|
list_free_ext(usedList);
|
|
}
|
|
|
|
foreach (lc, restrictColList) {
|
|
variable = (Var *)lfirst(lc);
|
|
readerState->restrictRequired[variable->varattno - 1] = true;
|
|
}
|
|
}
|
|
|
|
void FillReaderState(dfs::reader::ReaderState *readerState, ScanState *ss, DfsPrivateItem *item, Snapshot snapshot)
|
|
{
|
|
uint32 i = 0;
|
|
Plan *plan = ss->ps.plan;
|
|
Assert(readerState != NULL);
|
|
if (readerState->persistCtx == NULL) {
|
|
readerState->persistCtx = AllocSetContextCreate(CurrentMemoryContext, "dfs reader context",
|
|
ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE,
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
|
}
|
|
readerState->rescanCtx = AllocSetContextCreate(CurrentMemoryContext, "dfs rescan context", ALLOCSET_DEFAULT_MINSIZE,
|
|
ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
|
|
/* To indicate which column will be used in the current query. */
|
|
readerState->relAttrNum = ss->ss_currentRelation->rd_att->natts;
|
|
readerState->queryRestrictionList = ExtractNonParamRestriction(item->opExpressionList);
|
|
readerState->runtimeRestrictionList = NIL;
|
|
readerState->staticPruneFiles = 0;
|
|
readerState->dynamicPrunFiles = 0;
|
|
readerState->bloomFilterRows = 0;
|
|
readerState->bloomFilterBlocks = 0;
|
|
readerState->minmaxFilterRows = 0;
|
|
|
|
/* init min/max statistics info */
|
|
readerState->minmaxCheckFiles = 0;
|
|
readerState->minmaxFilterFiles = 0;
|
|
readerState->minmaxCheckStripe = 0;
|
|
readerState->minmaxFilterStripe = 0;
|
|
readerState->minmaxCheckStride = 0;
|
|
readerState->minmaxFilterStride = 0;
|
|
|
|
readerState->orcMetaCacheBlockCount = 0;
|
|
readerState->orcMetaLoadBlockCount = 0;
|
|
readerState->orcDataCacheBlockCount = 0;
|
|
readerState->orcDataLoadBlockCount = 0;
|
|
readerState->orcMetaCacheBlockSize = 0;
|
|
readerState->orcMetaLoadBlockSize = 0;
|
|
readerState->orcDataCacheBlockSize = 0;
|
|
readerState->orcDataLoadBlockSize = 0;
|
|
|
|
readerState->currentFileID = 0;
|
|
readerState->currentFileSize = 0;
|
|
readerState->localBlock = 0;
|
|
readerState->remoteBlock = 0;
|
|
readerState->nnCalls = 0;
|
|
readerState->dnCalls = 0;
|
|
readerState->fdwEncoding = INVALID_ENCODING;
|
|
readerState->checkEncodingLevel = NO_ENCODING_CHECK;
|
|
readerState->incompatibleCount = 0;
|
|
readerState->dealWithCount = 0;
|
|
readerState->scanstate = ss;
|
|
readerState->snapshot = (snapshot == NULL) ? GetActiveSnapshot() : snapshot;
|
|
Assert(readerState->snapshot != NULL);
|
|
Assert(ss != NULL);
|
|
ss->runTimePredicatesReady = false;
|
|
ss->runTimeParamPredicates = NIL;
|
|
readerState->orderedCols = (uint32 *)palloc(sizeof(uint32) * readerState->relAttrNum);
|
|
for (i = 0; i < readerState->relAttrNum; i++) {
|
|
readerState->orderedCols[i] = i;
|
|
}
|
|
InitRequiredCols(readerState, plan->qual, item->columnList, item->targetList, item->restrictColList);
|
|
readerState->allColumnList = item->columnList;
|
|
|
|
/* Allocate and initialize the variables if the table is partitioned. */
|
|
if (((Scan *)plan)->isPartTbl) {
|
|
if (RelationIsForeignTable(ss->ss_currentRelation)) { /* value partition for hdfs foreign table */
|
|
readerState->partList = item->partList;
|
|
PartitionTblInit(readerState);
|
|
} else { /* value partition for hdfs table */
|
|
List *prunedSplitList = NULL;
|
|
|
|
/* try to prune necessary partitions here */
|
|
readerState->partList = item->partList;
|
|
prunedSplitList = PruningUnnecessaryPartitions(ss, readerState, item->columnList);
|
|
|
|
/* update scan list */
|
|
readerState->splitList = prunedSplitList;
|
|
PartitionTblInit(readerState);
|
|
}
|
|
} else {
|
|
readerState->partNum = 0;
|
|
readerState->partList = NIL;
|
|
readerState->colNoMapArr = NULL;
|
|
readerState->partitionColValueArr = NULL;
|
|
}
|
|
|
|
/*
|
|
* Because the ORC file dose not include partition column, not only must correct this
|
|
* hdfsScanPredicateArr number, but also call PartitionTblInit function.
|
|
* description: The partition table is CU format, do not repair this hdfsScanPredicateArr number
|
|
* and do not call PartitionTblInit function.
|
|
*/
|
|
readerState->hdfsScanPredicateArr =
|
|
(List **)palloc0(sizeof(List *) * (readerState->relAttrNum - readerState->partNum));
|
|
if (u_sess->attr.attr_sql.enable_hdfs_predicate_pushdown && list_length(item->hdfsQual) > 0) {
|
|
readerState->bloomFilters =
|
|
(filter::BloomFilter **)palloc0(sizeof(filter::BloomFilter *) * readerState->relAttrNum);
|
|
InitHdfsScanPredicateArr(readerState, &(ss->ps), item->hdfsQual);
|
|
SortReadColsByWeight(readerState, item->selectivity);
|
|
}
|
|
}
|