Files
openGauss-server/src/gausskernel/runtime/executor/opfusion.cpp
2021-03-06 12:39:28 +08:00

2775 lines
93 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.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.cpp
*
* ---------------------------------------------------------------------------------------
*/
#include "opfusion/opfusion.h"
#include "access/printtup.h"
#include "access/transam.h"
#include "access/tableam.h"
#include "access/tupdesc.h"
#include "catalog/pg_aggregate.h"
#include "catalog/storage_gtt.h"
#include "catalog/heap.h"
#include "commands/copy.h"
#include "executor/nodeIndexscan.h"
#include "gstrace/executer_gstrace.h"
#include "instruments/instr_unique_sql.h"
#include "libpq/pqformat.h"
#include "mb/pg_wchar.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
#include "parser/parsetree.h"
#include "parser/parse_coerce.h"
#include "utils/lsyscache.h"
#include "utils/snapmgr.h"
#include "tcop/tcopprot.h"
#include "commands/matview.h"
#ifdef ENABLE_MOT
#include "storage/mot/jit_exec.h"
#endif
extern void opfusion_executeEnd(PlannedStmt* plannedstmt, const char *queryString, Snapshot snapshot);
OpFusion::OpFusion(MemoryContext context, CachedPlanSource* psrc, List* plantree_list)
{
m_context = AllocSetContextCreate(
context, "OpfusionContext", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE);
m_tmpContext = AllocSetContextCreate(context,
"OpfusionTemporaryContext",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
if (psrc == NULL && plantree_list != NULL) {
m_is_pbe_query = false;
m_psrc = NULL;
m_cacheplan = NULL;
m_planstmt = (PlannedStmt*)linitial(plantree_list);
} else if (plantree_list == NULL && psrc != NULL) {
m_is_pbe_query = true;
m_psrc = psrc;
m_cacheplan = psrc->cplan ? psrc->cplan : psrc->gplan;
m_cacheplan->refcount++;
m_planstmt = (PlannedStmt*)linitial(m_cacheplan->stmt_list);
} else {
Assert(0);
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_PSTATEMENT), errmsg("Both cacheplan and planstmt are NULL")));
}
m_outParams = NULL;
m_params = NULL;
m_isFirst = true;
m_rformats = NULL;
m_isCompleted = false;
m_position = 0;
m_isInsideRec = false;
m_tmpvals = NULL;
m_reloid = 0;
m_isnull = NULL;
m_attrno = NULL;
m_reslot = NULL;
m_receiver = NULL;
m_tupDesc = NULL;
m_values = NULL;
m_paramNum = 0;
m_paramLoc = NULL;
m_tmpisnull = NULL;
m_portalName = NULL;
m_snapshot = NULL;
m_scan = NULL;
}
#ifdef ENABLE_MOT
FusionType OpFusion::GetMotFusionType(PlannedStmt* plannedStmt)
{
FusionType result;
switch (plannedStmt->commandType) {
case CMD_SELECT:
result = MOT_JIT_SELECT_FUSION;
break;
case CMD_INSERT:
case CMD_UPDATE:
case CMD_DELETE:
result = MOT_JIT_MODIFY_FUSION;
break;
default:
result = NOBYPASS_NO_QUERY_TYPE;
break;
}
return result;
}
#endif
FusionType OpFusion::getFusionType(CachedPlan* plan, ParamListInfo params, List* plantree_list)
{
if (IsInitdb == true) {
return NONE_FUSION;
}
if (ENABLE_GPC) {
return NONE_FUSION;
}
if (!u_sess->attr.attr_sql.enable_opfusion) {
return NONE_FUSION;
}
List* plist = NULL;
if (plan && plantree_list == NULL) {
plist = plan->stmt_list;
} else if (plantree_list && plan == NULL) {
plist = plantree_list;
} else {
Assert(0);
}
/* check stmt num */
if (list_length(plist) != 1) {
return NOBYPASS_NO_SIMPLE_PLAN;
}
/* check whether is planedstmt */
Node* st = (Node*)linitial(plist);
if (!IsA(st, PlannedStmt)) {
/* may be ddl */
return NONE_FUSION;
}
PlannedStmt* planned_stmt = (PlannedStmt*)st;
if (planned_stmt->utilityStmt != NULL) {
/* may be utility functions */
return NONE_FUSION;
}
FusionType result = NONE_FUSION;
#ifdef ENABLE_MOT
if (plan && plan->mot_jit_context && JitExec::IsMotCodegenEnabled()) {
result = GetMotFusionType(planned_stmt);
} else {
#endif
if (planned_stmt->subplans != NULL || planned_stmt->initPlan != NULL) {
return NOBYPASS_NO_SIMPLE_PLAN;
}
if (((PlannedStmt*)st)->commandType == CMD_SELECT) {
result = getSelectFusionType(plist, params);
} else if (planned_stmt->commandType == CMD_INSERT) {
result = getInsertFusionType(plist, params);
} else if (planned_stmt->commandType == CMD_UPDATE) {
result = getUpdateFusionType(plist, params);
} else if (planned_stmt->commandType == CMD_DELETE) {
result = getDeleteFusionType(plist, params);
} else {
result = NOBYPASS_NO_QUERY_TYPE;
}
#ifdef ENABLE_MOT
}
#endif
return result;
}
void OpFusion::setCurrentOpFusionObj(OpFusion* obj)
{
u_sess->exec_cxt.CurrentOpFusionObj = obj;
}
void OpFusion::checkPermission()
{
bool check = false;
if (!(IS_PGXC_DATANODE && (IsConnFromCoord() || IsConnFromDatanode()))) {
check = true;
}
if (m_planstmt->in_compute_pool) {
check = false;
}
if (t_thrd.pgxc_cxt.is_gc_fdw && t_thrd.pgxc_cxt.is_gc_fdw_analyze) {
check = false;
}
if (check) {
(void)ExecCheckRTPerms(m_planstmt->rtable, true);
}
}
void OpFusion::executeInit()
{
if (m_isFirst == true) {
checkPermission();
}
if (IS_SINGLE_NODE && ENABLE_WORKLOAD_CONTROL) {
u_sess->debug_query_id = generate_unique_id64(&gt_queryId);
WLMCreateDNodeInfoOnDN(NULL);
WLMCreateIOInfoOnDN();
}
if (u_sess->attr.attr_common.XactReadOnly) {
ExecCheckXactReadOnly(m_planstmt);
}
#ifdef ENABLE_MOT
if (!(u_sess->exec_cxt.CurrentOpFusionObj->m_cacheplan &&
u_sess->exec_cxt.CurrentOpFusionObj->m_cacheplan->storageEngineType == SE_TYPE_MOT)) {
#endif
if (m_snapshot == NULL) {
m_snapshot = RegisterSnapshot(GetTransactionSnapshot());
}
PushActiveSnapshot(m_snapshot);
#ifdef ENABLE_MOT
}
#endif
}
void OpFusion::auditRecord()
{
if ((u_sess->attr.attr_security.Audit_DML_SELECT != 0 || u_sess->attr.attr_security.Audit_DML != 0) &&
u_sess->attr.attr_security.Audit_enabled &&
IsPostmasterEnvironment) {
char* object_name = NULL;
switch (m_planstmt->commandType) {
case CMD_INSERT:
case CMD_DELETE:
case CMD_UPDATE:
if (u_sess->attr.attr_security.Audit_DML != 0) {
object_name = pgaudit_get_relation_name(m_planstmt->rtable);
pgaudit_dml_table(object_name, m_is_pbe_query ? m_psrc->query_string : t_thrd.postgres_cxt.debug_query_string);
}
break;
case CMD_SELECT:
if (u_sess->attr.attr_security.Audit_DML_SELECT != 0) {
object_name = pgaudit_get_relation_name(m_planstmt->rtable);
pgaudit_dml_table_select(object_name, m_is_pbe_query ? m_psrc->query_string : t_thrd.postgres_cxt.debug_query_string);
}
break;
/* Not support others */
default:
break;
}
}
}
void OpFusion::executeEnd(const char* portal_name, bool* isQueryCompleted)
{
#ifdef ENABLE_MOT
if (!(u_sess->exec_cxt.CurrentOpFusionObj->m_cacheplan &&
u_sess->exec_cxt.CurrentOpFusionObj->m_cacheplan->storageEngineType == SE_TYPE_MOT)) {
#endif
opfusion_executeEnd(m_planstmt, ((m_psrc == NULL)?(NULL):(m_psrc->query_string)), GetActiveSnapshot());
PopActiveSnapshot();
#ifdef ENABLE_MOT
}
#endif
#ifdef MEMORY_CONTEXT_CHECKING
/* Check all memory contexts when executor starts */
MemoryContextCheck(TopMemoryContext, false);
/* Check per-query memory context before Opfusion temp memory */
MemoryContextCheck(m_tmpContext, true);
#endif
/* reset the context. */
if (m_isCompleted) {
UnregisterSnapshot(m_snapshot);
m_snapshot = NULL;
MemoryContextDeleteChildren(m_tmpContext);
/* reset the context. */
MemoryContextReset(m_tmpContext);
/* clear hash table */
removeFusionFromHtab(portal_name);
m_outParams = NULL;
m_isCompleted = false;
if (isQueryCompleted)
*isQueryCompleted = true;
u_sess->xact_cxt.pbe_execute_complete = true;
} else {
if (isQueryCompleted)
*isQueryCompleted = false;
u_sess->xact_cxt.pbe_execute_complete = false;
}
u_sess->exec_cxt.CurrentOpFusionObj = NULL;
m_isFirst = false;
auditRecord();
if (u_sess->attr.attr_common.pgstat_track_activities && u_sess->attr.attr_common.pgstat_track_sql_count) {
report_qps_type(m_planstmt->commandType);
report_qps_type(CMD_DML);
}
}
bool OpFusion::process(int op, StringInfo msg, char* completionTag, bool isTopLevel, bool* isQueryCompleted)
{
if (op == FUSION_EXECUTE && msg != NULL)
refreshCurFusion(msg);
bool res = false;
if (u_sess->exec_cxt.CurrentOpFusionObj != NULL) {
switch (op) {
case FUSION_EXECUTE: {
long max_rows = FETCH_ALL;
const char* portal_name = NULL;
/* msg is null means 'U' message, need fetch all. */
if (msg != NULL) {
portal_name = pq_getmsgstring(msg);
max_rows = (long)pq_getmsgint(msg, 4);
if (max_rows <= 0)
max_rows = FETCH_ALL;
}
u_sess->exec_cxt.CurrentOpFusionObj->executeInit();
gstrace_entry(GS_TRC_ID_BypassExecutor);
bool old_status = u_sess->exec_cxt.need_track_resource;
if (u_sess->attr.attr_resource.resource_track_cost == 0 &&
u_sess->attr.attr_resource.enable_resource_track &&
u_sess->attr.attr_resource.resource_track_level != RESOURCE_TRACK_NONE) {
u_sess->exec_cxt.need_track_resource = true;
WLMSetCollectInfoStatus(WLM_STATUS_RUNNING);
}
u_sess->exec_cxt.CurrentOpFusionObj->execute(max_rows, completionTag);
u_sess->exec_cxt.need_track_resource = old_status;
gstrace_exit(GS_TRC_ID_BypassExecutor);
u_sess->exec_cxt.CurrentOpFusionObj->executeEnd(portal_name, isQueryCompleted);
UpdateSingleNodeByPassUniqueSQLStat(isTopLevel);
break;
}
case FUSION_DESCRIB: {
u_sess->exec_cxt.CurrentOpFusionObj->describe(msg);
break;
}
default: {
Assert(0);
ereport(ERROR,
(errcode(ERRCODE_CASE_NOT_FOUND),
errmsg("unrecognized bypass support process option: %d", (int)op)));
}
}
res = true;
}
/*
* Emit duration logging if appropriate.
*/
char msec_str[32];
switch (check_log_duration(msec_str, false)) {
case 1:
ereport(DEBUG1, (errmsg("duration: %s ms, queryid %lu, unique id %lu", msec_str, u_sess->debug_query_id, u_sess->slow_query_cxt.slow_query.unique_sql_id), errhidestmt(true)));
break;
case 2: {
ereport(DEBUG1,
(errmsg("duration: %s ms queryid %lu unique id %lu", msec_str, u_sess->debug_query_id, u_sess->slow_query_cxt.slow_query.unique_sql_id),
errhidestmt(true)));
break;
}
default:
break;
}
return res;
}
void OpFusion::CopyFormats(int16* formats, int numRFormats)
{
MemoryContext old_context = MemoryContextSwitchTo(m_tmpContext);
int natts;
if (m_tupDesc == NULL) {
Assert(0);
return;
}
if (formats == NULL && numRFormats > 0) {
ereport(ERROR,
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
errmsg("r_formats can not be NULL when num of rformats is not 0")));
}
natts = m_tupDesc->natts;
m_rformats = (int16*)palloc(natts * sizeof(int16));
if (numRFormats > 1) {
/* format specified for each column */
if (numRFormats != natts)
ereport(ERROR,
(errcode(ERRCODE_PROTOCOL_VIOLATION),
errmsg("bind message has %d result formats but query has %d columns", numRFormats, natts)));
errno_t errorno = EOK;
errorno = memcpy_s(m_rformats, natts * sizeof(int16), formats, natts * sizeof(int16));
securec_check(errorno, "\0", "\0");
} else if (numRFormats > 0) {
/* single format specified, use for all columns */
int16 format1 = formats[0];
for (int i = 0; i < natts; i++) {
m_rformats[i] = format1;
}
} else {
/* use default format for all columns */
for (int i = 0; i < natts; i++) {
m_rformats[i] = 0;
}
}
MemoryContextSwitchTo(old_context);
}
void OpFusion::useOuterParameter(ParamListInfo params)
{
m_outParams = params;
}
void* OpFusion::FusionFactory(
FusionType ftype, MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
{
Assert(ftype != BYPASS_OK);
if (u_sess->attr.attr_sql.opfusion_debug_mode == BYPASS_LOG) {
BypassUnsupportedReason(ftype);
}
if (ftype > BYPASS_OK) {
return NULL;
}
switch (ftype) {
case SELECT_FUSION:
return New(context) SelectFusion(context, psrc, plantree_list, params);
case INSERT_FUSION:
return New(context) InsertFusion(context, psrc, plantree_list, params);
case UPDATE_FUSION:
return New(context) UpdateFusion(context, psrc, plantree_list, params);
case DELETE_FUSION:
return New(context) DeleteFusion(context, psrc, plantree_list, params);
case SELECT_FOR_UPDATE_FUSION:
return New(context) SelectForUpdateFusion(context, psrc, plantree_list, params);
#ifdef ENABLE_MOT
case MOT_JIT_SELECT_FUSION:
return New(context) MotJitSelectFusion(context, psrc, plantree_list, params);
case MOT_JIT_MODIFY_FUSION:
return New(context) MotJitModifyFusion(context, psrc, plantree_list, params);
#endif
case AGG_INDEX_FUSION:
return New(context) AggFusion(context, psrc, plantree_list, params);
case SORT_INDEX_FUSION:
return New(context) SortFusion(context, psrc, plantree_list, params);
case NONE_FUSION:
return NULL;
default:
return NULL;
}
return NULL;
}
void OpFusion::updatePreAllocParamter(StringInfo input_message)
{
/* Switch back to message context */
MemoryContext old_context = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt);
int num_pformats;
int16* pformats = NULL;
int num_params;
int num_rformats;
int paramno;
ParamListInfo params = m_params;
/* Get the parameter format codes */
num_pformats = pq_getmsgint(input_message, 2);
if (num_pformats > 0) {
int i;
pformats = (int16*)palloc(num_pformats * sizeof(int16));
for (i = 0; i < num_pformats; i++) {
pformats[i] = pq_getmsgint(input_message, 2);
}
}
/* Get the parameter value count */
num_params = pq_getmsgint(input_message, 2);
if (unlikely(num_params != m_paramNum)) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_PSTATEMENT), errmsg("unmatched parameter number")));
}
MemoryContextSwitchTo(m_tmpContext);
if (num_params > 0) {
for (paramno = 0; paramno < num_params; paramno++) {
Oid ptype = m_psrc->param_types[paramno];
int32 plength;
Datum pval;
bool isNull = false;
StringInfoData pbuf;
char csave;
int16 pformat;
plength = pq_getmsgint(input_message, 4);
isNull = (plength == -1);
/* add null value process for date type */
if ((VARCHAROID == ptype || TIMESTAMPOID == ptype || TIMESTAMPTZOID == ptype || TIMEOID == ptype ||
TIMETZOID == ptype || INTERVALOID == ptype || SMALLDATETIMEOID == ptype) &&
plength == 0 && u_sess->attr.attr_sql.sql_compatibility == A_FORMAT) {
isNull = true;
}
/*
* Insert into bind values support illegal characters import,
* and this just wroks for char type attribute.
*/
u_sess->mb_cxt.insertValuesBind_compatible_illegal_chars = IsCharType(ptype);
if (!isNull) {
const char* pvalue = pq_getmsgbytes(input_message, plength);
/*
* Rather than copying data around, we just set up a phony
* StringInfo pointing to the correct portion of the message
* buffer. We assume we can scribble on the message buffer so
* as to maintain the convention that StringInfos have a
* trailing null. This is grotty but is a big win when
* dealing with very large parameter strings.
*/
pbuf.data = (char*)pvalue;
pbuf.maxlen = plength + 1;
pbuf.len = plength;
pbuf.cursor = 0;
csave = pbuf.data[plength];
pbuf.data[plength] = '\0';
} else {
pbuf.data = NULL; /* keep compiler quiet */
csave = 0;
}
if (num_pformats > 1) {
Assert(pformats != NULL);
pformat = pformats[paramno];
} else if (pformats != NULL) {
Assert(pformats != NULL);
pformat = pformats[0];
} else {
pformat = 0; /* default = text */
}
if (pformat == 0) {
/* text mode */
Oid typinput;
Oid typioparam;
char* pstring = NULL;
getTypeInputInfo(ptype, &typinput, &typioparam);
/*
* We have to do encoding conversion before calling the
* typinput routine.
*/
if (isNull) {
pstring = NULL;
} else {
pstring = pg_client_to_server(pbuf.data, plength);
}
pval = OidInputFunctionCall(typinput, pstring, typioparam, -1);
/* Free result of encoding conversion, if any */
if (pstring != NULL && pstring != pbuf.data) {
pfree(pstring);
}
} else if (pformat == 1) {
/* binary mode */
Oid typreceive;
Oid typioparam;
StringInfo bufptr;
/*
* Call the parameter type's binary input converter
*/
getTypeBinaryInputInfo(ptype, &typreceive, &typioparam);
if (isNull) {
bufptr = NULL;
} else {
bufptr = &pbuf;
}
pval = OidReceiveFunctionCall(typreceive, bufptr, typioparam, -1);
/* Trouble if it didn't eat the whole buffer */
if (!isNull && pbuf.cursor != pbuf.len) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("incorrect binary data format in bind parameter %d", paramno + 1)));
}
} else {
ereport(
ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unsupported format code: %d", pformat)));
pval = 0; /* keep compiler quiet */
}
/* Restore message buffer contents */
if (!isNull) {
pbuf.data[plength] = csave;
}
params->params[paramno].value = pval;
params->params[paramno].isnull = isNull;
/*
* We mark the params as CONST. This ensures that any custom plan
* makes full use of the parameter values.
*/
params->params[paramno].pflags = PARAM_FLAG_CONST;
params->params[paramno].ptype = ptype;
/* Reset the compatible illegal chars import flag */
u_sess->mb_cxt.insertValuesBind_compatible_illegal_chars = false;
}
}
int16* rformats = NULL;
MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt);
/* Get the result format codes */
num_rformats = pq_getmsgint(input_message, 2);
if (num_rformats > 0) {
int i;
rformats = (int16*)palloc(num_rformats * sizeof(int16));
for (i = 0; i < num_rformats; i++) {
rformats[i] = pq_getmsgint(input_message, 2);
}
}
CopyFormats(rformats, num_rformats);
pq_getmsgend(input_message);
pfree_ext(rformats);
MemoryContextSwitchTo(old_context);
}
void OpFusion::describe(StringInfo msg)
{
if (m_psrc->resultDesc != NULL) {
StringInfoData buf;
initStringInfo(&buf);
SendRowDescriptionMessage(&buf, m_tupDesc, m_planstmt->planTree->targetlist, m_rformats);
pfree_ext(buf.data);
} else {
pq_putemptymessage('n');
}
}
void OpFusion::setPreparedDestReceiver(DestReceiver* preparedDest)
{
m_receiver = preparedDest;
m_isInsideRec = false;
}
/* evaluate datum from node, simple node types such as Const, Param, Var excludes FuncExpr and OpExpr */
Datum OpFusion::EvalSimpleArg(Node* arg, bool* is_null, Datum* values, bool* isNulls)
{
switch (nodeTag(arg)) {
case T_Const:
*is_null = ((Const*)arg)->constisnull;
return ((Const*)arg)->constvalue;
case T_Var:
*is_null = isNulls[(((Var*)arg)->varattno - 1)];
return values[(((Var*)arg)->varattno - 1)];
case T_Param: {
ParamListInfo param_list = m_outParams != NULL ? m_outParams : m_params;
if (param_list->params[(((Param*)arg)->paramid - 1)].isnull) {
*is_null = true;
}
return param_list->params[(((Param*)arg)->paramid - 1)].value;
}
case T_RelabelType: {
if (IsA(((RelabelType*)arg)->arg, FuncExpr)) {
FuncExpr* expr = (FuncExpr*)(((RelabelType*)arg)->arg);
return CalFuncNodeVal(expr->funcid, expr->args, is_null, values, isNulls);
} else if (IsA(((RelabelType*)arg)->arg, OpExpr)) {
OpExpr* expr = (OpExpr*)(((RelabelType*)arg)->arg);
return CalFuncNodeVal(expr->opfuncid, expr->args, is_null, values, isNulls);
}
return EvalSimpleArg((Node*)((RelabelType*)arg)->arg, is_null, values, isNulls);
}
default:
Assert(0);
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unexpected node type: %d when processing bypass expression.", (int)nodeTag(arg))));
break;
}
Assert(0);
return 0;
}
Datum OpFusion::CalFuncNodeVal(Oid functionId, List* args, bool* is_null, Datum* values, bool* isNulls)
{
if (*is_null) {
return 0;
}
Node* first_arg_node = (Node*)linitial(args);
Datum* arg;
bool* is_nulls = (bool*)palloc0(4 * sizeof(bool));
arg = (Datum*)palloc0(4 * sizeof(Datum));
/* for now, we assuming FuncExpr and OpExpr only appear in the first arg */
if (IsA(first_arg_node, FuncExpr)) {
arg[0] = CalFuncNodeVal(((FuncExpr*)first_arg_node)->funcid, ((FuncExpr*)first_arg_node)->args,
&is_nulls[0], values, isNulls);
} else if (IsA(first_arg_node, OpExpr)) {
arg[0] = CalFuncNodeVal(((OpExpr*)first_arg_node)->opfuncid, ((OpExpr*)first_arg_node)->args,
&is_nulls[0], values, isNulls);
} else {
arg[0] = EvalSimpleArg(first_arg_node, &is_nulls[0], values, isNulls);
}
int length = list_length(args);
if (length < 1 || length > 4) {
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unexpected arg length : %d when processing bypass expression.", length)));
}
*is_null = is_nulls[0];
ListCell* tmp_arg = list_head(args);
for (int i = 1; i < length; i++) {
tmp_arg = lnext(tmp_arg);
arg[i] = EvalSimpleArg((Node*)lfirst(tmp_arg), &is_nulls[i], values, isNulls);
*is_null = *is_null || is_nulls[i];
}
if (*is_null) {
return 0;
}
switch (length) {
case 1:
return OidFunctionCall1(functionId, arg[0]);
case 2:
return OidFunctionCall2(functionId, arg[0], arg[1]);
case 3:
return OidFunctionCall3(functionId, arg[0], arg[1], arg[2]);
case 4:
return OidFunctionCall4(functionId, arg[0], arg[1], arg[2], arg[3]);
default: {
Assert(0);
ereport(ERROR,
(errcode(ERRCODE_CASE_NOT_FOUND),
errmsg("unrecognized bypass support number of arguments function calls: %d", (int)length)));
}
}
Assert(0);
return 0;
}
void OpFusion::tearDown(OpFusion* opfusion)
{
if (opfusion == NULL) {
return;
}
removeFusionFromHtab(opfusion->m_portalName);
if (opfusion->m_cacheplan != NULL) {
ReleaseCachedPlan(opfusion->m_cacheplan, false);
}
if (opfusion->m_psrc != NULL) {
opfusion->m_psrc->is_checked_opfusion = false;
opfusion->m_psrc->opFusionObj = NULL;
}
MemoryContextDelete(opfusion->m_tmpContext);
MemoryContextDelete(opfusion->m_context);
delete opfusion;
OpFusion::setCurrentOpFusionObj(NULL);
}
void OpFusion::clearForCplan(OpFusion* opfusion, CachedPlanSource* psrc)
{
if (opfusion == NULL)
return;
if (psrc->cplan != NULL) {
tearDown(opfusion);
}
}
/* just for select and select for */
void OpFusion::setReceiver()
{
if (m_isInsideRec) {
m_receiver = CreateDestReceiver((CommandDest)t_thrd.postgres_cxt.whereToSendOutput);
}
((DR_printtup*)m_receiver)->sendDescrip = false;
if (m_receiver->mydest == DestRemote || m_receiver->mydest == DestRemoteExecute) {
((DR_printtup*)m_receiver)->formats = m_rformats;
}
(*m_receiver->rStartup)(m_receiver, CMD_SELECT, m_tupDesc);
if (!m_is_pbe_query) {
StringInfoData buf = ((DR_printtup*)m_receiver)->buf;
initStringInfo(&buf);
SendRowDescriptionMessage(&buf, m_tupDesc, m_planstmt->planTree->targetlist, m_rformats);
}
}
void OpFusion::initParams(ParamListInfo params)
{
m_outParams = params;
/* init params */
if (m_is_pbe_query && params != NULL) {
m_paramNum = params->numParams;
m_params = (ParamListInfo)palloc(offsetof(ParamListInfoData, params) + m_paramNum * sizeof(ParamExternData));
m_params->paramFetch = NULL;
m_params->paramFetchArg = NULL;
m_params->parserSetup = NULL;
m_params->parserSetupArg = NULL;
m_params->params_need_process = false;
m_params->numParams = m_paramNum;
}
}
bool OpFusion::isQueryCompleted()
{
if (u_sess->exec_cxt.CurrentOpFusionObj != NULL)
return false;
else {
return true; /* bypass completed */
}
}
void OpFusion::bindClearPosition()
{
m_isCompleted = false;
m_position = 0;
m_outParams = NULL;
m_snapshot = NULL;
MemoryContextDeleteChildren(m_tmpContext);
/* reset the context. */
MemoryContextReset(m_tmpContext);
}
void OpFusion::initFusionHtab()
{
HASHCTL hash_ctl;
errno_t rc = 0;
rc = memset_s(&hash_ctl, sizeof(hash_ctl), 0, sizeof(hash_ctl));
securec_check(rc, "\0", "\0");
hash_ctl.keysize = NAMEDATALEN;
hash_ctl.entrysize = sizeof(pnFusionObj);
hash_ctl.hcxt = u_sess->cache_mem_cxt;
u_sess->pcache_cxt.pn_fusion_htab = hash_create("OpFusion_hashtable",
HASH_TBL_LEN, &hash_ctl, HASH_ELEM | HASH_CONTEXT);
}
void OpFusion::ClearInUnexpectSituation()
{
if (u_sess->pcache_cxt.pn_fusion_htab != NULL) {
HASH_SEQ_STATUS seq;
pnFusionObj *entry = NULL;
hash_seq_init(&seq, u_sess->pcache_cxt.pn_fusion_htab);
while ((entry = (pnFusionObj *)hash_seq_search(&seq)) != NULL) {
OpFusion* curr = entry->opfusion;
if (curr->m_portalName == NULL || strcmp(curr->m_portalName, entry->portalname) == 0) {
curr->clean();
/* only opfusion has reference on cachedplan, no need any more */
if (curr->m_cacheplan && curr->m_cacheplan->refcount == 1) {
ReleaseCachedPlan(curr->m_cacheplan, false);
MemoryContextDelete(curr->m_tmpContext);
MemoryContextDelete(curr->m_context);
delete curr;
}
}
hash_search(u_sess->pcache_cxt.pn_fusion_htab, entry->portalname, HASH_REMOVE, NULL);
}
}
u_sess->exec_cxt.CurrentOpFusionObj = NULL;
}
void OpFusion::clean()
{
UnregisterSnapshot(m_snapshot);
m_snapshot = NULL;
m_position = 0;
m_outParams = NULL;
if (m_scan)
m_scan->End(true);
m_isCompleted = false;
MemoryContextDeleteChildren(m_tmpContext);
/* reset the context. */
MemoryContextReset(m_tmpContext);
}
void OpFusion::storeFusion(const char *portalname)
{
pnFusionObj *entry = NULL;
if (!u_sess->pcache_cxt.pn_fusion_htab)
initFusionHtab();
removeFusionFromHtab(m_portalName);
entry = (pnFusionObj *)(hash_search(u_sess->pcache_cxt.pn_fusion_htab, portalname, HASH_ENTER, NULL));
entry->opfusion = this;
MemoryContext old_cxt = MemoryContextSwitchTo(m_context);
pfree_ext(m_portalName);
m_portalName = pstrdup(portalname);
MemoryContextSwitchTo(old_cxt);
}
OpFusion *OpFusion::locateFusion(const char *portalname)
{
pnFusionObj *entry = NULL;
if (u_sess->pcache_cxt.pn_fusion_htab) {
entry = (pnFusionObj *)(hash_search(u_sess->pcache_cxt.pn_fusion_htab, portalname, HASH_FIND, NULL));
}
if (entry) {
return entry->opfusion;
} else {
return NULL;
}
}
void OpFusion::removeFusionFromHtab(const char *portalname)
{
if (u_sess->pcache_cxt.pn_fusion_htab && portalname != NULL && portalname[0] != '\0') {
hash_search(u_sess->pcache_cxt.pn_fusion_htab, portalname, HASH_REMOVE, NULL);
}
}
void OpFusion::refreshCurFusion(StringInfo msg)
{
OpFusion *opfusion = NULL;
int oldCursor = msg->cursor;
const char *portal_name = pq_getmsgstring(msg);
if (portal_name[0] != '\0') {
opfusion = OpFusion::locateFusion(portal_name);
OpFusion::setCurrentOpFusionObj(opfusion);
}
msg->cursor = oldCursor;
}
/* 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;
}
SelectFusion::SelectFusion(MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
: OpFusion(context, psrc, plantree_list)
{
m_tmpvals = NULL;
m_values = NULL;
m_isnull = NULL;
m_paramLoc = NULL;
m_tmpisnull = NULL;
m_attrno = NULL;
m_reloid = 0;
MemoryContext old_context = MemoryContextSwitchTo(m_context);
Node* node = NULL;
m_limitCount = -1;
m_limitOffset = -1;
/* get limit num */
if (IsA(m_planstmt->planTree, Limit)) {
Limit* limit = (Limit*)m_planstmt->planTree;
node = JudgePlanIsPartIterator(m_planstmt->planTree->lefttree);
if (limit->limitCount != NULL && IsA(limit->limitCount, Const) && !((Const*)limit->limitCount)->constisnull) {
m_limitCount = DatumGetInt64(((Const*)limit->limitCount)->constvalue);
}
if (limit->limitOffset != NULL && IsA(limit->limitOffset, Const) &&
!((Const*)limit->limitOffset)->constisnull) {
m_limitOffset = DatumGetInt64(((Const*)limit->limitOffset)->constvalue);
}
} else {
node = JudgePlanIsPartIterator(m_planstmt->planTree);
}
initParams(params);
m_receiver = NULL;
m_isInsideRec = true;
m_scan = ScanFusion::getScanFusion(node, m_planstmt, m_outParams);
m_tupDesc = m_scan->m_tupDesc;
m_reslot = NULL;
MemoryContextSwitchTo(old_context);
}
bool SelectFusion::execute(long max_rows, char* completionTag)
{
MemoryContext oldContext = MemoryContextSwitchTo(m_tmpContext);
bool success = false;
int64 start_row = 0;
int64 get_rows = 0;
/*******************
* step 1: prepare *
*******************/
start_row = m_limitOffset >= 0 ? m_limitOffset : start_row;
get_rows = m_limitCount >= 0 ? (m_limitCount + start_row) : max_rows;
/**********************
* step 2: begin scan *
**********************/
if (m_position == 0) {
m_scan->refreshParameter(m_outParams == NULL ? m_params : m_outParams);
m_scan->Init(max_rows);
}
setReceiver();
unsigned long nprocessed = 0;
/* put selected tuple into receiver */
TupleTableSlot* offset_reslot = NULL;
while (nprocessed < (unsigned long)start_row && (offset_reslot = m_scan->getTupleSlot()) != NULL) {
tpslot_free_heaptuple(offset_reslot);
nprocessed++;
}
while (nprocessed < (unsigned long)get_rows && (m_reslot = m_scan->getTupleSlot()) != NULL) {
CHECK_FOR_INTERRUPTS();
nprocessed++;
(*m_receiver->receiveSlot)(m_reslot, m_receiver);
tpslot_free_heaptuple(m_reslot);
}
if (!ScanDirectionIsNoMovement(*(m_scan->m_direction))) {
if (max_rows == 0 || nprocessed < (unsigned long)max_rows) {
m_isCompleted = true;
}
m_position += nprocessed;
} else {
m_isCompleted = true;
}
success = true;
/****************
* step 3: done *
****************/
if (m_isInsideRec) {
(*m_receiver->rDestroy)(m_receiver);
}
m_scan->End(m_isCompleted);
if (m_isCompleted) {
m_position = 0;
}
errno_t errorno =
snprintf_s(completionTag, COMPLETION_TAG_BUFSIZE, COMPLETION_TAG_BUFSIZE - 1, "SELECT %lu", nprocessed);
securec_check_ss(errorno, "\0", "\0");
MemoryContextSwitchTo(oldContext);
return success;
}
void SelectFusion::close()
{
if (m_isCompleted == false) {
m_scan->End(true);
m_isCompleted = true;
m_position = 0;
}
}
#ifdef ENABLE_MOT
MotJitSelectFusion::MotJitSelectFusion(
MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
: OpFusion(context, psrc, plantree_list)
{
MemoryContext oldContext = MemoryContextSwitchTo(m_context);
Node* node = NULL;
node = (Node*)m_planstmt->planTree;
initParams(params);
m_receiver = NULL;
m_isInsideRec = true;
if (m_planstmt->planTree->targetlist) {
m_tupDesc = ExecCleanTypeFromTL(m_planstmt->planTree->targetlist, false);
} else {
ereport(ERROR,
(errmodule(MOD_EXECUTOR),
errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unrecognized node type: %d when executing executor node.", (int)nodeTag(node))));
}
m_reslot = MakeSingleTupleTableSlot(m_tupDesc);
MemoryContextSwitchTo(oldContext);
}
bool MotJitSelectFusion::execute(long max_rows, char* completionTag)
{
ParamListInfo params = (m_outParams != NULL) ? m_outParams : m_params;
bool success = false;
setReceiver();
unsigned long nprocessed = 0;
bool finish = false;
int rc = 0;
while (!finish) {
uint64_t tpProcessed = 0;
int scanEnded = 0;
rc = JitExec::JitExecQuery(m_cacheplan->mot_jit_context, params, m_reslot, &tpProcessed, &scanEnded);
if (scanEnded || (tpProcessed == 0) || (rc != 0)) {
// raise flag so that next round we will bail out (current tuple still must be reported to user)
finish = true;
}
CHECK_FOR_INTERRUPTS();
if (tpProcessed > 0) {
nprocessed++;
(*m_receiver->receiveSlot)(m_reslot, m_receiver);
(void)ExecClearTuple(m_reslot);
if ((max_rows != FETCH_ALL) && (nprocessed == (unsigned long)max_rows)) {
finish = true;
}
}
}
success = true;
if (m_isInsideRec) {
(*m_receiver->rDestroy)(m_receiver);
}
m_position = 0;
m_isCompleted = true;
errno_t errorno = snprintf_s(completionTag, COMPLETION_TAG_BUFSIZE, COMPLETION_TAG_BUFSIZE - 1,
"SELECT %lu", nprocessed);
securec_check_ss(errorno, "\0", "\0");
return success;
}
#endif
/* 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(Relation bucketRel, EState* estate)
{
if (estate->esfRelations) {
FakeRelationCacheDestroy(estate->esfRelations);
}
if (bucketRel != NULL) {
bucketCloseRelation(bucketRel);
}
}
InsertFusion::InsertFusion(MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
: OpFusion(context, psrc, plantree_list)
{
m_tmpisnull = NULL;
m_tmpvals = NULL;
m_attrno = NULL;
MemoryContext old_context = MemoryContextSwitchTo(m_context);
ModifyTable* node = (ModifyTable*)m_planstmt->planTree;
m_reloid = getrelid(linitial_int(m_planstmt->resultRelations), m_planstmt->rtable);
Relation rel = heap_open(m_reloid, AccessShareLock);
m_estate = CreateExecutorState();
m_estate->es_range_table = m_planstmt->rtable;
BaseResult* baseresult = (BaseResult*)linitial(node->plans);
List* targetList = baseresult->plan.targetlist;
m_tupDesc = ExecTypeFromTL(targetList, false, false, rel->rd_tam_type);
m_reslot = MakeSingleTupleTableSlot(m_tupDesc);
m_values = (Datum*)palloc0(RelationGetDescr(rel)->natts * sizeof(Datum));
m_isnull = (bool*)palloc0(RelationGetDescr(rel)->natts * sizeof(bool));
m_curVarValue = (Datum*)palloc0(RelationGetDescr(rel)->natts * sizeof(Datum));
m_curVarIsnull = (bool*)palloc0(RelationGetDescr(rel)->natts * sizeof(bool));
m_is_bucket_rel = RELATION_OWN_BUCKET(rel);
heap_close(rel, AccessShareLock);
/* init param */
m_paramNum = 0;
m_paramLoc = (ParamLoc*)palloc0(RelationGetDescr(rel)->natts * sizeof(ParamLoc));
m_targetParamNum = 0;
m_targetFuncNum = 0;
m_targetFuncNodes = (FuncExprInfo*)palloc0(RelationGetDescr(rel)->natts * sizeof(FuncExprInfo));
ListCell* lc = NULL;
int i = 0;
FuncExpr* func = NULL;
TargetEntry* res = NULL;
Expr* expr = NULL;
OpExpr* opexpr = NULL;
foreach (lc, targetList) {
res = (TargetEntry*)lfirst(lc);
expr = res->expr;
Assert(
IsA(expr, Const) || IsA(expr, Param) || IsA(expr, FuncExpr) || IsA(expr, RelabelType) || IsA(expr, OpExpr));
while (IsA(expr, RelabelType)) {
expr = ((RelabelType*)expr)->arg;
}
if (IsA(expr, FuncExpr)) {
func = (FuncExpr*)expr;
m_targetFuncNodes[m_targetFuncNum].resno = res->resno;
m_targetFuncNodes[m_targetFuncNum].resname = res->resname;
m_targetFuncNodes[m_targetFuncNum].funcid = func->funcid;
m_targetFuncNodes[m_targetFuncNum].args = func->args;
++m_targetFuncNum;
} else if (IsA(expr, Param)) {
Param* param = (Param*)expr;
m_paramLoc[m_targetParamNum].paramId = param->paramid;
m_paramLoc[m_targetParamNum++].scanKeyIndx = i;
} else if (IsA(expr, Const)) {
Assert(IsA(expr, Const));
m_isnull[i] = ((Const*)expr)->constisnull;
m_values[i] = ((Const*)expr)->constvalue;
} else if (IsA(expr, OpExpr)) {
opexpr = (OpExpr*)expr;
m_targetFuncNodes[m_targetFuncNum].resno = res->resno;
m_targetFuncNodes[m_targetFuncNum].resname = res->resname;
m_targetFuncNodes[m_targetFuncNum].funcid = opexpr->opfuncid;
m_targetFuncNodes[m_targetFuncNum].args = opexpr->args;
++m_targetFuncNum;
}
i++;
}
initParams(params);
m_receiver = NULL;
m_isInsideRec = true;
MemoryContextSwitchTo(old_context);
}
void InsertFusion::refreshParameterIfNecessary()
{
ParamListInfo parms = m_outParams != NULL ? m_outParams : m_params;
bool func_isnull = false;
/* save cur var value */
for (int i = 0; i < m_tupDesc->natts; i++) {
m_curVarValue[i] = m_values[i];
m_curVarIsnull[i] = m_isnull[i];
}
/* calculate func result */
for (int i = 0; i < m_targetFuncNum; ++i) {
ELOG_FIELD_NAME_START(m_targetFuncNodes[i].resname);
if (m_targetFuncNodes[i].funcid != InvalidOid) {
func_isnull = false;
m_values[m_targetFuncNodes[i].resno - 1] = CalFuncNodeVal(
m_targetFuncNodes[i].funcid, m_targetFuncNodes[i].args, &func_isnull, m_curVarValue, m_curVarIsnull);
m_isnull[m_targetFuncNodes[i].resno - 1] = func_isnull;
}
ELOG_FIELD_NAME_END;
}
/* mapping params */
if (m_targetParamNum > 0) {
for (int i = 0; i < m_targetParamNum; i++) {
m_values[m_paramLoc[i].scanKeyIndx] = parms->params[m_paramLoc[i].paramId - 1].value;
m_isnull[m_paramLoc[i].scanKeyIndx] = parms->params[m_paramLoc[i].paramId - 1].isnull;
}
}
}
bool InsertFusion::execute(long max_rows, char* completionTag)
{
bool success = false;
Oid partOid = InvalidOid;
Partition part = NULL;
Relation partRel = NULL;
/*******************
* step 1: prepare *
*******************/
Relation rel = heap_open(m_reloid, RowExclusiveLock);
Relation bucket_rel = NULL;
int2 bucketid = InvalidBktId;
ResultRelInfo* result_rel_info = makeNode(ResultRelInfo);
InitResultRelInfo(result_rel_info, rel, 1, 0);
m_estate->es_result_relation_info = result_rel_info;
if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) {
ExecOpenIndices(result_rel_info, false);
}
CommandId mycid = GetCurrentCommandId(true);
refreshParameterIfNecessary();
init_gtt_storage(CMD_INSERT, result_rel_info);
/************************
* step 2: begin insert *
************************/
HeapTuple tuple = (HeapTuple)tableam_tops_form_tuple(m_tupDesc, m_values, m_isnull, HEAP_TUPLE);
Assert(tuple != NULL);
if (RELATION_IS_PARTITIONED(rel)) {
m_estate->esfRelations = NULL;
partOid = heapTupleGetPartitionId(rel, tuple);
part = partitionOpen(rel, partOid, RowExclusiveLock);
partRel = partitionGetRelation(rel, part);
}
if (m_is_bucket_rel) {
bucketid = computeTupleBucketId(result_rel_info->ri_RelationDesc, tuple);
bucket_rel = InitBucketRelation(bucketid, rel, part);
}
(void)ExecStoreTuple(tuple, m_reslot, InvalidBuffer, false);
if (rel->rd_att->constr) {
ExecConstraints(result_rel_info, m_reslot, m_estate);
}
Relation destRel = RELATION_IS_PARTITIONED(rel) ? partRel : rel;
(void)tableam_tuple_insert(bucket_rel == NULL ? destRel : bucket_rel, tuple, mycid, 0, NULL);
if (!RELATION_IS_PARTITIONED(rel)) {
/* try to insert tuple into mlog-table. */
if (rel != NULL && rel->rd_mlogoid != InvalidOid) {
/* judge whether need to insert into mlog-table */
insert_into_mlog_table(rel, rel->rd_mlogoid, tuple, &tuple->t_self,
GetCurrentTransactionId(), 'I');
}
}
/* insert index entries for tuple */
List* recheck_indexes = NIL;
if (result_rel_info->ri_NumIndices > 0) {
recheck_indexes = ExecInsertIndexTuples(m_reslot,
&(tuple->t_self),
m_estate,
RELATION_IS_PARTITIONED(rel) ? partRel : NULL,
RELATION_IS_PARTITIONED(rel) ? part : NULL,
bucketid, NULL);
}
list_free_ext(recheck_indexes);
tableam_tops_free_tuple(tuple);
(void)ExecClearTuple(m_reslot);
success = true;
m_isCompleted = true;
/****************
* step 3: done *
****************/
ExecCloseIndices(result_rel_info);
heap_close(rel, RowExclusiveLock);
ExecDoneStepInFusion(bucket_rel, m_estate);
if (RELATION_IS_PARTITIONED(rel)) {
partitionClose(rel, part, RowExclusiveLock);
releaseDummyRelation(&partRel);
}
errno_t errorno = snprintf_s(completionTag, COMPLETION_TAG_BUFSIZE, COMPLETION_TAG_BUFSIZE - 1, "INSERT 0 1");
securec_check_ss(errorno, "\0", "\0");
return success;
}
#ifdef ENABLE_MOT
MotJitModifyFusion::MotJitModifyFusion(
MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
: OpFusion(context, psrc, plantree_list)
{
MemoryContext oldContext = MemoryContextSwitchTo(m_context);
ModifyTable* node = (ModifyTable*)m_planstmt->planTree;
m_cmdType = node->operation;
m_reloid = getrelid(linitial_int(m_planstmt->resultRelations), m_planstmt->rtable);
m_estate = CreateExecutorState();
m_estate->es_range_table = m_planstmt->rtable;
BaseResult* baseresult = (BaseResult*)linitial(node->plans);
List* targetList = baseresult->plan.targetlist;
m_tupDesc = ExecTypeFromTL(targetList, false);
m_reslot = MakeSingleTupleTableSlot(m_tupDesc);
/* init param */
m_paramNum = 0;
initParams(params);
m_receiver = NULL;
m_isInsideRec = true;
MemoryContextSwitchTo(oldContext);
}
bool MotJitModifyFusion::execute(long max_rows, char* completionTag)
{
bool success = false;
uint64_t tpProcessed = 0;
int scanEnded = 0;
ParamListInfo params = (m_outParams != NULL) ? m_outParams : m_params;
int rc = JitExec::JitExecQuery(m_cacheplan->mot_jit_context, params, m_reslot, &tpProcessed, &scanEnded);
if (rc == 0) {
(void)ExecClearTuple(m_reslot);
success = true;
errno_t ret = EOK;
switch (m_cmdType)
{
case CMD_INSERT:
ret = snprintf_s(completionTag, COMPLETION_TAG_BUFSIZE, COMPLETION_TAG_BUFSIZE - 1,
"INSERT 0 %lu", tpProcessed);
securec_check_ss(ret, "\0", "\0");
break;
case CMD_UPDATE:
ret = snprintf_s(completionTag, COMPLETION_TAG_BUFSIZE, COMPLETION_TAG_BUFSIZE - 1,
"UPDATE %lu", tpProcessed);
securec_check_ss(ret, "\0", "\0");
break;
case CMD_DELETE:
ret = snprintf_s(completionTag, COMPLETION_TAG_BUFSIZE, COMPLETION_TAG_BUFSIZE - 1,
"DELETE %lu", tpProcessed);
securec_check_ss(ret, "\0", "\0");
break;
default:
break;
}
}
m_isCompleted = true;
return success;
}
#endif
HeapTuple UpdateFusion::heapModifyTuple(HeapTuple tuple)
{
HeapTuple new_tuple;
tableam_tops_deform_tuple(tuple, m_tupDesc, m_values, m_isnull);
refreshTargetParameterIfNecessary();
/*
* create a new tuple from the values and isnull arrays
*/
new_tuple = (HeapTuple)tableam_tops_form_tuple(m_tupDesc, m_values, m_isnull, HEAP_TUPLE);
/*
* copy the identification info of the old tuple: t_ctid, t_self, and OID
* (if any)
*/
new_tuple->t_data->t_ctid = tuple->t_data->t_ctid;
new_tuple->t_self = tuple->t_self;
new_tuple->t_tableOid = tuple->t_tableOid;
new_tuple->t_bucketId = tuple->t_bucketId;
HeapTupleCopyBase(new_tuple, tuple);
#ifdef PGXC
new_tuple->t_xc_node_id = tuple->t_xc_node_id;
#endif
Assert(m_tupDesc->tdhasoid == false);
return new_tuple;
}
UpdateFusion::UpdateFusion(MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
: OpFusion(context, psrc, plantree_list)
{
m_attrno = NULL;
m_paramLoc = NULL;
m_tmpisnull = NULL;
m_tmpvals = NULL;
MemoryContext old_context = MemoryContextSwitchTo(m_context);
ModifyTable* node = (ModifyTable*)m_planstmt->planTree;
Plan *updatePlan = (Plan *)linitial(node->plans);
IndexScan* indexscan = (IndexScan *)JudgePlanIsPartIterator(updatePlan);
m_reloid = getrelid(linitial_int(m_planstmt->resultRelations), m_planstmt->rtable);
Relation rel = heap_open(m_reloid, AccessShareLock);
m_estate = CreateExecutorState();
m_estate->es_range_table = m_planstmt->rtable;
m_tupDesc = CreateTupleDescCopy(RelationGetDescr(rel));
m_reslot = MakeSingleTupleTableSlot(m_tupDesc);
m_values = (Datum*)palloc0(RelationGetDescr(rel)->natts * sizeof(Datum));
m_isnull = (bool*)palloc0(RelationGetDescr(rel)->natts * sizeof(bool));
m_is_bucket_rel = RELATION_OWN_BUCKET(rel);
heap_close(rel, AccessShareLock);
if (m_is_bucket_rel) {
// ctid + tablebucketid
Assert(RelationGetDescr(rel)->natts + 2 == list_length(indexscan->scan.plan.targetlist));
} else {
// ctid
if (indexscan->scan.isPartTbl) {
Assert(RelationGetDescr(rel)->natts + 2 == list_length(indexscan->scan.plan.targetlist));
} else {
Assert(RelationGetDescr(rel)->natts + 1 == list_length(indexscan->scan.plan.targetlist));
}
}
m_outParams = params;
/* init target, include param and const */
m_targetParamNum = 0;
m_targetValues = (Datum*)palloc0(RelationGetDescr(rel)->natts * sizeof(Datum));
m_targetIsnull = (bool*)palloc0(RelationGetDescr(rel)->natts * sizeof(bool));
m_targetParamLoc = (ParamLoc*)palloc0(RelationGetDescr(rel)->natts * sizeof(ParamLoc));
m_targetConstLoc = (int*)palloc0(RelationGetDescr(rel)->natts * sizeof(int));
m_curVarValue = (Datum*)palloc0(RelationGetDescr(rel)->natts * sizeof(Datum));
m_curVarIsnull = (bool*)palloc0(RelationGetDescr(rel)->natts * sizeof(bool));
m_targetVarLoc = (VarLoc*)palloc0(RelationGetDescr(rel)->natts * sizeof(VarLoc));
m_targetFuncNum = 0;
m_targetFuncNodes = (FuncExprInfo*)palloc0(RelationGetDescr(rel)->natts * sizeof(FuncExprInfo));
m_varNum = 0;
int i = 0;
ListCell* lc = NULL;
OpExpr* opexpr = NULL;
FuncExpr* func = NULL;
Expr* expr = NULL;
foreach (lc, indexscan->scan.plan.targetlist) {
// ignore ctid + tablebucketid or ctid at last
if (i >= RelationGetDescr(rel)->natts) {
break;
}
TargetEntry* res = (TargetEntry*)lfirst(lc);
expr = res->expr;
while (IsA(expr, RelabelType)) {
expr = ((RelabelType*)expr)->arg;
}
m_targetConstLoc[i] = -1;
if (IsA(expr, Param)) {
Param* param = (Param*)expr;
m_targetParamLoc[m_targetParamNum].paramId = param->paramid;
m_targetParamLoc[m_targetParamNum++].scanKeyIndx = i;
} else if (IsA(expr, Const)) {
m_targetIsnull[i] = ((Const*)expr)->constisnull;
m_targetValues[i] = ((Const*)expr)->constvalue;
m_targetConstLoc[i] = i;
} else if (IsA(expr, Var)) {
Var* var = (Var*)expr;
m_targetVarLoc[m_varNum].varNo = var->varattno;
m_targetVarLoc[m_varNum++].scanKeyIndx = i;
} else if (IsA(expr, OpExpr)) {
opexpr = (OpExpr*)expr;
m_targetFuncNodes[m_targetFuncNum].resno = res->resno;
m_targetFuncNodes[m_targetFuncNum].resname = res->resname;
m_targetFuncNodes[m_targetFuncNum].funcid = opexpr->opfuncid;
m_targetFuncNodes[m_targetFuncNum].args = opexpr->args;
++m_targetFuncNum;
} else if (IsA(expr, FuncExpr)) {
func = (FuncExpr*)expr;
m_targetFuncNodes[m_targetFuncNum].resno = res->resno;
m_targetFuncNodes[m_targetFuncNum].resname = res->resname;
m_targetFuncNodes[m_targetFuncNum].funcid = func->funcid;
m_targetFuncNodes[m_targetFuncNum].args = func->args;
++m_targetFuncNum;
}
i++;
}
m_targetNum = i;
initParams(params);
m_receiver = NULL;
m_isInsideRec = true;
m_scan = ScanFusion::getScanFusion((Node*)indexscan, m_planstmt, m_outParams);
MemoryContextSwitchTo(old_context);
}
void UpdateFusion::refreshTargetParameterIfNecessary()
{
ParamListInfo parms = m_outParams != NULL ? m_outParams : m_params;
/* save cur var value */
for (int i = 0; i < m_tupDesc->natts; i++) {
m_curVarValue[i] = m_values[i];
m_curVarIsnull[i] = m_isnull[i];
}
if (m_varNum > 0) {
for (int i = 0; i < m_varNum; i++) {
m_values[m_targetVarLoc[i].scanKeyIndx] = m_curVarValue[m_targetVarLoc[i].varNo - 1];
m_isnull[m_targetVarLoc[i].scanKeyIndx] = m_curVarIsnull[m_targetVarLoc[i].varNo - 1];
}
}
/* mapping value for update from target */
if (m_targetParamNum > 0) {
Assert(m_targetParamNum > 0);
for (int i = 0; i < m_targetParamNum; i++) {
m_values[m_targetParamLoc[i].scanKeyIndx] = parms->params[m_targetParamLoc[i].paramId - 1].value;
m_isnull[m_targetParamLoc[i].scanKeyIndx] = parms->params[m_targetParamLoc[i].paramId - 1].isnull;
}
}
for (int i = 0; i < m_targetNum; i++) {
if (m_targetConstLoc[i] >= 0) {
m_values[i] = m_targetValues[m_targetConstLoc[i]];
m_isnull[i] = m_targetIsnull[m_targetConstLoc[i]];
}
}
/* calculate func result */
for (int i = 0; i < m_targetFuncNum; ++i) {
ELOG_FIELD_NAME_START(m_targetFuncNodes[i].resname);
if (m_targetFuncNodes[i].funcid != InvalidOid) {
bool func_isnull = false;
m_values[m_targetFuncNodes[i].resno - 1] =
CalFuncNodeVal(m_targetFuncNodes[i].funcid, m_targetFuncNodes[i].args, &func_isnull,
m_curVarValue, m_curVarIsnull);
m_isnull[m_targetFuncNodes[i].resno - 1] = func_isnull;
}
ELOG_FIELD_NAME_END;
}
}
bool UpdateFusion::execute(long max_rows, char* completionTag)
{
bool success = false;
bool execMatview = false;
/*******************
* step 1: prepare *
*******************/
m_scan->refreshParameter(m_outParams == NULL ? m_params : m_outParams);
m_scan->Init(max_rows);
Relation rel = ((m_scan->m_parentRel) == NULL ? m_scan->m_rel : m_scan->m_parentRel);
Relation partRel = NULL;
Partition part = NULL;
Relation bucket_rel = NULL;
int2 bucketid = InvalidBktId;
ResultRelInfo* result_rel_info = makeNode(ResultRelInfo);
InitResultRelInfo(result_rel_info, rel, 1, 0);
m_estate->es_result_relation_info = result_rel_info;
m_estate->es_output_cid = GetCurrentCommandId(true);
if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) {
ExecOpenIndices(result_rel_info, false);
}
InitPartitionByScanFusion(rel, &partRel, &part, m_estate, m_scan);
/*********************************
* step 2: begin scan and update *
*********************************/
HeapTuple oldtup = NULL;
HeapTuple tup = NULL;
unsigned long nprocessed = 0;
List* recheck_indexes = NIL;
m_tupDesc = RelationGetDescr(rel);
while ((oldtup = m_scan->getTuple()) != NULL) {
CHECK_FOR_INTERRUPTS();
TM_Result result;
TM_FailureData tmfd;
lreplace:
Relation destRel = RELATION_IS_PARTITIONED(rel) ? partRel : rel;
Relation parentRel = RELATION_IS_PARTITIONED(rel) ? rel : NULL;
tup = heapModifyTuple(oldtup);
m_scan->UpdateCurrentRel(&rel);
if (m_is_bucket_rel) {
Assert(tup->t_bucketId == computeTupleBucketId(result_rel_info->ri_RelationDesc, tup));
bucketid = tup->t_bucketId;
bucket_rel = InitBucketRelation(bucketid, rel, part);
}
(void)ExecStoreTuple(tup, m_reslot, InvalidBuffer, false);
if (rel->rd_att->constr)
ExecConstraints(result_rel_info, m_reslot, m_estate);
bool update_indexes = false;
result = tableam_tuple_update(bucket_rel == NULL ? destRel : bucket_rel,
bucket_rel == NULL ? parentRel : rel,
&oldtup->t_self,
tup,
m_estate->es_output_cid,
InvalidSnapshot,
m_estate->es_snapshot,
true,
&tmfd,
&update_indexes,
false);
switch (result) {
case TM_SelfModified:
if (tmfd.cmax != m_estate->es_output_cid)
ereport(ERROR,
(errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
/* already deleted by self; nothing to do */
break;
case TM_Ok:
if (!RELATION_IS_PARTITIONED(rel)) {
/* handle with matview. */
execMatview = (rel != NULL && rel->rd_mlogoid != InvalidOid);
if (execMatview) {
/* judge whether need to insert into mlog-table */
/* 1. delete one tuple. */
insert_into_mlog_table(rel, rel->rd_mlogoid, NULL, &oldtup->t_self,
tmfd.xmin, 'D');
/* 2. insert new tuple */
insert_into_mlog_table(rel, rel->rd_mlogoid, tup, &(tup->t_self),
GetCurrentTransactionId(), 'I');
}
/* done successfully */
}
nprocessed++;
if (result_rel_info->ri_NumIndices > 0 && update_indexes) {
recheck_indexes = ExecInsertIndexTuples(m_reslot,
&(tup->t_self),
m_estate,
RELATION_IS_PARTITIONED(rel) ? partRel : NULL,
RELATION_IS_PARTITIONED(rel) ? part : NULL,
bucketid, NULL);
list_free_ext(recheck_indexes);
}
break;
case TM_Updated: {
if (IsolationUsesXactSnapshot()) {
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
}
Assert(!ItemPointerEquals(&tup->t_self, &tmfd.ctid));
bool* isnullfornew = NULL;
Datum* valuesfornew = NULL;
HeapTuple copyTuple;
copyTuple = heap_lock_updated(m_estate->es_output_cid,
bucket_rel == NULL ? destRel : bucket_rel,
LockTupleExclusive,
&tmfd.ctid,
tmfd.xmax);
if (copyTuple == NULL) {
pfree_ext(valuesfornew);
pfree_ext(isnullfornew);
break;
}
valuesfornew = (Datum*)palloc0(m_tupDesc->natts * sizeof(Datum));
isnullfornew = (bool*)palloc0(m_tupDesc->natts * sizeof(bool));
tableam_tops_deform_tuple(copyTuple, RelationGetDescr(rel), valuesfornew, isnullfornew);
if (m_scan->EpqCheck(valuesfornew, isnullfornew) == false) {
pfree_ext(valuesfornew);
pfree_ext(isnullfornew);
break;
}
oldtup->t_self = tmfd.ctid;
oldtup = copyTuple;
pfree(valuesfornew);
pfree(isnullfornew);
goto lreplace;
break;
}
case TM_Deleted:
if (IsolationUsesXactSnapshot()) {
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
}
Assert (ItemPointerEquals(&tup->t_self, &tmfd.ctid));
break;
default:
elog(ERROR, "unrecognized heap_update status: %u", result);
break;
}
}
tableam_tops_free_tuple(tup);
(void)ExecClearTuple(m_reslot);
success = true;
/****************
* step 3: done *
****************/
ExecCloseIndices(result_rel_info);
m_isCompleted = true;
m_scan->End(true);
ExecDoneStepInFusion(bucket_rel, m_estate);
errno_t errorno =
snprintf_s(completionTag, COMPLETION_TAG_BUFSIZE, COMPLETION_TAG_BUFSIZE - 1, "UPDATE %lu", nprocessed);
securec_check_ss(errorno, "\0", "\0");
return success;
}
DeleteFusion::DeleteFusion(MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
: OpFusion(context, psrc, plantree_list)
{
m_paramLoc = NULL;
m_tmpvals = NULL;
m_tmpisnull = NULL;
m_attrno = NULL;
MemoryContext old_context = MemoryContextSwitchTo(m_context);
ModifyTable* node = (ModifyTable*)m_planstmt->planTree;
Plan *deletePlan = (Plan *)linitial(node->plans);
IndexScan* indexscan = (IndexScan *)JudgePlanIsPartIterator(deletePlan);
m_reloid = getrelid(linitial_int(m_planstmt->resultRelations), m_planstmt->rtable);
Relation rel = heap_open(m_reloid, AccessShareLock);
m_estate = CreateExecutorState();
m_estate->es_range_table = m_planstmt->rtable;
m_tupDesc = CreateTupleDescCopy(RelationGetDescr(rel));
m_reslot = MakeSingleTupleTableSlot(m_tupDesc);
m_values = (Datum*)palloc0(RelationGetDescr(rel)->natts * sizeof(Datum));
m_isnull = (bool*)palloc0(RelationGetDescr(rel)->natts * sizeof(bool));
m_is_bucket_rel = RELATION_OWN_BUCKET(rel);
heap_close(rel, AccessShareLock);
initParams(params);
m_receiver = NULL;
m_isInsideRec = true;
m_scan = ScanFusion::getScanFusion((Node*)indexscan, m_planstmt, m_outParams);
MemoryContextSwitchTo(old_context);
}
bool DeleteFusion::execute(long max_rows, char* completionTag)
{
bool success = false;
bool execMatview = false;
/*******************
* step 1: prepare *
*******************/
m_scan->refreshParameter(m_outParams == NULL ? m_params : m_outParams);
m_scan->Init(max_rows);
Relation rel = ((m_scan->m_parentRel) == NULL ? m_scan->m_rel : m_scan->m_parentRel);
Relation bucket_rel = NULL;
int2 bucketid = InvalidBktId;
Relation partRel = NULL;
Partition part = NULL;
ResultRelInfo* result_rel_info = makeNode(ResultRelInfo);
InitResultRelInfo(result_rel_info, rel, 1, 0);
m_estate->es_result_relation_info = result_rel_info;
m_estate->es_output_cid = GetCurrentCommandId(true);
if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) {
ExecOpenIndices(result_rel_info, false);
}
InitPartitionByScanFusion(rel, &partRel, &part, m_estate, m_scan);
/********************************
* step 2: begin scan and delete*
********************************/
HeapTuple oldtup = NULL;
unsigned long nprocessed = 0;
m_tupDesc = RelationGetDescr(rel);
while ((oldtup = m_scan->getTuple()) != NULL) {
TM_Result result;
TM_FailureData tmfd;
m_scan->UpdateCurrentRel(&rel);
if (m_is_bucket_rel) {
Assert(oldtup->t_bucketId == computeTupleBucketId(result_rel_info->ri_RelationDesc, oldtup));
bucketid = oldtup->t_bucketId;
bucket_rel = InitBucketRelation(bucketid, rel, part);
}
ldelete:
Relation destRel = RELATION_IS_PARTITIONED(rel) ? partRel : rel;
result = tableam_tuple_delete(bucket_rel == NULL ? destRel : bucket_rel,
&oldtup->t_self,
m_estate->es_output_cid,
//m_estate->es_snapshot,
InvalidSnapshot,
NULL,
true,
&tmfd,
false);
switch (result) {
case TM_SelfModified:
if (tmfd.cmax != m_estate->es_output_cid)
ereport(ERROR,
(errcode(ERRCODE_TRIGGERED_DATA_CHANGE_VIOLATION),
errmsg("tuple to be updated was already modified by an operation triggered by the current command"),
errhint("Consider using an AFTER trigger instead of a BEFORE trigger to propagate changes to other rows.")));
/* already deleted by self; nothing to do */
break;
case TM_Ok:
if (!RELATION_IS_PARTITIONED(rel)) {
/* Here Do the thing for Matview. */
execMatview = (rel != NULL && rel->rd_mlogoid != InvalidOid);
if (execMatview) {
/* judge whether need to insert into mlog-table */
insert_into_mlog_table(rel, rel->rd_mlogoid, NULL,
&oldtup->t_self, tmfd.xmin, 'D');
}
}
/* done successfully */
nprocessed++;
break;
case TM_Updated: {
if (IsolationUsesXactSnapshot()) {
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
}
Assert(!ItemPointerEquals(&oldtup->t_self, &tmfd.ctid));
bool* isnullfornew = NULL;
Datum* valuesfornew = NULL;
HeapTuple copyTuple;
copyTuple = heap_lock_updated(m_estate->es_output_cid,
bucket_rel == NULL ? destRel : bucket_rel,
LockTupleExclusive,
&tmfd.ctid,
tmfd.xmax);
if (copyTuple == NULL) {
break;
}
valuesfornew = (Datum*)palloc0(m_tupDesc->natts * sizeof(Datum));
isnullfornew = (bool*)palloc0(m_tupDesc->natts * sizeof(bool));
tableam_tops_deform_tuple(copyTuple, RelationGetDescr(rel), valuesfornew, isnullfornew);
if (m_scan->EpqCheck(valuesfornew, isnullfornew) == false) {
break;
}
oldtup->t_self = tmfd.ctid;
oldtup = copyTuple;
pfree(valuesfornew);
pfree(isnullfornew);
goto ldelete;
break;
}
case TM_Deleted:
if (IsolationUsesXactSnapshot()) {
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
}
Assert(ItemPointerEquals(&oldtup->t_self, &tmfd.ctid));
break;
default:
elog(ERROR, "unrecognized heap_delete status: %u", result);
break;
}
}
(void)ExecClearTuple(m_reslot);
success = true;
/****************
* step 3: done *
****************/
ExecCloseIndices(result_rel_info);
m_isCompleted = true;
m_scan->End(true);
ExecDoneStepInFusion(bucket_rel, m_estate);
errno_t errorno =
snprintf_s(completionTag, COMPLETION_TAG_BUFSIZE, COMPLETION_TAG_BUFSIZE - 1, "DELETE %lu", nprocessed);
securec_check_ss(errorno, "\0", "\0");
return success;
}
SelectForUpdateFusion::SelectForUpdateFusion(
MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
: OpFusion(context, psrc, plantree_list)
{
m_paramLoc = NULL;
MemoryContext old_context = MemoryContextSwitchTo(m_context);
IndexScan* node = NULL;
m_limitCount = -1;
m_limitOffset = -1;
/* get limit num */
if (IsA(m_planstmt->planTree, Limit)) {
Limit* limit = (Limit*)m_planstmt->planTree;
node = (IndexScan *)JudgePlanIsPartIterator(m_planstmt->planTree->lefttree->lefttree);
if (limit->limitOffset != NULL && IsA(limit->limitOffset, Const) &&
!((Const*)limit->limitOffset)->constisnull) {
m_limitOffset = DatumGetInt64(((Const*)limit->limitOffset)->constvalue);
}
if (limit->limitCount != NULL && IsA(limit->limitCount, Const) && !((Const*)limit->limitCount)->constisnull) {
m_limitCount = DatumGetInt64(((Const*)limit->limitCount)->constvalue);
}
} else {
node = (IndexScan *)JudgePlanIsPartIterator(m_planstmt->planTree->lefttree);
}
List* targetList = node->scan.plan.targetlist;
m_reloid = getrelid(node->scan.scanrelid, m_planstmt->rtable);
Relation rel = heap_open(m_reloid, AccessShareLock);
Assert(list_length(targetList) >= 2);
m_tupDesc = ExecCleanTypeFromTL(targetList, false, rel->rd_tam_type);
m_reslot = MakeSingleTupleTableSlot(m_tupDesc);
m_estate = CreateExecutorState();
m_estate->es_range_table = m_planstmt->rtable;
m_attrno = (int16*)palloc(m_tupDesc->natts * sizeof(int16));
m_values = (Datum*)palloc(RelationGetDescr(rel)->natts * sizeof(Datum));
m_tmpvals = (Datum*)palloc(m_tupDesc->natts * sizeof(Datum));
m_isnull = (bool*)palloc(RelationGetDescr(rel)->natts * sizeof(bool));
m_tmpisnull = (bool*)palloc(m_tupDesc->natts * sizeof(bool));
m_is_bucket_rel = RELATION_OWN_BUCKET(rel);
ListCell* lc = NULL;
int cur_resno = 1;
TargetEntry* res = NULL;
foreach (lc, targetList) {
res = (TargetEntry*)lfirst(lc);
if (res->resjunk) {
continue;
}
m_attrno[cur_resno - 1] = res->resorigcol;
cur_resno++;
}
Assert(m_tupDesc->natts == cur_resno - 1);
heap_close(rel, AccessShareLock);
initParams(params);
m_receiver = NULL;
m_isInsideRec = true;
m_scan = ScanFusion::getScanFusion((Node*)node, m_planstmt, m_outParams);
MemoryContextSwitchTo(old_context);
}
bool SelectForUpdateFusion::execute(long max_rows, char* completionTag)
{
bool success = false;
int64 start_row = 0;
int64 get_rows = 0;
/*******************
* step 1: prepare *
*******************/
IndexScan* node = NULL;
if (m_limitCount >= 0 || m_limitOffset >= 0) {
node = (IndexScan*)m_planstmt->planTree->lefttree->lefttree;
} else {
node = (IndexScan*)m_planstmt->planTree->lefttree;
}
start_row = m_limitOffset >= 0 ? m_limitOffset : start_row;
get_rows = m_limitCount >= 0 ? (m_limitCount + start_row) : max_rows;
if (m_position == 0) {
m_scan->refreshParameter(m_outParams == NULL ? m_params : m_outParams);
m_scan->Init(max_rows);
}
Relation rel = ((m_scan->m_parentRel == NULL) ? m_scan->m_rel : m_scan->m_parentRel);
Relation partRel = NULL;
Relation bucket_rel = NULL;
Partition part = NULL;
int2 bucketid = InvalidBktId;
ResultRelInfo* result_rel_info = makeNode(ResultRelInfo);
InitResultRelInfo(result_rel_info, rel, 1, 0);
m_estate->es_result_relation_info = result_rel_info;
m_estate->es_output_cid = GetCurrentCommandId(true);
if (result_rel_info->ri_RelationDesc->rd_rel->relhasindex) {
ExecOpenIndices(result_rel_info, false);
}
InitPartitionByScanFusion(rel, &partRel, &part, m_estate, m_scan);
/**************************************
* step 2: begin scan and update xmax *
**************************************/
setReceiver();
HeapTuple tuple = NULL;
HeapTuple tmptup = NULL;
unsigned long nprocessed = 0;
TM_Result result;
TM_FailureData tmfd;
Buffer buffer;
while (nprocessed < (unsigned long)start_row && (tuple = m_scan->getTuple()) != NULL) {
nprocessed++;
}
while (nprocessed < (unsigned long)get_rows && (tuple = m_scan->getTuple()) != NULL) {
m_scan->UpdateCurrentRel(&rel);
CHECK_FOR_INTERRUPTS();
tableam_tops_deform_tuple(tuple, RelationGetDescr(rel), m_values, m_isnull);
for (int i = 0; i < m_tupDesc->natts; i++) {
Assert(m_attrno[i] > 0);
m_tmpvals[i] = m_values[m_attrno[i] - 1];
m_tmpisnull[i] = m_isnull[m_attrno[i] - 1];
}
tmptup = (HeapTuple)tableam_tops_form_tuple(m_tupDesc, m_tmpvals, m_tmpisnull, HEAP_TUPLE);
Assert(tmptup != NULL);
{
if (m_is_bucket_rel) {
Assert(tuple->t_bucketId == computeTupleBucketId(result_rel_info->ri_RelationDesc, tuple));
bucketid = tuple->t_bucketId;
bucket_rel = InitBucketRelation(bucketid, rel, part);
}
(void)ExecStoreTuple(tmptup, /* tuple to store */
m_reslot, /* slot to store in */
InvalidBuffer, /* TO DO: survey */
false); /* don't pfree this pointer */
Relation destRel = RELATION_IS_PARTITIONED(rel) ? partRel : rel;
tableam_tslot_getsomeattrs(m_reslot, m_tupDesc->natts);
result = tableam_tuple_lock(bucket_rel == NULL ? destRel : bucket_rel,
tuple,
&buffer,
m_estate->es_output_cid,
LockTupleExclusive,
false,
&tmfd, false, false, false, InvalidSnapshot, NULL, false);
ReleaseBuffer(buffer);
if (result == TM_SelfModified) {
continue;
}
switch (result) {
case TM_SelfCreated:
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("attempted to lock invisible tuple")));
break;
case TM_SelfModified:
/*
* The target tuple was already updated or deleted by the
* current command, or by a later command in the current
* transaction. We *must* ignore the tuple in the former
* case, so as to avoid the "Halloween problem" of repeated
* update attempts. In the latter case it might be sensible
* to fetch the updated tuple instead, but doing so would
* require changing heap_update and heap_delete to not
* complain about updating "invisible" tuples, which seems
* pretty scary (heap_lock_tuple will not complain, but few
* callers expect HeapTupleInvisible, and we're not one of
* them). So for now, treat the tuple as deleted and do not
* process.
*/
/* already deleted by self; nothing to do */
break;
case TM_Ok:
/* done successfully */
nprocessed++;
(*m_receiver->receiveSlot)(m_reslot, m_receiver);
tpslot_free_heaptuple(m_reslot);
break;
case TM_Updated: {
if (IsolationUsesXactSnapshot()) {
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
}
Assert(!ItemPointerEquals(&tuple->t_self, &tmfd.ctid));
bool* isnullfornew = NULL;
Datum* valuesfornew = NULL;
HeapTuple copyTuple;
copyTuple = heap_lock_updated(m_estate->es_output_cid,
bucket_rel == NULL ? destRel : bucket_rel,
LockTupleExclusive,
&tmfd.ctid,
tmfd.xmax);
if (copyTuple == NULL) {
break;
}
valuesfornew = (Datum*)palloc0(RelationGetDescr(rel)->natts * sizeof(Datum));
isnullfornew = (bool*)palloc0(RelationGetDescr(rel)->natts * sizeof(bool));
tableam_tops_deform_tuple(copyTuple, RelationGetDescr(rel), valuesfornew, isnullfornew);
if (m_scan->EpqCheck(valuesfornew, isnullfornew) == false) {
pfree_ext(valuesfornew);
pfree_ext(isnullfornew);
break;
}
for (int i = 0; i < m_tupDesc->natts; i++) {
m_tmpvals[i] = valuesfornew[m_attrno[i] - 1];
m_tmpisnull[i] = isnullfornew[m_attrno[i] - 1];
}
tmptup = (HeapTuple)tableam_tops_form_tuple(m_tupDesc, m_tmpvals, m_tmpisnull, HEAP_TUPLE);
Assert(tmptup != NULL);
(void)ExecStoreTuple(tmptup, /* tuple to store */
m_reslot, /* slot to store in */
InvalidBuffer, /* TO DO: survey */
false); /* don't pfree this pointer */
tableam_tslot_getsomeattrs(m_reslot, m_tupDesc->natts);
nprocessed++;
(*m_receiver->receiveSlot)(m_reslot, m_receiver);
tuple->t_self = tmfd.ctid;
tuple = copyTuple;
pfree(valuesfornew);
pfree(isnullfornew);
break;
}
case TM_Deleted:
if (IsolationUsesXactSnapshot()) {
ereport(ERROR,
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
errmsg("could not serialize access due to concurrent update")));
}
Assert(ItemPointerEquals(&tuple->t_self, &tmfd.ctid));
break;
default:
elog(ERROR, "unrecognized heap_lock_tuple status: %u", result);
break;
}
}
}
if (!ScanDirectionIsNoMovement(*(m_scan->m_direction))) {
if (max_rows == 0 || nprocessed < (unsigned long)max_rows) {
m_isCompleted = true;
}
m_position += nprocessed;
} else {
m_isCompleted = true;
}
(void)ExecClearTuple(m_reslot);
success = true;
/****************
* step 3: done *
****************/
ExecCloseIndices(result_rel_info);
m_scan->End(m_isCompleted);
if (m_isCompleted) {
m_position = 0;
}
ExecDoneStepInFusion(bucket_rel, m_estate);
if (m_isInsideRec) {
(*m_receiver->rDestroy)(m_receiver);
}
errno_t errorno =
snprintf_s(completionTag, COMPLETION_TAG_BUFSIZE, COMPLETION_TAG_BUFSIZE - 1, "SELECT %lu", nprocessed);
securec_check_ss(errorno, "\0", "\0");
return success;
}
void SelectForUpdateFusion::close()
{
if (m_isCompleted == false) {
m_scan->End(true);
m_isCompleted = true;
m_position = 0;
}
}
AggFusion::AggFusion(MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
: OpFusion(context, psrc, plantree_list)
{
m_tmpvals = NULL;
m_values = NULL;
m_isnull = NULL;
m_tmpisnull = NULL;
m_paramLoc = NULL;
m_attrno = NULL;
m_reloid = 0;
MemoryContext oldContext = MemoryContextSwitchTo(m_context);
Agg *aggnode = (Agg *)m_planstmt->planTree;
Node* scan = JudgePlanIsPartIterator(aggnode->plan.lefttree);
initParams(params);
m_receiver = NULL;
m_isInsideRec = true;
m_scan = ScanFusion::getScanFusion(scan, m_planstmt, m_outParams);
m_reslot = NULL;
/* agg init */
List *targetList = aggnode->plan.targetlist;
TargetEntry *tar = (TargetEntry *)linitial(targetList);
Aggref *aggref = (Aggref *)tar->expr;
switch (aggref->aggfnoid) {
case INT2SUMFUNCOID:
m_aggSumFunc = &AggFusion::agg_int2_sum;
break;
case INT4SUMFUNCOID:
m_aggSumFunc = &AggFusion::agg_int4_sum;
break;
case INT8SUMFUNCOID:
m_aggSumFunc = &AggFusion::agg_int8_sum;
break;
case NUMERICSUMFUNCOID:
m_aggSumFunc = &AggFusion::agg_numeric_sum;
break;
default:
elog(ERROR, "unsupported aggfnoid %u for bypass.", aggref->aggfnoid);
break;
}
HeapTuple aggTuple = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(aggref->aggfnoid));
if (!HeapTupleIsValid(aggTuple)) {
elog(ERROR, "cache lookup failed for aggregate %u",
aggref->aggfnoid);
}
ReleaseSysCache(aggTuple);
m_tupDesc = ExecTypeFromTL(targetList, false);
m_attrno = (int16 *) palloc(m_tupDesc->natts * sizeof(int16));
/* m_tupDesc->natts always be 1 currently. */
TargetEntry *res = NULL;
res = (TargetEntry *)linitial(aggref->args);
Var *var = (Var *)res->expr;
m_attrno[0] = var->varattno;
m_isCompleted = true;
m_reslot = MakeSingleTupleTableSlot(m_tupDesc);
m_values = (Datum*)palloc0(m_tupDesc->natts * sizeof(Datum));
m_isnull = (bool*)palloc0(m_tupDesc->natts * sizeof(bool));
MemoryContextSwitchTo(oldContext);
}
bool AggFusion::execute(long max_rows, char *completionTag)
{
max_rows = FETCH_ALL;
bool success = false;
TupleTableSlot *reslot = m_reslot;
Datum* values = m_values;
bool * isnull = m_isnull;
for (int i = 0; i < m_tupDesc->natts; i++) {
isnull[i] = true;
}
/* step 2: begin scan */
m_scan->refreshParameter(m_outParams == NULL ? m_params : m_outParams);
m_scan->Init(max_rows);
setReceiver();
long nprocessed = 0;
TupleTableSlot *slot = NULL;
while ((slot = m_scan->getTupleSlot()) != NULL) {
CHECK_FOR_INTERRUPTS();
nprocessed++;
{
AutoContextSwitch memSwitch(m_tmpContext);
for (int i = 0; i < m_tupDesc->natts; i++) {
reslot->tts_values[i] = slot->tts_values[m_attrno[i] - 1];
reslot->tts_isnull[i] = slot->tts_isnull[m_attrno[i] - 1];
(this->*m_aggSumFunc)(&values[i], isnull[i],
&reslot->tts_values[i], reslot->tts_isnull[i]);
isnull[i] = false;
}
}
if (nprocessed >= max_rows) {
break;
}
}
HeapTuple tmptup = (HeapTuple)tableam_tops_form_tuple(m_tupDesc, values, isnull, HEAP_TUPLE);
(void)ExecStoreTuple(tmptup, reslot, InvalidBuffer, false);
tableam_tslot_getsomeattrs(reslot, m_tupDesc->natts);
(*m_receiver->receiveSlot)(reslot, m_receiver);
tpslot_free_heaptuple(m_reslot);
success = true;
/* step 3: done */
if (m_isInsideRec == true) {
(*m_receiver->rDestroy)(m_receiver);
}
m_isCompleted = true;
m_scan->End(true);
errno_t errorno = snprintf_s(completionTag, COMPLETION_TAG_BUFSIZE, COMPLETION_TAG_BUFSIZE - 1,
"SELECT %lu", nprocessed);
securec_check_ss(errorno, "\0", "\0");
return success;
}
void
AggFusion::agg_int2_sum(Datum *transVal, bool transIsNull, Datum *inVal, bool inIsNull)
{
int64 newval;
if (unlikely(inIsNull)) {
return;
}
if (unlikely(transIsNull)) {
newval = (int64)DatumGetInt16(*inVal);
*transVal = Int64GetDatum(newval);
return;
}
int64 oldsum = DatumGetInt64(*transVal);
newval = oldsum + (int64)DatumGetInt16(*inVal);
*transVal = Int64GetDatum(newval);
}
void
AggFusion::agg_int4_sum(Datum *transVal, bool transIsNull, Datum *inVal, bool inIsNull)
{
int64 newval;
if (unlikely(inIsNull)) {
return;
}
if (unlikely(transIsNull)) {
newval = (int64)DatumGetInt32(*inVal);
*transVal = Int64GetDatum(newval);
return;
}
int64 oldsum = DatumGetInt64(*transVal);
newval = oldsum + (int64)DatumGetInt32(*inVal);
*transVal = Int64GetDatum(newval);
}
void
AggFusion::agg_int8_sum(Datum *transVal, bool transIsNull, Datum *inVal, bool inIsNull)
{
Numeric num1;
Numeric num2;
Numeric res;
NumericVar result;
int64 val;
Datum newVal;
NumericVar arg1;
NumericVar arg2;
if (unlikely(inIsNull)) {
return;
}
if (unlikely(transIsNull)) {
val = DatumGetInt64(*inVal);
init_var(&result);
int64_to_numericvar(val, &result);
res = make_result(&result);
free_var(&result);
newVal = NumericGetDatum(res);
newVal = datumCopy(newVal, false, -1);
*transVal = newVal;
return;
}
val = DatumGetInt64(*inVal);
init_var(&result);
int64_to_numericvar(val, &result);
res = make_result(&result);
free_var(&result);
num1 = DatumGetNumeric(*transVal);
num2 = res;
init_var_from_num(num1, &arg1);
init_var_from_num(num2, &arg2);
init_var(&result);
add_var(&arg1, &arg2, &result);
res = make_result(&result);
free_var(&result);
newVal = NumericGetDatum(res);
newVal = datumCopy(newVal, false, -1);
if (likely(!transIsNull)) {
pfree(DatumGetPointer(*transVal));
}
*transVal = newVal;
return;
}
void AggFusion::agg_numeric_sum(Datum *transVal, bool transIsNull, Datum *inVal, bool inIsNull)
{
Numeric num1;
Numeric num2;
Numeric res;
NumericVar result;
Datum newVal;
NumericVar arg1;
NumericVar arg2;
if (unlikely(inIsNull)) {
return;
}
if (unlikely(transIsNull)) {
*transVal = datumCopy(*inVal, false, -1);
return;
}
num1 = DatumGetNumeric(*transVal);
num2 = DatumGetNumeric(*inVal);
init_var_from_num(num1, &arg1);
init_var_from_num(num2, &arg2);
init_var(&result);
add_var(&arg1, &arg2, &result);
res = make_result(&result);
free_var(&result);
newVal = NumericGetDatum(res);
newVal = datumCopy(newVal, false, -1);
if (likely(!transIsNull)) {
pfree(DatumGetPointer(*transVal));
}
*transVal = newVal;
return;
}
SortFusion::SortFusion(MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
: OpFusion(context, psrc, plantree_list)
{
m_tmpvals = NULL;
m_values = NULL;
m_isnull = NULL;
m_tmpisnull = NULL;
m_paramLoc = NULL;
m_attrno = NULL;
m_reloid = 0;
MemoryContext oldContext = MemoryContextSwitchTo(m_context);
Sort *sortnode = (Sort *)m_planstmt->planTree;
m_tupDesc = ExecCleanTypeFromTL(sortnode->plan.targetlist, false);
m_attrno = (int16 *) palloc(m_tupDesc->natts * sizeof(int16));
ListCell *lc = NULL;
int cur_resno = 1;
foreach (lc, sortnode->plan.targetlist) {
TargetEntry *res = (TargetEntry *)lfirst(lc);
if (res->resjunk)
continue;
Var *var = (Var *)res->expr;
m_attrno[cur_resno - 1] = var->varattno;
cur_resno++;
}
Plan *node = (Plan*)m_planstmt->planTree->lefttree;
m_scanDesc = ExecTypeFromTL(node->targetlist, false);
initParams(params);
m_receiver = NULL;
m_isInsideRec = true;
m_scan = ScanFusion::getScanFusion((Node*)sortnode->plan.lefttree, m_planstmt, m_outParams);
m_tupDesc->tdTableAmType = m_scan->m_tupDesc->tdTableAmType;
m_scanDesc->tdTableAmType = m_scan->m_tupDesc->tdTableAmType;
m_reslot = NULL;
m_isCompleted = true;
m_reslot = MakeSingleTupleTableSlot(m_tupDesc, false, m_scan->m_tupDesc->tdTableAmType);
m_values = (Datum*)palloc0(m_tupDesc->natts * sizeof(Datum));
m_isnull = (bool*)palloc0(m_tupDesc->natts * sizeof(bool));
MemoryContextSwitchTo(oldContext);
}
bool SortFusion::execute(long max_rows, char *completionTag)
{
max_rows = FETCH_ALL;
bool success = false;
TupleTableSlot *reslot = m_reslot;
Datum *values = m_values;
bool *isnull = m_isnull;
for (int i = 0; i < m_tupDesc->natts; i++) {
isnull[i] = true;
}
/* prepare */
m_scan->refreshParameter(m_outParams == NULL ? m_params : m_outParams);
m_scan->Init(max_rows);
setReceiver();
Tuplesortstate *tuplesortstate = NULL;
Sort *sortnode = (Sort *)m_planstmt->planTree;
int64 sortMem = SET_NODEMEM(sortnode->plan.operatorMemKB[0], sortnode->plan.dop);
int64 maxMem = (sortnode->plan.operatorMaxMem > 0) ?
SET_NODEMEM(sortnode->plan.operatorMaxMem, sortnode->plan.dop) : 0;
{
AutoContextSwitch memSwitch(m_context);
tuplesortstate = tuplesort_begin_heap(m_scanDesc,
sortnode->numCols,
sortnode->sortColIdx,
sortnode->sortOperators,
sortnode->collations,
sortnode->nullsFirst,
sortMem,
false,
maxMem,
sortnode->plan.plan_node_id,
SET_DOP(sortnode->plan.dop));
}
/* receive data from indexscan node */
long nprocessed = 0;
TupleTableSlot *slot = NULL;
while ((slot = m_scan->getTupleSlot()) != NULL) {
tuplesort_puttupleslot(tuplesortstate, slot);
}
/* sort all data */
tuplesort_performsort(tuplesortstate);
/* send sorted data to client */
slot = MakeSingleTupleTableSlot(m_scanDesc);
while (tuplesort_gettupleslot(tuplesortstate, true, slot, NULL)) {
tableam_tslot_getsomeattrs(slot, m_scanDesc->natts);
for (int i = 0; i < m_tupDesc->natts; i++) {
values[i] = slot->tts_values[m_attrno[i] - 1];
isnull[i] = slot->tts_isnull[m_attrno[i] - 1];
}
HeapTuple tmptup = (HeapTuple) tableam_tops_form_tuple(m_tupDesc, values, isnull, TableAMGetTupleType(m_tupDesc->tdTableAmType));
(void)ExecStoreTuple(tmptup, reslot, InvalidBuffer, false);
(*m_receiver->receiveSlot)(reslot, m_receiver);
tableam_tops_free_tuple(tmptup);
CHECK_FOR_INTERRUPTS();
nprocessed++;
if (nprocessed >= max_rows) {
break;
}
}
success = true;
/* step 3: done */
if (m_isInsideRec == true) {
(*m_receiver->rDestroy)(m_receiver);
}
m_isCompleted = true;
m_scan->End(true);
errno_t errorno = snprintf_s(completionTag, COMPLETION_TAG_BUFSIZE, COMPLETION_TAG_BUFSIZE - 1,
"SELECT %lu", nprocessed);
securec_check_ss(errorno, "\0", "\0");
return success;
}