Files
openGauss-server/src/gausskernel/runtime/opfusion/opfusion_util.cpp
wuchenglin d8178cbb42 INSERT SUB FUSION
opfusion support insert ... select
2023-06-19 09:23:29 +08:00

1357 lines
43 KiB
C++

/*
* Copyright (c) 2020 Huawei Technologies Co.,Ltd.
*
* openGauss is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* ---------------------------------------------------------------------------------------
*
* opfusion_util.cpp
* The main part of the bypass executor. Instead of processing through the origin
* Portal executor, the bypass executor provides a shortcut when the query is
* simple.
*
* IDENTIFICATION
* src/gausskernel/runtime/executor/opfusion_util.cpp
*
* ---------------------------------------------------------------------------------------
*/
#include "opfusion/opfusion_util.h"
#include "access/printtup.h"
#include "access/transam.h"
#include "catalog/pg_aggregate.h"
#include "catalog/pg_partition_fn.h"
#include "catalog/pg_proc.h"
#include "commands/copy.h"
#include "executor/node/nodeIndexscan.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "parser/parsetree.h"
#include "utils/dynahash.h"
#include "utils/lsyscache.h"
#include "utils/snapmgr.h"
#include "utils/knl_partcache.h"
const char* getBypassReason(FusionType result)
{
switch (result) {
case NONE_FUSION: {
return "Bypass not executed";
}
case NOBYPASS_NO_CPLAN: {
return "Bypass not executed because the plan is custom plan";
}
case SELECT_FUSION: {
return "Bypass executed through select fusion";
}
case SELECT_FOR_UPDATE_FUSION: {
return "Bypass executed through select for update fusion";
}
case INSERT_FUSION: {
return "Bypass executed through insert fusion";
}
case UPDATE_FUSION: {
return "Bypass executed through update fusion";
}
case DELETE_FUSION: {
return "Bypass executed through delete fusion";
}
case INSERT_SUB_FUSION: {
return "Bypass executed through insert sub fusion";
}
case AGG_INDEX_FUSION: {
return "Bypass executed through agg fusion";
}
case SORT_INDEX_FUSION: {
return "Bypass executed through sort fusion";
}
case MOT_JIT_SELECT_FUSION: {
return "Bypass executed through MOT JIT select fusion";
}
case MOT_JIT_MODIFY_FUSION: {
return "Bypass executed through MOT JIT modify fusion";
}
case NOBYPASS_NO_SIMPLE_PLAN: {
return "Bypass not executed because the plan of query is not a simple plan";
}
case NOBYPASS_NO_QUERY_TYPE: {
return "Bypass not executed because query is not an avaliable bypass statement such as select, delete, "
"update and insert";
}
case NOBYPASS_NO_INDEXSCAN: {
return "Bypass not executed because query\'s scan operator is not index";
}
case NOBYPASS_ONLY_SUPPORT_BTREE_INDEX: {
return "Bypass not executed because only support btree index currently";
}
case NOBYPASS_INDEXSCAN_WITH_ORDERBY: {
return "Bypass not executed because query used indexscan with order by clause method";
}
case NOBYPASS_INDEXSCAN_WITH_QUAL: {
return "Bypass not executed because query used indexscan with qual";
}
case NOBYPASS_INDEXSCAN_CONDITION_INVALID: {
return "Bypass not executed because query used unsupported indexscan condition";
}
case NOBYPASS_INDEXONLYSCAN_WITH_ORDERBY: {
return "Bypass not executed because query used indexonlyscan with order by clause method";
}
case NOBYPASS_INDEXONLYSCAN_WITH_QUAL: {
return "Bypass not executed because query used indexonlyscan with qual";
}
case NOBYPASS_INDEXONLYSCAN_CONDITION_INVALID: {
return "Bypass not executed because query used invalid indexonlyscan condition";
}
case NOBYPASS_TARGET_WITH_SYS_COL: {
return "Bypass not executed because query used the target list with system column";
}
case NOBYPASS_TARGET_WITH_NO_TABLE_COL: {
return "Bypass not executed because query used the target list which only contains table's column";
}
case NOBYPASS_NO_TARGETENTRY: {
return "Bypass not executed because the type of targetlist of query should be targetEntry";
}
case NOBYPASS_PARAM_TYPE_INVALID: {
return "Bypass not executed because query used unsupported param type";
}
case NOBYPASS_DML_RELATION_NUM_INVALID: {
return "Bypass not executed because query\'s relation number is not 1";
}
case NOBYPASS_DML_RELATION_NOT_SUPPORT: {
return "Bypass not executed because query\'s relation is not support";
}
case NOBYPASS_DML_TARGET_TYPE_INVALID: {
return "Bypass not executed because query used unsupported DML target type";
}
case NOBYPASS_EXP_NOT_SUPPORT: {
return "Bypass not executed because the expression of query is not support";
}
case NOBYPASS_LIMITOFFSET_CONST_LESS_THAN_ZERO: {
return "Bypass not executed because query used limit offset grammar with const less than zero";
}
case NOBYPASS_LIMITCOUNT_CONST_LESS_THAN_ZERO: {
return "Bypass not executed because query used limit count grammar with const less than zero";
}
case NOBYPASS_LIMIT_NOT_CONST: {
return "Bypass not executed because query used limit grammar with a non-constant value";
}
case NOBYPASS_NO_SIMPLE_INSERT: {
return "Bypass not executed because query combines insert operator with others";
}
case NOBYPASS_INVALID_SELECT_FOR_UPDATE: {
return "Bypass not executed because query used invalid select for update";
}
case NOBYPASS_INVALID_MODIFYTABLE: {
return "Bypass not executed because query used invalid modifytable";
}
case NOBYPASS_STREAM_NOT_SUPPORT: {
return "Bypass not executed because query used streaming plan";
}
case NOBYPASS_NULLTEST_TYPE_INVALID: {
return "Bypass not executed because query used invalid composite type";
break;
}
case NOBYPASS_INVALID_PLAN: {
return "Bypass not executed because invalid plan node";
break;
}
case NOBYPASS_NOT_PLAIN_AGG: {
return "Bypass not executed because it's not a plain agg query";
break;
}
case NOBYPASS_ONE_TARGET_ALLOWED: {
return "Bypass not executed because it's just one target allowed";
break;
}
case NOBYPASS_AGGREF_TARGET_ALLOWED: {
return "Bypass not executed because it's just aggref allowed";
break;
}
case NOBYPASS_JUST_SUM_ALLOWED: {
return "Bypass not executed because it's sum() allowed";
break;
}
case NOBYPASS_JUST_VAR_FOR_AGGARGS: {
return "Bypass not executed because it's Var type allowed for agg argument";
break;
}
case NOBYPASS_JUST_MERGE_UNSUPPORTED: {
return "Bypass not executed because it's unsupported that sort node just merge results";
break;
}
case NOBYPASS_JUST_VAR_ALLOWED_IN_SORT: {
return "Bypass not executed because it's Var type allowed for target in sort query";
break;
}
case NOBYPASS_REPLACE_NOT_SUPPORT: {
return "Bypass not support REPLACE INTO statement";
break;
}
case NOBYPASS_UPSERT_NOT_SUPPORT: {
return "Bypass not support INSERT INTO ... ON DUPLICATE KEY UPDATE statement";
break;
}
case NOBYPASS_ZERO_PARTITION: {
return "Bypass not support query in zero partition";
break;
}
case NOBYPASS_MULTI_PARTITION: {
return "Bypass not support query in multiple partitions";
break;
}
case NOBYPASS_EXP_NOT_SUPPORT_IN_PARTITION: {
return "Bypass not executed because the expression of query is not support in partition table";
break;
}
case NO_BYPASS_PARTITIONKEY_IS_NULL: {
return "Bypass not executed because the partition key is null";
break;
}
case NOBYPASS_NO_UPDATE_PARTITIONKEY: {
return "Bypass not support update the partition key";
break;
}
case NOBYPASS_NO_INCLUDING_PARTITIONKEY: {
return "Bypass not executed because the partition key is not in the parameters";
break;
}
case NOBYPASS_PARTITION_BYPASS_NOT_OPEN: {
return "enable_partition_opfusion is in the closed state";
}
case NOBYPASS_PARTITION_TYPE_NOT_SUPPORT: {
return "Bypass only support in range partition currently";
}
case NOBYPASS_VERSION_SCAN_PLAN: {
return "Bypass not executed because the plan contains version table scan.";
break;
}
case NOBYPASS_INSERT_SUB_FUSION_NOT_SUPPORT_PARTITION_BYPASS: {
return "Bypass not executed because insert sub fusion not support partition table bypass.";
break;
}
case NOBYPASS_GPC_NOT_SUPPORT_PARTITION_BYPASS: {
return "Bypass not executed because GPC not support partition table bypass.";
break;
}
default: {
Assert(0);
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("unrecognized bypass support type: %d", (int)result)));
return NULL;
}
}
}
void BypassUnsupportedReason(FusionType result)
{
if (result == NONE_FUSION) {
return;
}
if (u_sess->attr.attr_sql.opfusion_debug_mode == BYPASS_OFF) {
return;
}
Assert(result != BYPASS_OK);
Assert(u_sess->attr.attr_sql.opfusion_debug_mode == BYPASS_LOG);
const char *bypass_reason = getBypassReason(result);
int elevel = DEBUG4;
if (result != BYPASS_OK) {
ereport(elevel, (errmodule(MOD_OPFUSION), errcode(ERRCODE_LOG),
errmsg("%s: \"%s\".", bypass_reason, t_thrd.postgres_cxt.debug_query_string)));
}
}
bool checkFusionParam(Param *param, ParamListInfo boundParams)
{
if (param->paramkind == PARAM_EXTERN && boundParams != NULL && param->paramid > 0 &&
param->paramid <= boundParams->numParams) {
ParamExternData *prm = &boundParams->params[param->paramid - 1];
if (OidIsValid(prm->ptype) && (prm->pflags & PARAM_FLAG_CONST)) {
return true;
}
}
return false;
}
static bool checkFlinfo(Node *node, bool *is_nextval)
{
/* check whether the flinfo satisfy conditon */
FmgrInfo *flinfo = NULL;
flinfo = (FmgrInfo *)palloc(sizeof(FmgrInfo));
fmgr_info(((FuncExpr *)node)->funcid, flinfo);
if (flinfo->fn_retset == true || (flinfo->fn_strict == false && flinfo->fn_expr == NULL)) {
pfree(flinfo);
flinfo = NULL;
return false;
}
/* Help function nextval_oid support SQL Bypass */
if (flinfo->fn_oid == NEXTVALFUNCOID) {
*is_nextval = true;
}
pfree(flinfo);
flinfo = NULL;
return true;
}
static bool checkExpr(Node *node, bool is_first)
{
NodeTag tag = nodeTag(node);
switch (tag) {
case T_Const:
case T_Param: {
return true;
}
case T_Var: {
return true;
}
case T_FuncExpr: {
if (is_first == false) {
return false;
}
bool is_nextval = false;
if (!checkFlinfo(node, &is_nextval)) {
return false;
}
if (is_nextval) {
return true;
}
bool found_ptr = true;
void *ans = NULL;
ans = hash_search(g_instance.exec_cxt.function_id_hashtbl, (void *)&((FuncExpr *)node)->funcid, HASH_FIND,
&found_ptr);
if (found_ptr == false) {
return false;
}
List *args = ((FuncExpr *)node)->args;
if (list_length(args) == 0 || list_length(args) > 4) {
return false;
}
bool result = true;
ListCell *lc = NULL;
foreach (lc, args) {
result = result && checkExpr((Node *)lfirst(lc), is_first);
is_first = false;
}
return result;
}
case T_OpExpr: {
if (is_first == false) {
return false;
}
/* check whether return set */
if (((OpExpr *)node)->opretset == true) {
return false;
}
List *args = ((OpExpr *)node)->args;
if (list_length(args) == 0 || list_length(args) > 4) {
return false;
}
bool result = true;
ListCell *lc = NULL;
foreach (lc, args) {
result = result && checkExpr((Node *)lfirst(lc), is_first);
is_first = false;
}
return result;
}
case T_RelabelType: {
return checkExpr((Node *)((RelabelType *)node)->arg, is_first);
}
default: {
return false;
}
}
}
FusionType checkFusionAgg(Agg *node, ParamListInfo params)
{
if (node->plan.righttree != NULL || node->plan.lefttree == NULL) {
return NOBYPASS_INVALID_PLAN;
}
/* check whether to have order by */
if (node->aggstrategy != AGG_PLAIN ||
node->groupingSets > 0) {
return NOBYPASS_NOT_PLAIN_AGG;
}
Assert (node->numCols == 0);
if (list_length(node->plan.targetlist) != 1 ||
node->plan.qual != NULL) {
return NOBYPASS_ONE_TARGET_ALLOWED;
}
TargetEntry *res = (TargetEntry *)linitial(node->plan.targetlist);
if (!IsA(res->expr, Aggref)) {
return NOBYPASS_AGGREF_TARGET_ALLOWED;
}
Aggref *aggref = (Aggref *)res->expr;
if (list_length(aggref->args) != 1 ||
aggref->aggorder != NULL ||
aggref->aggdistinct != NULL ||
aggref->aggvariadic) {
return NOBYPASS_AGGREF_TARGET_ALLOWED;
}
switch (aggref->aggfnoid) {
case INT2SUMFUNCOID:
case INT4SUMFUNCOID:
case INT8SUMFUNCOID:
case NUMERICSUMFUNCOID:
break;
default:
return NOBYPASS_JUST_SUM_ALLOWED;
}
res = (TargetEntry *)linitial(aggref->args);
if (!IsA(res->expr, Var)) {
return NOBYPASS_JUST_VAR_FOR_AGGARGS;
}
return BYPASS_OK;
}
FusionType checkFusionSort(Sort *node, ParamListInfo params)
{
if (node->plan.righttree != NULL || node->plan.lefttree == NULL) {
return NOBYPASS_INVALID_PLAN;
}
if (node->srt_start_merge) {
return NOBYPASS_JUST_MERGE_UNSUPPORTED;
}
ListCell *lc = NULL;
/* check whether targetlist is simple */
foreach (lc, node->plan.targetlist) {
Assert (IsA(lfirst(lc), TargetEntry));
TargetEntry *res = (TargetEntry *)lfirst(lc);
if (!IsA(res->expr, Var)) {
return NOBYPASS_JUST_VAR_ALLOWED_IN_SORT;
}
Var *var = (Var *)res->expr;
/* System columns, such as ctid and xmin, are not supported. */
if (var->varoattno <= 0) {
return NOBYPASS_TARGET_WITH_SYS_COL;
}
}
return BYPASS_OK;
}
template <bool is_dml, bool isonlyindex> FusionType checkFusionIndexScan(Node *node, ParamListInfo params)
{
List *tarlist = NULL;
List *indexorderby = NULL;
List *indexqual = NULL;
List *qual = NULL;
Oid indexOid = InvalidOid;
Relation index = NULL;
if (isonlyindex) {
tarlist = ((IndexOnlyScan *)node)->scan.plan.targetlist;
indexorderby = ((IndexOnlyScan *)node)->indexorderby;
indexqual = ((IndexOnlyScan *)node)->indexqual;
qual = ((IndexOnlyScan *)node)->scan.plan.qual;
indexOid = ((IndexOnlyScan *)node)->indexid;
if (indexorderby != NULL) {
return NOBYPASS_INDEXONLYSCAN_WITH_ORDERBY;
}
} else {
tarlist = ((IndexScan *)node)->scan.plan.targetlist;
indexorderby = ((IndexScan *)node)->indexorderby;
indexqual = ((IndexScan *)node)->indexqual;
qual = ((IndexScan *)node)->scan.plan.qual;
indexOid = ((IndexScan *)node)->indexid;
if (indexorderby != NULL) {
return NOBYPASS_INDEXSCAN_WITH_ORDERBY;
}
}
index = index_open(indexOid, AccessShareLock);
if (!OID_IS_BTREE(index->rd_rel->relam)) {
index_close(index, NoLock);
return NOBYPASS_ONLY_SUPPORT_BTREE_INDEX;
}
index_close(index, NoLock);
ListCell *lc = NULL;
if (is_dml == false) {
/* check whether targetlist is simple */
foreach (lc, tarlist) {
if (!IsA(lfirst(lc), TargetEntry)) {
return NOBYPASS_NO_TARGETENTRY;
}
TargetEntry *res = (TargetEntry *)lfirst(lc);
if (res->resjunk == true) {
continue;
}
if (!IsA(res->expr, Var)) {
return NOBYPASS_TARGET_WITH_NO_TABLE_COL;
}
Var *var = (Var*)res->expr;
AttrNumber attno = var->varno == INDEX_VAR ? var->varoattno : var->varattno;
if (attno <= 0) {
return NOBYPASS_TARGET_WITH_SYS_COL;
}
}
}
/* check whether index expression is simple */
foreach (lc, indexqual) {
if (IsA(lfirst(lc), NullTest)) {
if (((NullTest *)lfirst(lc))->argisrow == true) {
return NOBYPASS_NULLTEST_TYPE_INVALID;
}
continue;
}
if (!IsA(lfirst(lc), OpExpr)) {
return NOBYPASS_INDEXSCAN_CONDITION_INVALID;
}
OpExpr *opexpr = (OpExpr *)lfirst(lc);
if (list_length(opexpr->args) != 2) {
if (isonlyindex) {
return NOBYPASS_INDEXONLYSCAN_CONDITION_INVALID;
} else {
return NOBYPASS_INDEXSCAN_CONDITION_INVALID;
}
}
Expr *leftop = NULL; /* expr on lhs of operator */
Expr *rightop = NULL; /* expr on rhs ... */
leftop = (Expr *)linitial(opexpr->args);
if (leftop != NULL && IsA(leftop, RelabelType)) {
leftop = ((RelabelType *)leftop)->arg;
}
rightop = (Expr *)lsecond(opexpr->args);
if (rightop != NULL && IsA(rightop, RelabelType)) {
rightop = ((RelabelType *)rightop)->arg;
}
if (leftop == NULL || rightop == NULL) {
if (isonlyindex) {
return NOBYPASS_INDEXONLYSCAN_CONDITION_INVALID;
} else {
return NOBYPASS_INDEXSCAN_CONDITION_INVALID;
}
}
if (!IsA(leftop, Var) || (!IsA(rightop, Param) && !IsA(rightop, Const))) {
if (isonlyindex) {
return NOBYPASS_INDEXONLYSCAN_CONDITION_INVALID;
} else {
return NOBYPASS_INDEXSCAN_CONDITION_INVALID;
}
}
if (IsA(rightop, Param) && !checkFusionParam((Param *)rightop, params)) {
return NOBYPASS_PARAM_TYPE_INVALID;
}
}
/* check whether filter expression is simple */
if (qual != NULL) {
if (isonlyindex) {
return NOBYPASS_INDEXONLYSCAN_WITH_QUAL;
} else {
return NOBYPASS_INDEXSCAN_WITH_QUAL;
}
}
return BYPASS_OK;
}
/* check expression can be used for pruning */
void CheckExprPartitionTable(Node* node, ParamListInfo params, FusionType* ftype)
{
PruningResult* result = IsA(node, IndexScan) ? ((IndexScan *)node)->scan.pruningInfo
: ((IndexOnlyScan *)node)->scan.pruningInfo;
Param* paramArg = result->paramArg;
if (paramArg == NULL) {
*ftype = NOBYPASS_NO_INCLUDING_PARTITIONKEY;
return;
}
if (params->params[paramArg->paramid - 1].isnull) {
*ftype = NO_BYPASS_PARTITIONKEY_IS_NULL;
return;
}
Expr* expr = result->expr;
switch (nodeTag(expr)) {
case T_BoolExpr: {
BoolExpr* boolExpr = (BoolExpr *)expr;
int count = 0;
ListCell* cell = NULL;
foreach (cell, boolExpr->args) {
if (count == result->paramArg->paramid - 1) {
if (nodeTag(cell) == T_BoolExpr) {
*ftype = NOBYPASS_EXP_NOT_SUPPORT_IN_PARTITION;
return;
}
OpExpr* arg = (OpExpr*)lfirst(cell);
char* opName = get_opname(arg->opno);
if (strncmp("=", opName, 1) != 0) {
*ftype = NOBYPASS_EXP_NOT_SUPPORT_IN_PARTITION;
return;
}
} else {
count += 1;
continue;
}
}
return;
} break;
case T_OpExpr: {
OpExpr* opExpr = (OpExpr*)expr;
char* opName = get_opname(opExpr->opno);
Assert(opName != NULL);
if (strncmp("=", opName, 1) != 0) {
*ftype = NOBYPASS_EXP_NOT_SUPPORT_IN_PARTITION;
return;
} else {
return;
}
} break;
default: {
*ftype = NOBYPASS_EXP_NOT_SUPPORT_IN_PARTITION;
return;
} break;
}
return;
}
void CheckFusionPartitionNumber(FusionType* ftype, Scan scan)
{
if (scan.itrs == 0) {
*ftype = NOBYPASS_ZERO_PARTITION;
return;
}
if (scan.itrs > 1) {
*ftype = NOBYPASS_MULTI_PARTITION;
return;
}
return;
}
bool checkPartitionType(const Relation rel)
{
if (!RELATION_IS_PARTITIONED(rel)) {
return false;
}
if (rel->partMap->type == PART_TYPE_RANGE) {
return false;
} else {
return true;
}
}
bool checkDMLRelation(const Relation rel, const PlannedStmt *plannedstmt, bool isInsert, bool isPartTbl)
{
bool result = false;
if (rel->rd_rel->relkind != RELKIND_RELATION || rel->rd_rel->relhasrules || rel->rd_rel->relhastriggers ||
rel->rd_rel->relhasoids || rel->rd_rel->relhassubclass || RelationIsColStore(rel) || RelationIsTsStore(rel) ||
RelationInRedistribute(rel) || plannedstmt->hasReturning || RelationIsSubPartitioned(rel)) {
result = true;
}
if (isInsert) {
return result;
} else if (!isPartTbl && RELATION_IS_PARTITIONED(rel)) {
result = true;
}
return result;
}
FusionType getSelectFusionType(List *stmt_list, ParamListInfo params)
{
FusionType ftype = SELECT_FUSION;
bool limitplan = false;
bool isPartTbl = false;
Index res_rel_idx = 0;
/* check whether is only one index scan */
PlannedStmt *plannedstmt = (PlannedStmt *)linitial(stmt_list);
Plan *top_plan = plannedstmt->planTree;
/* check for limit */
if (IsA(top_plan, Limit)) {
Limit *limit = (Limit *)top_plan;
if (limit->limitOffset != NULL) {
if (IsA(limit->limitOffset, Const)) {
Assert(((Const *)limit->limitOffset)->consttype == 20);
if (DatumGetInt64(((Const *)limit->limitOffset)->constvalue) < 0) {
return NOBYPASS_LIMITOFFSET_CONST_LESS_THAN_ZERO;
}
} else {
return NOBYPASS_LIMIT_NOT_CONST;
}
}
if (limit->limitCount != NULL) {
if (IsA(limit->limitCount, Const)) {
Assert(((Const *)limit->limitCount)->consttype == 20);
if (DatumGetInt64(((Const *)limit->limitCount)->constvalue) < 0) {
return NOBYPASS_LIMITCOUNT_CONST_LESS_THAN_ZERO;
}
} else {
return NOBYPASS_LIMIT_NOT_CONST;
}
}
top_plan = top_plan->lefttree;
limitplan = true;
}
/* check select for update */
if (IsA(top_plan, LockRows)) {
LockRows *lockrows = (LockRows *)top_plan;
bool is_select_for_update =
(
list_length(lockrows->rowMarks) == 1 && IsA(linitial(lockrows->rowMarks), PlanRowMark) &&
((PlanRowMark *)linitial(lockrows->rowMarks))->markType == ROW_MARK_EXCLUSIVE &&
(
((PlanRowMark *)linitial(lockrows->rowMarks))->waitPolicy == LockWaitBlock ||
((PlanRowMark *)linitial(lockrows->rowMarks))->waitPolicy == LockWaitSkip
) &&
((PlanRowMark *)linitial(lockrows->rowMarks))->waitSec == 0
);
if (is_select_for_update) {
top_plan = top_plan->lefttree;
ftype = SELECT_FOR_UPDATE_FUSION;
} else {
return NOBYPASS_INVALID_SELECT_FOR_UPDATE;
}
}
#ifndef ENABLE_MULTIPLE_NODES
/* check select for agg */
if (u_sess->attr.attr_sql.enable_beta_opfusion && !limitplan && IsA(top_plan, Agg) &&
ftype == SELECT_FUSION) {
FusionType ttype;
ttype = checkFusionAgg((Agg *)top_plan, params);
if (ttype > BYPASS_OK) {
return ttype;
}
ftype = AGG_INDEX_FUSION;
top_plan = top_plan->lefttree;
}
/* check select for sort */
if (u_sess->attr.attr_sql.enable_beta_opfusion && !limitplan && IsA(top_plan, Sort) &&
ftype == SELECT_FUSION) {
FusionType ttype;
ttype = checkFusionSort((Sort*)top_plan, params);
if (ttype > BYPASS_OK) {
return ttype;
}
ftype = SORT_INDEX_FUSION;
top_plan = top_plan->lefttree;
}
#endif
/* check for partition table */
if (IsA(top_plan, PartIterator)) {
if (!u_sess->attr.attr_sql.enable_partition_opfusion) {
return NOBYPASS_PARTITION_BYPASS_NOT_OPEN;
} else if (ENABLE_GPC) {
return NOBYPASS_GPC_NOT_SUPPORT_PARTITION_BYPASS;
} else {
top_plan = top_plan->lefttree;
}
}
/* check for indexscan or indexonlyscan */
if ((IsA(top_plan, IndexScan) || IsA(top_plan, IndexOnlyScan)) && top_plan->lefttree == NULL) {
FusionType ttype;
if (IsA(top_plan, IndexScan)) {
ttype = checkFusionIndexScan<false, false>((Node *)top_plan, params);
IndexScan* node = (IndexScan *)top_plan;
isPartTbl = node->scan.isPartTbl;
res_rel_idx = node->scan.scanrelid;
} else {
ttype = checkFusionIndexScan<false, true>((Node *)top_plan, params);
IndexOnlyScan* node = (IndexOnlyScan *)top_plan;
isPartTbl = node->scan.isPartTbl;
res_rel_idx = node->scan.scanrelid;
}
/* check failed */
if (ttype > BYPASS_OK) {
return ttype;
}
} else {
return NOBYPASS_NO_INDEXSCAN;
}
/*
* recheck partition, when set partition_iterator_elimination,
* PartIterator may be eliminated.
*/
if (isPartTbl) {
if (!u_sess->attr.attr_sql.enable_partition_opfusion) {
return NOBYPASS_PARTITION_BYPASS_NOT_OPEN;
} else if (ENABLE_GPC) {
return NOBYPASS_GPC_NOT_SUPPORT_PARTITION_BYPASS;
}
}
Oid relid = getrelid(res_rel_idx, plannedstmt->rtable);
Relation rel = heap_open(relid, AccessShareLock);
if (checkDMLRelation(rel, plannedstmt, false, isPartTbl)) {
heap_close(rel, AccessShareLock);
return NOBYPASS_DML_RELATION_NOT_SUPPORT;
}
if (checkPartitionType(rel)) {
heap_close(rel, AccessShareLock);
return NOBYPASS_PARTITION_TYPE_NOT_SUPPORT;
}
heap_close(rel, AccessShareLock);
/* check for the number of partitions */
if (IsA(top_plan, IndexScan)) {
IndexScan* scan = (IndexScan *)top_plan;
if (scan->scan.isPartTbl) {
CheckFusionPartitionNumber(&ftype, ((IndexScan *)scan)->scan);
if (params != NULL) {
CheckExprPartitionTable((Node *)scan, params, &ftype);
}
}
} else {
IndexOnlyScan* scan = (IndexOnlyScan *)top_plan;
if (scan->scan.isPartTbl) {
CheckFusionPartitionNumber(&ftype, ((IndexOnlyScan *)scan)->scan);
if (params != NULL) {
CheckExprPartitionTable((Node *)scan, params, &ftype);
}
}
}
return ftype;
}
void checkTargetlist(List *targetList, FusionType* ftype)
{
ListCell *lc = NULL;
TargetEntry *target = NULL;
foreach (lc, targetList) {
target = (TargetEntry *)lfirst(lc);
if (target->resjunk && nodeTag((Node *)target->expr) == T_Var) {
continue;
}
if (!checkExpr((Node *)target->expr, true)) {
*ftype = NOBYPASS_EXP_NOT_SUPPORT;
return;
}
}
return;
}
FusionType checkBaseResult(Plan* top_plan)
{
FusionType result = INSERT_FUSION;
ModifyTable *node = (ModifyTable *)top_plan;
if (IsA(linitial(node->plans), SeqScan) && list_length(node->plans) == 1) {
result = INSERT_SUB_FUSION;
/* may be we need do some extra check here like BaseResult? */
} else if (IsA(linitial(node->plans), BaseResult)) {
BaseResult *base = (BaseResult *)linitial(node->plans);
if (base->plan.lefttree != NULL || base->plan.initPlan != NIL || base->resconstantqual != NULL) {
return NOBYPASS_NO_SIMPLE_INSERT;
}
} else {
return NOBYPASS_NO_SIMPLE_INSERT;
}
if (node->upsertAction != UPSERT_NONE) {
return NOBYPASS_UPSERT_NOT_SUPPORT;
}
if (node->isReplace) {
return NOBYPASS_REPLACE_NOT_SUPPORT;
}
return result;
}
FusionType getInsertFusionType(List *stmt_list, ParamListInfo params)
{
FusionType ftype = INSERT_FUSION;
/* check result relaiton */
PlannedStmt *plannedstmt = (PlannedStmt *)linitial(stmt_list);
if (plannedstmt->resultRelations == NULL || list_length((List*)linitial(plannedstmt->resultRelations)) != 1) {
return NOBYPASS_DML_RELATION_NUM_INVALID;
}
Plan *top_plan = plannedstmt->planTree;
#ifdef ENABLE_MULTIPLE_NODES
if (!IsA(top_plan, ModifyTable) || top_plan->plan_node_id != 0) {
return NOBYPASS_INVALID_MODIFYTABLE;
}
#else
if (!IsA(top_plan, ModifyTable)) {
return NOBYPASS_INVALID_MODIFYTABLE;
}
#endif
/* check subquery num */
/* we also check SeqScan here, so ftype may be INSERT_SUB_FUSION */
ftype = checkBaseResult(top_plan);
if (ftype > BYPASS_OK) {
return ftype;
}
ModifyTable *node = (ModifyTable *)top_plan;
/* check relation */
Index res_rel_idx = linitial_int((List*)linitial(plannedstmt->resultRelations));
Oid relid = getrelid(res_rel_idx, plannedstmt->rtable);
Relation rel = heap_open(relid, RowExclusiveLock);
for (int i = 0; i < rel->rd_att->natts; i++) {
if (rel->rd_att->attrs[i].attisdropped) {
continue;
}
/* check whether the attrs of */
HeapTuple tuple = SearchSysCache1(TYPEOID, ObjectIdGetDatum(rel->rd_att->attrs[i].atttypid));
if (!HeapTupleIsValid(tuple)) {
/* should not happen */
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED),
errmsg("cache lookup failed for type %u", rel->rd_att->attrs[i].atttypid)));
}
Form_pg_type type_form = (Form_pg_type)GETSTRUCT(tuple);
ReleaseSysCache(tuple);
if (type_form->typtype != 'b') {
heap_close(rel, NoLock);
return NOBYPASS_DML_TARGET_TYPE_INVALID;
}
}
if (checkDMLRelation(rel, plannedstmt, true, RELATION_IS_PARTITIONED(rel))) {
heap_close(rel, NoLock);
return NOBYPASS_DML_RELATION_NOT_SUPPORT;
}
if (RELATION_IS_PARTITIONED(rel) && !u_sess->attr.attr_sql.enable_partition_opfusion) {
heap_close(rel, NoLock);
return NOBYPASS_PARTITION_BYPASS_NOT_OPEN;
}
if (RELATION_IS_PARTITIONED(rel) && ftype == INSERT_SUB_FUSION) {
heap_close(rel, AccessShareLock);
return NOBYPASS_PARTITION_BYPASS_NOT_OPEN;
}
if (RELATION_IS_PARTITIONED(rel) && ENABLE_GPC) {
heap_close(rel, NoLock);
return NOBYPASS_GPC_NOT_SUPPORT_PARTITION_BYPASS;
}
if (checkPartitionType(rel)) {
heap_close(rel, NoLock);
return NOBYPASS_PARTITION_TYPE_NOT_SUPPORT;
}
heap_close(rel, NoLock);
/*
* check targetlist
* maybe expr type is FuncExpr because of type conversion.
*/
List *targetlist = NIL;
if (IsA(linitial(node->plans), SeqScan)) {
targetlist = ((SeqScan*)linitial(node->plans))->plan.targetlist;
} else {
targetlist = ((BaseResult*)linitial(node->plans))->plan.targetlist;
}
checkTargetlist(targetlist, &ftype);
return ftype;
}
FusionType getUpdateFusionType(List *stmt_list, ParamListInfo params)
{
FusionType ftype = UPDATE_FUSION;
/* check result relaiton */
PlannedStmt *plannedstmt = (PlannedStmt *)linitial(stmt_list);
if (plannedstmt->resultRelations == NULL || list_length((List*)linitial(plannedstmt->resultRelations)) != 1) {
return NOBYPASS_DML_RELATION_NUM_INVALID;
}
Plan* top_plan = plannedstmt->planTree;
#ifdef ENABLE_MULTIPLE_NODES
if (!IsA(top_plan, ModifyTable) || top_plan->plan_node_id != 0) {
return NOBYPASS_INVALID_MODIFYTABLE;
}
#else
if (!IsA(top_plan, ModifyTable)) {
return NOBYPASS_INVALID_MODIFYTABLE;
}
#endif
/* check subquery num */
ModifyTable *node = (ModifyTable *)top_plan;
if (list_length(node->plans) != 1) {
return NOBYPASS_NO_SIMPLE_PLAN;
}
Plan *updatePlan = (Plan *)linitial(node->plans);
if (IsA(updatePlan, PartIterator)) {
if (!u_sess->attr.attr_sql.enable_partition_opfusion) {
return NOBYPASS_PARTITION_BYPASS_NOT_OPEN;
} else if (ENABLE_GPC) {
return NOBYPASS_GPC_NOT_SUPPORT_PARTITION_BYPASS;
} else {
updatePlan = updatePlan->lefttree;
}
}
if (!IsA(updatePlan, IndexScan)) {
return NOBYPASS_NO_INDEXSCAN;
}
/* check index scan */
FusionType ttype = checkFusionIndexScan<true, false>((Node *)updatePlan, params);
/* check failed */
if (ttype > BYPASS_OK) {
return ttype;
}
/* check relation */
IndexScan *indexscan = (IndexScan *)updatePlan;
Index res_rel_idx = linitial_int((List*)linitial(plannedstmt->resultRelations));
Oid relid = getrelid(res_rel_idx, plannedstmt->rtable);
Relation rel = heap_open(relid, RowExclusiveLock);
if (checkDMLRelation(rel, plannedstmt, false, indexscan->scan.isPartTbl)) {
heap_close(rel, NoLock);
return NOBYPASS_DML_RELATION_NOT_SUPPORT;
}
if (checkPartitionType(rel)) {
heap_close(rel, NoLock);
return NOBYPASS_PARTITION_TYPE_NOT_SUPPORT;
}
heap_close(rel, NoLock);
/* check target list */
if (node->partKeyUpdated) {
return NOBYPASS_NO_UPDATE_PARTITIONKEY;
}
List *targetlist = indexscan->scan.plan.targetlist;
checkTargetlist(targetlist, &ftype);
/* check the number of partitions */
if (indexscan->scan.isPartTbl) {
CheckFusionPartitionNumber(&ftype, indexscan->scan);
if (params != NULL) {
CheckExprPartitionTable((Node *)indexscan, params, &ftype);
}
}
return ftype;
}
FusionType getDeleteFusionType(List *stmt_list, ParamListInfo params)
{
FusionType ftype = DELETE_FUSION;
/* check result relaiton */
PlannedStmt *plannedstmt = (PlannedStmt *)linitial(stmt_list);
if (plannedstmt->resultRelations == NULL || list_length((List*)linitial(plannedstmt->resultRelations)) != 1) {
return NOBYPASS_DML_RELATION_NUM_INVALID;
}
Plan* top_plan = plannedstmt->planTree;
#ifdef ENABLE_MULTIPLE_NODES
if (!IsA(top_plan, ModifyTable) || top_plan->plan_node_id != 0) {
return NOBYPASS_INVALID_MODIFYTABLE;
}
#else
if (!IsA(top_plan, ModifyTable)) {
return NOBYPASS_INVALID_MODIFYTABLE;
}
#endif
/* check subquery num */
ModifyTable *node = (ModifyTable *)top_plan;
if (list_length(node->plans) != 1) {
return NOBYPASS_NO_SIMPLE_PLAN;
}
Plan *deletePlan = (Plan *)linitial(node->plans);
if (IsA(deletePlan, PartIterator)) {
if (!u_sess->attr.attr_sql.enable_partition_opfusion) {
return NOBYPASS_PARTITION_BYPASS_NOT_OPEN;
} else if (ENABLE_GPC) {
return NOBYPASS_GPC_NOT_SUPPORT_PARTITION_BYPASS;
} else {
deletePlan = deletePlan->lefttree;
}
}
if (!IsA(deletePlan, IndexScan)) {
return NOBYPASS_NO_INDEXSCAN;
}
/* check index scan */
FusionType ttype = checkFusionIndexScan<true, false>((Node *)deletePlan, params);
/* check failed */
if (ttype > BYPASS_OK) {
return ttype;
}
IndexScan* indexscan = (IndexScan *)deletePlan;
/* check relation */
Index res_rel_idx = linitial_int((List*)linitial(plannedstmt->resultRelations));
Oid relid = getrelid(res_rel_idx, plannedstmt->rtable);
Relation rel = heap_open(relid, RowExclusiveLock);
if (checkDMLRelation(rel, plannedstmt, false, indexscan->scan.isPartTbl)) {
heap_close(rel, NoLock);
return NOBYPASS_DML_RELATION_NOT_SUPPORT;
}
if (checkPartitionType(rel)) {
heap_close(rel, NoLock);
return NOBYPASS_PARTITION_TYPE_NOT_SUPPORT;
}
heap_close(rel, NoLock);
/* check the number of partitions */
if (indexscan->scan.isPartTbl) {
CheckFusionPartitionNumber(&ftype, indexscan->scan);
if (params != NULL) {
CheckExprPartitionTable((Node *)indexscan, params, &ftype);
}
}
return ftype;
}
void InitOpfusionFunctionId()
{
/* init opfusion function_id hash table */
HASHCTL ctl_func;
errno_t rc;
rc = memset_s(&ctl_func, sizeof(ctl_func), 0, sizeof(ctl_func));
securec_check_c(rc, "\0", "\0");
ctl_func.keysize = sizeof(Oid);
ctl_func.entrysize = sizeof(Oid);
ctl_func.hcxt = g_instance.instance_context;
g_instance.exec_cxt.function_id_hashtbl =
hash_create("Opfusion function id white-list", OPFUSION_FUNCTION_ID_MAX_HASH_SIZE, &ctl_func, HASH_ELEM);
/* enter function id from array to hash table */
uint32 i = 0;
bool found_ptr = false;
uint32 length = sizeof(function_id) / sizeof(Oid);
void *ans = NULL;
for (i = 0; i < length; i++) {
ans = hash_search(g_instance.exec_cxt.function_id_hashtbl, (void *)&function_id[i], HASH_ENTER, &found_ptr);
}
}
void tpslot_free_heaptuple(TupleTableSlot* reslot)
{
if (reslot->tts_tuple) {
heap_freetuple((HeapTuple)reslot->tts_tuple);
reslot->tts_tuple = NULL;
}
}
/* judge plan node is partiterator */
Node* JudgePlanIsPartIterator(Plan* plan)
{
Node* node = NULL;
if (IsA(plan, PartIterator)) {
node = (Node*)plan->lefttree;
} else {
node = (Node*)plan;
}
return node;
}
/* init partition by the ScanFusion */
void InitPartitionByScanFusion(Relation rel, Relation* fakRel, Partition* part, EState* estate,
const ScanFusion* scan)
{
if (RELATION_IS_PARTITIONED(rel)) {
estate->esfRelations = NULL;
*fakRel = scan->m_rel;
*part = scan->m_partRel;
}
}
/* init bucket relation */
Relation InitBucketRelation(int2 bucketid, Relation rel, Partition part)
{
Relation bucketRel = NULL;
if (bucketid != InvalidBktId) {
bucketRel = bucketGetRelation(rel, part, bucketid);
} else {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
errmsg("Invaild Oid when open hash bucket relation.")));
}
return bucketRel;
}
/* execute the process of done in the Fusion */
void ExecDoneStepInFusion(EState* estate)
{
if (estate->esfRelations) {
FakeRelationCacheDestroy(estate->esfRelations);
estate->esfRelations = NULL;
}
}
/* init the Partition Oid in construct */
Oid GetRelOidForPartitionTable(Scan scan, const Relation rel, ParamListInfo params)
{
Oid relOid = InvalidOid;
if (params != NULL) {
Param* paramArg = scan.pruningInfo->paramArg;
relOid = GetPartitionOidByParam(rel->partMap, paramArg, &(params->params[paramArg->paramid - 1]));
} else {
Assert((list_length(scan.pruningInfo->ls_rangeSelectedPartitions) != 0));
int partId = linitial_int(scan.pruningInfo->ls_rangeSelectedPartitions);
int partitionno = linitial_int(scan.pruningInfo->ls_selectedPartitionnos);
relOid = getPartitionOidFromSequence(rel, partId, partitionno);
}
return relOid;
}
/* init the index in construct */
Relation InitPartitionIndexInFusion(Oid parentIndexOid, Oid partOid, Partition *partIndex, Relation *parentIndex,
Relation rel)
{
*parentIndex = relation_open(parentIndexOid, AccessShareLock);
Oid partIndexOid;
if (ENABLE_SQL_BETA_FEATURE(PARTITION_OPFUSION) && list_length(rel->rd_indexlist) == 1) {
partIndexOid = lfirst_oid(rel->rd_indexlist->head);
} else {
partIndexOid = getPartitionIndexOid(parentIndexOid, partOid);
}
*partIndex = partitionOpen(*parentIndex, partIndexOid, AccessShareLock);
Relation index;
if (PARTITION_ENABLE_CACHE_OPFUSION) {
if (!(*partIndex)->partrel) {
partitionInitPartRel(*parentIndex, *partIndex);
}
(*partIndex)->partrel->pgstat_info = (*partIndex)->pd_pgstat_info;
index = (*partIndex)->partrel;
} else {
index = partitionGetRelation(*parentIndex, *partIndex);
}
return index;
}
/* init the Partition in construct */
void InitPartitionRelationInFusion(Oid partOid, Relation parentRel, Partition* partRel, Relation* rel)
{
*partRel = partitionOpen(parentRel, partOid, AccessShareLock);
(void)PartitionGetPartIndexList(*partRel);
if (PARTITION_ENABLE_CACHE_OPFUSION) {
if (!(*partRel)->partrel) {
partitionInitPartRel(parentRel, *partRel);
}
(*partRel)->partrel->pgstat_info = (*partRel)->pd_pgstat_info;
*rel = (*partRel)->partrel;
} else {
*rel = partitionGetRelation(parentRel, *partRel);
}
}
/* execute the process of done in construct */
void ExeceDoneInIndexFusionConstruct(bool isPartTbl, Relation* parentRel, Partition* part,
Relation* index, Relation* rel)
{
if (isPartTbl) {
if (*index != NULL) {
if (!PARTITION_ENABLE_CACHE_OPFUSION) {
releaseDummyRelation(index);
}
*index = NULL;
}
if (!PARTITION_ENABLE_CACHE_OPFUSION) {
releaseDummyRelation(rel);
}
partitionClose(*parentRel, *part, AccessShareLock);
heap_close(*parentRel, AccessShareLock);
*parentRel = NULL;
*part = NULL;
*rel = NULL;
} else {
heap_close((*index == NULL) ? *rel : *index, AccessShareLock);
*index = NULL;
*rel = NULL;
}
}