openGauss-server/src/gausskernel/runtime/executor/opfusion_util.cpp

927 lines
29 KiB
C++
Executable File

/*
* 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 "commands/copy.h"
#include "executor/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 "parser/parse_coerce.h"
const char *getBypassReason(FusionType result)
{
switch (result) {
case NONE_FUSION: {
return "Bypass not executed";
}
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 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_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_UPSERT_NOT_SUPPORT: {
return "Bypass not support INSERT INTO ... ON DUPLICATE KEY UPDATE statement";
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.", bypass_reason)));
}
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)
{
/* 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;
}
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_Var:
case T_Param: {
return true;
}
case T_FuncExpr: {
if (is_first == false) {
return false;
}
if (!checkFlinfo(node)) {
return false;
}
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;
if (isonlyindex) {
tarlist = ((IndexOnlyScan *)node)->scan.plan.targetlist;
indexorderby = ((IndexOnlyScan *)node)->indexorderby;
indexqual = ((IndexOnlyScan *)node)->indexqual;
qual = ((IndexOnlyScan *)node)->scan.plan.qual;
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;
if (indexorderby != NULL) {
return NOBYPASS_INDEXSCAN_WITH_ORDERBY;
}
}
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;
}
FusionType checkFusionNestLoop(NestLoop *node, ParamListInfo params)
{
Join *joinNode = &node->join;
Plan *plan = &joinNode->plan;
ListCell *lc = NULL;
/* NestLoop */
if (node->nestParams != NIL || node->materialAll == true) {
return NONE_FUSION;
}
/* join */
if (joinNode->jointype != JOIN_INNER ||
joinNode->joinqual != NIL ||
joinNode->nulleqqual != NIL ||
joinNode->optimizable == true ||
joinNode->skewoptimize != 0) {
return NONE_FUSION;
}
/* check whether targetlist is simple */
foreach (lc, plan->targetlist) {
Assert (IsA(lfirst(lc), TargetEntry));
TargetEntry *res = (TargetEntry *)lfirst(lc);
if (!IsA(res->expr, Var)) {
return NONE_FUSION;
}
Var *var = (Var *)res->expr;
/* System columns, such as ctid and xmin, are not supported. */
if (var->varoattno <= 0) {
return NONE_FUSION;
}
}
/* Plan */
if (plan->lefttree == NULL ||
!IsA(plan->lefttree, IndexScan) ||
plan->lefttree->lefttree != NULL ||
plan->righttree == NULL ||
!IsA(plan->righttree, IndexScan) ||
plan->righttree->lefttree != NULL) {
return NONE_FUSION;
}
/* IndexScan */
FusionType ttype;
ttype = checkFusionIndexScan<false, false>((Node*)plan->lefttree, params);
if (ttype > BYPASS_OK) {
return ttype;
}
ttype = checkFusionIndexScan<false, false>((Node*)plan->righttree, params);
if (ttype > BYPASS_OK) {
return ttype;
}
/* OK */
return NESTLOOP_INDEX_FUSION;
}
FusionType getSelectFusionType(List *stmt_list, ParamListInfo params)
{
FusionType ftype = SELECT_FUSION;
bool limitplan = false;
/* check whether is only one index scan */
Plan *top_plan = ((PlannedStmt *)linitial(stmt_list))->planTree;
#ifndef ENABLE_MULTIPLE_NODES
/* check for nestloop */
if (u_sess->attr.attr_sql.enable_beta_opfusion &&
u_sess->attr.attr_sql.enable_beta_nestloop_fusion && IsA(top_plan, NestLoop)) {
return checkFusionNestLoop((NestLoop *)top_plan, params);
}
#endif
/* 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))->noWait == false);
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 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);
} else {
ttype = checkFusionIndexScan<false, true>((Node *)top_plan, params);
}
/* check failed */
if (ttype > BYPASS_OK) {
return ttype;
}
} else {
return NOBYPASS_NO_INDEXSCAN;
}
return ftype;
}
FusionType checkTargetlist(List *targetList, FusionType ftype)
{
ListCell *lc = NULL;
TargetEntry *target = NULL;
foreach (lc, targetList) {
target = (TargetEntry *)lfirst(lc);
if (!checkExpr((Node *)target->expr, true)) {
return NOBYPASS_EXP_NOT_SUPPORT;
}
}
return ftype;
}
bool checkDMLRelation(Relation rel, PlannedStmt *plannedstmt)
{
if (rel->rd_rel->relkind != RELKIND_RELATION || rel->rd_rel->relhasrules || rel->rd_rel->relhastriggers ||
rel->rd_rel->relhasoids || rel->rd_rel->relhassubclass || RelationIsPartitioned(rel) ||
RelationIsColStore(rel) || RelationInRedistribute(rel) || plannedstmt->hasReturning) {
return true;
}
return false;
}
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(plannedstmt->resultRelations) != 1) {
return NOBYPASS_DML_RELATION_NUM_INVALID;
}
Plan *top_plan = plannedstmt->planTree;
if (!IsA(top_plan, ModifyTable) || top_plan->plan_node_id != 1) {
return NOBYPASS_INVALID_MODIFYTABLE;
}
/* check subquery num */
ModifyTable *node = (ModifyTable *)top_plan;
if (list_length(node->plans) != 1) {
return NOBYPASS_NO_SIMPLE_PLAN;
}
if (!IsA(linitial(node->plans), BaseResult)) {
return NOBYPASS_NO_SIMPLE_INSERT;
}
BaseResult *base = (BaseResult *)linitial(node->plans);
if (base->plan.lefttree != NULL || base->plan.initPlan != NIL || base->resconstantqual != NULL) {
return NOBYPASS_NO_SIMPLE_INSERT;
}
if (node->upsertAction != UPSERT_NONE) {
return NOBYPASS_UPSERT_NOT_SUPPORT;
}
/* check relation */
Index res_rel_idx = linitial_int(plannedstmt->resultRelations);
Oid relid = getrelid(res_rel_idx, plannedstmt->rtable);
Relation rel = heap_open(relid, AccessShareLock);
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, AccessShareLock);
return NOBYPASS_DML_TARGET_TYPE_INVALID;
}
if (TypeCategory(rel->rd_att->attrs[i]->atttypid) == TYPCATEGORY_STRING &&
rel->rd_att->attrs[i]->atttypid != TEXTOID) {
heap_close(rel, AccessShareLock);
return NOBYPASS_DML_TARGET_TYPE_INVALID;
}
}
if (checkDMLRelation(rel, plannedstmt)) {
heap_close(rel, AccessShareLock);
return NOBYPASS_DML_RELATION_NOT_SUPPORT;
}
heap_close(rel, AccessShareLock);
/*
* check targetlist
* maybe expr type is FuncExpr because of type conversion.
*/
BaseResult *base_result = (BaseResult *)linitial(node->plans);
List *targetlist = base_result->plan.targetlist;
return checkTargetlist(targetlist, 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(plannedstmt->resultRelations) != 1) {
return NOBYPASS_DML_RELATION_NUM_INVALID;
}
Plan *top_plan = plannedstmt->planTree;
if (!IsA(top_plan, ModifyTable) || top_plan->plan_node_id != 1) {
return NOBYPASS_INVALID_MODIFYTABLE;
}
/* check subquery num */
ModifyTable *node = (ModifyTable *)top_plan;
if (list_length(node->plans) != 1) {
return NOBYPASS_NO_SIMPLE_PLAN;
}
if (!IsA(linitial(node->plans), IndexScan)) {
return NOBYPASS_NO_INDEXSCAN;
}
/* check index scan */
FusionType ttype = checkFusionIndexScan<true, false>((Node *)linitial(node->plans), params);
/* check failed */
if (ttype > BYPASS_OK) {
return ttype;
}
/* check relation */
Index res_rel_idx = linitial_int(plannedstmt->resultRelations);
Oid relid = getrelid(res_rel_idx, plannedstmt->rtable);
Relation rel = heap_open(relid, AccessShareLock);
if (checkDMLRelation(rel, plannedstmt)) {
heap_close(rel, AccessShareLock);
return NOBYPASS_DML_RELATION_NOT_SUPPORT;
}
heap_close(rel, AccessShareLock);
/* check target list */
IndexScan *indexscan = (IndexScan *)linitial(node->plans);
List *targetlist = indexscan->scan.plan.targetlist;
return checkTargetlist(targetlist, 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(plannedstmt->resultRelations) != 1) {
return NOBYPASS_DML_RELATION_NUM_INVALID;
}
Plan *top_plan = plannedstmt->planTree;
if (!IsA(top_plan, ModifyTable) || top_plan->plan_node_id != 1) {
return NOBYPASS_INVALID_MODIFYTABLE;
}
/* check subquery num */
ModifyTable *node = (ModifyTable *)top_plan;
if (list_length(node->plans) != 1) {
return NOBYPASS_NO_SIMPLE_PLAN;
}
if (!IsA(linitial(node->plans), IndexScan)) {
return NOBYPASS_NO_INDEXSCAN;
}
/* check index scan */
FusionType ttype = checkFusionIndexScan<true, false>((Node *)linitial(node->plans), params);
/* check failed */
if (ttype > BYPASS_OK) {
return ttype;
}
/* check relation */
Index res_rel_idx = linitial_int(plannedstmt->resultRelations);
Oid relid = getrelid(res_rel_idx, plannedstmt->rtable);
Relation rel = heap_open(relid, AccessShareLock);
if (checkDMLRelation(rel, plannedstmt)) {
heap_close(rel, AccessShareLock);
return NOBYPASS_DML_RELATION_NOT_SUPPORT;
}
heap_close(rel, AccessShareLock);
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);
}
}