2522 lines
81 KiB
C++
2522 lines
81 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 "catalog/pg_aggregate.h"
|
|
#include "catalog/storage_gtt.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 "utils/lsyscache.h"
|
|
#include "utils/snapmgr.h"
|
|
#include "storage/mot/jit_exec.h"
|
|
|
|
OpFusion::OpFusion(MemoryContext context, CachedPlanSource* psrc, List* plantree_list, FusionType ftype)
|
|
{
|
|
bool is_share = (ENABLE_DN_GPC && psrc != NULL && psrc->gpc.is_insert == true);
|
|
|
|
m_context = AllocSetContextCreate(
|
|
context, "OpfusionContext", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE,
|
|
is_share ? SHARED_CONTEXT : STANDARD_CONTEXT);
|
|
m_tmpContext = AllocSetContextCreate(context,
|
|
"FusionTemporaryContext",
|
|
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_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;
|
|
}
|
|
|
|
FusionType OpFusion::getFusionType(CachedPlan* plan, ParamListInfo params, List* plantree_list)
|
|
{
|
|
if (IsInitdb == true) {
|
|
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;
|
|
if (plan && plan->mot_jit_context && JitExec::IsMotCodegenEnabled()) {
|
|
if (((PlannedStmt*)st)->commandType == CMD_SELECT) {
|
|
result = MOT_JIT_SELECT_FUSION;
|
|
} else if (((PlannedStmt*)st)->commandType == CMD_INSERT) {
|
|
result = MOT_JIT_MODIFY_FUSION;
|
|
} else if (((PlannedStmt*)st)->commandType == CMD_UPDATE) {
|
|
result = MOT_JIT_MODIFY_FUSION;
|
|
} else if (((PlannedStmt*)st)->commandType == CMD_DELETE) {
|
|
result = MOT_JIT_MODIFY_FUSION;
|
|
} else {
|
|
result = NOBYPASS_NO_QUERY_TYPE;
|
|
}
|
|
} else {
|
|
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;
|
|
}
|
|
|
|
if (result == NESTLOOP_INDEX_FUSION) {
|
|
return NONE_FUSION;
|
|
}
|
|
}
|
|
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(>_queryId);
|
|
WLMCreateDNodeInfoOnDN(NULL);
|
|
WLMCreateIOInfoOnDN();
|
|
}
|
|
|
|
if (u_sess->attr.attr_common.XactReadOnly) {
|
|
ExecCheckXactReadOnly(m_planstmt);
|
|
}
|
|
if (!(u_sess->exec_cxt.CurrentOpFusionObj->m_cacheplan &&
|
|
u_sess->exec_cxt.CurrentOpFusionObj->m_cacheplan->storageEngineType == SE_TYPE_MM)) {
|
|
PushActiveSnapshot(GetTransactionSnapshot());
|
|
}
|
|
|
|
}
|
|
|
|
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);
|
|
const char* cmdtext = m_is_pbe_query ? m_psrc->query_string :
|
|
t_thrd.postgres_cxt.debug_query_string;
|
|
pgaudit_dml_table(object_name, cmdtext);
|
|
}
|
|
break;
|
|
case CMD_SELECT:
|
|
if (u_sess->attr.attr_security.Audit_DML_SELECT != 0) {
|
|
object_name = pgaudit_get_relation_name(m_planstmt->rtable);
|
|
const char* cmdtext = m_is_pbe_query ? m_psrc->query_string :
|
|
t_thrd.postgres_cxt.debug_query_string;
|
|
pgaudit_dml_table_select(object_name, cmdtext);
|
|
}
|
|
break;
|
|
/* Not support others */
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void OpFusion::executeEnd()
|
|
{
|
|
if (!(u_sess->exec_cxt.CurrentOpFusionObj->m_cacheplan &&
|
|
u_sess->exec_cxt.CurrentOpFusionObj->m_cacheplan->storageEngineType == SE_TYPE_MM)) {
|
|
PopActiveSnapshot();
|
|
}
|
|
|
|
|
|
#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
|
|
if (m_isCompleted) {
|
|
MemoryContextDeleteChildren(m_tmpContext);
|
|
/* reset the context. */
|
|
MemoryContextReset(m_tmpContext);
|
|
|
|
u_sess->exec_cxt.CurrentOpFusionObj = NULL;
|
|
m_outParams = NULL;
|
|
m_isCompleted = false;
|
|
}
|
|
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)
|
|
{
|
|
if (u_sess->exec_cxt.CurrentOpFusionObj != NULL) {
|
|
switch (op) {
|
|
case FUSION_EXECUTE: {
|
|
long max_rows = FETCH_ALL;
|
|
|
|
/* msg is null means 'U' message, need fetch all. */
|
|
if (msg != NULL) {
|
|
(void)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);
|
|
u_sess->exec_cxt.CurrentOpFusionObj->execute(max_rows, completionTag);
|
|
gstrace_exit(GS_TRC_ID_BypassExecutor);
|
|
u_sess->exec_cxt.CurrentOpFusionObj->executeEnd();
|
|
UpdateSingleNodeByPassUniqueSQLStat(isTopLevel);
|
|
break;
|
|
}
|
|
|
|
case FUSION_DESCRIB: {
|
|
u_sess->exec_cxt.CurrentOpFusionObj->decribe(msg);
|
|
break;
|
|
}
|
|
|
|
default: {
|
|
Assert(0);
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_CASE_NOT_FOUND),
|
|
errmsg("unrecognized bypass support process option: %d", (int)op)));
|
|
}
|
|
}
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
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::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);
|
|
|
|
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);
|
|
|
|
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::useOuterParameter(ParamListInfo params)
|
|
{
|
|
m_outParams = params;
|
|
}
|
|
|
|
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 && DB_IS_CMPT(DB_CMPT_A)) {
|
|
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);
|
|
|
|
MemoryContextSwitchTo(old_context);
|
|
}
|
|
|
|
void OpFusion::decribe(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);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
switch (nodeTag(arg)) {
|
|
case T_Const:
|
|
*is_null = ((Const*)arg)->constisnull;
|
|
return ((Const*)arg)->constvalue;
|
|
case T_Var:
|
|
*is_null = m_isnull[(((Var*)arg)->varattno - 1)];
|
|
return m_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);
|
|
} else if (IsA(((RelabelType*)arg)->arg, OpExpr)) {
|
|
OpExpr* expr = (OpExpr*)(((RelabelType*)arg)->arg);
|
|
return CalFuncNodeVal(expr->opfuncid, expr->args, is_null);
|
|
}
|
|
|
|
return EvalSimpleArg((Node*)((RelabelType*)arg)->arg, is_null);
|
|
}
|
|
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)
|
|
{
|
|
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]);
|
|
} else if (IsA(first_arg_node, OpExpr)) {
|
|
arg[0] = CalFuncNodeVal(((OpExpr*)first_arg_node)->opfuncid, ((OpExpr*)first_arg_node)->args, &is_nulls[0]);
|
|
} else {
|
|
arg[0] = EvalSimpleArg(first_arg_node, &is_nulls[0]);
|
|
}
|
|
|
|
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]);
|
|
*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;
|
|
}
|
|
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);
|
|
}
|
|
|
|
/* 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;
|
|
|
|
MemoryContextDeleteChildren(m_tmpContext);
|
|
/* reset the context. */
|
|
MemoryContextReset(m_tmpContext);
|
|
}
|
|
|
|
SelectFusion::SelectFusion(MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
|
|
: OpFusion(context, psrc, plantree_list, SELECT_FUSION)
|
|
{
|
|
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 = (Node*)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 = (Node*)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) {
|
|
nprocessed++;
|
|
}
|
|
while (nprocessed < (unsigned long)get_rows && (m_reslot = m_scan->getTupleSlot()) != NULL) {
|
|
CHECK_FOR_INTERRUPTS();
|
|
nprocessed++;
|
|
(*m_receiver->receiveSlot)(m_reslot, m_receiver);
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
|
|
MotJitSelectFusion::MotJitSelectFusion(MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
|
|
: OpFusion(context, psrc, plantree_list, MOT_JIT_SELECT_FUSION)
|
|
{
|
|
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;
|
|
}
|
|
|
|
InsertFusion::InsertFusion(MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
|
|
: OpFusion(context, psrc, plantree_list, INSERT_FUSION)
|
|
{
|
|
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);
|
|
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);
|
|
|
|
/* 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].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].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;
|
|
/* calculate func result */
|
|
for (int i = 0; i < m_targetFuncNum; ++i) {
|
|
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_isnull[m_targetFuncNodes[i].resno - 1] = func_isnull;
|
|
}
|
|
}
|
|
/* 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;
|
|
|
|
/*******************
|
|
* 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 = heap_form_tuple(m_tupDesc, m_values, m_isnull);
|
|
Assert(tuple != NULL);
|
|
if (m_is_bucket_rel) {
|
|
bucketid = computeTupleBucketId(result_rel_info->ri_RelationDesc, tuple);
|
|
if (bucketid != InvalidBktId) {
|
|
bucket_rel = bucketGetRelation(rel, NULL, bucketid);
|
|
} else {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("Invaild Oid when open hash bucket relation.")));
|
|
}
|
|
}
|
|
|
|
(void)ExecStoreTuple(tuple, m_reslot, InvalidBuffer, false);
|
|
|
|
if (rel->rd_att->constr) {
|
|
ExecConstraints(result_rel_info, m_reslot, m_estate);
|
|
}
|
|
|
|
(void)heap_insert(bucket_rel == NULL ? rel : bucket_rel, tuple, mycid, 0, NULL);
|
|
|
|
/* 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, NULL, NULL, bucketid, NULL);
|
|
}
|
|
list_free_ext(recheck_indexes);
|
|
|
|
heap_freetuple_ext(tuple);
|
|
|
|
(void)ExecClearTuple(m_reslot);
|
|
success = true;
|
|
m_isCompleted = true;
|
|
/****************
|
|
* step 3: done *
|
|
****************/
|
|
ExecCloseIndices(result_rel_info);
|
|
|
|
heap_close(rel, RowExclusiveLock);
|
|
|
|
if (m_estate->esfRelations) {
|
|
FakeRelationCacheDestroy(m_estate->esfRelations);
|
|
}
|
|
|
|
if (bucket_rel != NULL) {
|
|
bucketCloseRelation(bucket_rel);
|
|
}
|
|
|
|
errno_t errorno = snprintf_s(completionTag, COMPLETION_TAG_BUFSIZE, COMPLETION_TAG_BUFSIZE - 1, "INSERT 0 1");
|
|
securec_check_ss(errorno, "\0", "\0");
|
|
|
|
return success;
|
|
}
|
|
|
|
MotJitModifyFusion::MotJitModifyFusion(MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
|
|
: OpFusion(context, psrc, plantree_list, MOT_JIT_MODIFY_FUSION)
|
|
{
|
|
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;
|
|
}
|
|
|
|
HeapTuple UpdateFusion::heapModifyTuple(HeapTuple tuple)
|
|
{
|
|
HeapTuple new_tuple;
|
|
|
|
heap_deform_tuple(tuple, m_tupDesc, m_values, m_isnull);
|
|
|
|
refreshTargetParameterIfNecessary();
|
|
|
|
/*
|
|
* create a new tuple from the values and isnull arrays
|
|
*/
|
|
new_tuple = heap_form_tuple(m_tupDesc, m_values, m_isnull);
|
|
|
|
/*
|
|
* 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, UPDATE_FUSION)
|
|
{
|
|
m_attrno = NULL;
|
|
m_paramLoc = NULL;
|
|
m_tmpisnull = NULL;
|
|
m_tmpvals = 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;
|
|
|
|
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);
|
|
|
|
IndexScan* indexscan = (IndexScan*)linitial(node->plans);
|
|
if (m_is_bucket_rel) {
|
|
// ctid + tablebucketid
|
|
Assert(RelationGetDescr(rel)->natts + 2 == list_length(indexscan->scan.plan.targetlist));
|
|
} else {
|
|
// ctid
|
|
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_targetFuncNum = 0;
|
|
m_targetFuncNodes = (FuncExprInfo*)palloc0(RelationGetDescr(rel)->natts * sizeof(FuncExprInfo));
|
|
|
|
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, OpExpr)) {
|
|
opexpr = (OpExpr*)expr;
|
|
|
|
m_targetFuncNodes[m_targetFuncNum].resno = res->resno;
|
|
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].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*)linitial(node->plans), m_planstmt, m_outParams);
|
|
|
|
MemoryContextSwitchTo(old_context);
|
|
}
|
|
|
|
void UpdateFusion::refreshTargetParameterIfNecessary()
|
|
{
|
|
ParamListInfo parms = m_outParams != NULL ? m_outParams : m_params;
|
|
/* 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) {
|
|
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_isnull[m_targetFuncNodes[i].resno - 1] = func_isnull;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool UpdateFusion::execute(long max_rows, char* completionTag)
|
|
{
|
|
bool success = false;
|
|
|
|
/*******************
|
|
* step 1: prepare *
|
|
*******************/
|
|
m_scan->refreshParameter(m_outParams == NULL ? m_params : m_outParams);
|
|
|
|
m_scan->Init(max_rows);
|
|
|
|
Relation rel = m_scan->m_rel;
|
|
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);
|
|
}
|
|
|
|
/*********************************
|
|
* 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) {
|
|
if (RelationIsPartitioned(m_scan->m_rel)) {
|
|
rel = m_scan->getCurrentRel();
|
|
}
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
HTSU_Result result;
|
|
ItemPointerData update_ctid;
|
|
TransactionId update_xmax;
|
|
|
|
lreplace:
|
|
tup = heapModifyTuple(oldtup);
|
|
if (m_is_bucket_rel) {
|
|
Assert(tup->t_bucketId == computeTupleBucketId(result_rel_info->ri_RelationDesc, tup));
|
|
bucketid = tup->t_bucketId;
|
|
if (bucketid != InvalidBktId) {
|
|
bucket_rel = bucketGetRelation(rel, NULL, bucketid);
|
|
} else {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("Invaild hash bucket oid from current tuple for update.")));
|
|
}
|
|
}
|
|
|
|
(void)ExecStoreTuple(tup, m_reslot, InvalidBuffer, false);
|
|
if (rel->rd_att->constr)
|
|
ExecConstraints(result_rel_info, m_reslot, m_estate);
|
|
|
|
result = heap_update(bucket_rel == NULL ? rel : bucket_rel,
|
|
bucket_rel == NULL ? NULL : rel,
|
|
&oldtup->t_self,
|
|
tup,
|
|
&update_ctid,
|
|
&update_xmax,
|
|
m_estate->es_output_cid,
|
|
InvalidSnapshot,
|
|
true);
|
|
switch (result) {
|
|
case HeapTupleSelfUpdated:
|
|
/* already deleted by self; nothing to do */
|
|
break;
|
|
|
|
case HeapTupleMayBeUpdated:
|
|
/* done successfully */
|
|
nprocessed++;
|
|
if (result_rel_info->ri_NumIndices > 0 && !HeapTupleIsHeapOnly(tup)) {
|
|
recheck_indexes = ExecInsertIndexTuples(m_reslot, &(tup->t_self), m_estate,
|
|
NULL, NULL, bucketid, NULL);
|
|
list_free_ext(recheck_indexes);
|
|
}
|
|
break;
|
|
|
|
case HeapTupleUpdated:
|
|
if (IsolationUsesXactSnapshot()) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
|
|
errmsg("could not serialize access due to concurrent update")));
|
|
}
|
|
if (!ItemPointerEquals(&tup->t_self, &update_ctid)) {
|
|
bool* isnullfornew = NULL;
|
|
Datum* valuesfornew = NULL;
|
|
HeapTuple copyTuple;
|
|
copyTuple = EvalPlanQualFetch(m_estate,
|
|
bucket_rel == NULL ? rel : bucket_rel,
|
|
LockTupleExclusive,
|
|
&update_ctid,
|
|
update_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));
|
|
|
|
heap_deform_tuple(copyTuple, RelationGetDescr(rel), valuesfornew, isnullfornew);
|
|
|
|
if (m_scan->EpqCheck(valuesfornew, isnullfornew) == false) {
|
|
pfree_ext(valuesfornew);
|
|
pfree_ext(isnullfornew);
|
|
break;
|
|
}
|
|
oldtup->t_self = update_ctid;
|
|
oldtup = copyTuple;
|
|
pfree(valuesfornew);
|
|
pfree(isnullfornew);
|
|
|
|
goto lreplace;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
elog(ERROR, "unrecognized heap_update status: %d", result);
|
|
break;
|
|
}
|
|
}
|
|
|
|
heap_freetuple_ext(tup);
|
|
|
|
(void)ExecClearTuple(m_reslot);
|
|
success = true;
|
|
|
|
/****************
|
|
* step 3: done *
|
|
****************/
|
|
ExecCloseIndices(result_rel_info);
|
|
m_isCompleted = true;
|
|
m_scan->End(true);
|
|
if (m_estate->esfRelations) {
|
|
FakeRelationCacheDestroy(m_estate->esfRelations);
|
|
}
|
|
if (bucket_rel != NULL) {
|
|
bucketCloseRelation(bucket_rel);
|
|
}
|
|
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, DELETE_FUSION)
|
|
{
|
|
m_paramLoc = NULL;
|
|
m_tmpvals = NULL;
|
|
m_tmpisnull = 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;
|
|
|
|
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*)linitial(node->plans), m_planstmt, m_outParams);
|
|
|
|
MemoryContextSwitchTo(old_context);
|
|
}
|
|
|
|
bool DeleteFusion::execute(long max_rows, char* completionTag)
|
|
{
|
|
bool success = false;
|
|
|
|
/*******************
|
|
* step 1: prepare *
|
|
*******************/
|
|
m_scan->refreshParameter(m_outParams == NULL ? m_params : m_outParams);
|
|
|
|
m_scan->Init(max_rows);
|
|
|
|
Relation rel = m_scan->m_rel;
|
|
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);
|
|
}
|
|
|
|
/********************************
|
|
* step 2: begin scan and delete*
|
|
********************************/
|
|
HeapTuple oldtup = NULL;
|
|
unsigned long nprocessed = 0;
|
|
m_tupDesc = RelationGetDescr(rel);
|
|
|
|
while ((oldtup = m_scan->getTuple()) != NULL) {
|
|
if (RelationIsPartitioned(m_scan->m_rel)) {
|
|
rel = m_scan->getCurrentRel();
|
|
}
|
|
|
|
HTSU_Result result;
|
|
ItemPointerData update_ctid;
|
|
TransactionId update_xmax;
|
|
if (m_is_bucket_rel) {
|
|
Assert(oldtup->t_bucketId == computeTupleBucketId(result_rel_info->ri_RelationDesc, oldtup));
|
|
bucketid = oldtup->t_bucketId;
|
|
if (bucketid != InvalidBktId) {
|
|
bucket_rel = bucketGetRelation(rel, NULL, bucketid);
|
|
} else {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("Invaild hash bucket oid from current tuple for delete.")));
|
|
}
|
|
}
|
|
|
|
ldelete:
|
|
result = heap_delete(bucket_rel == NULL ? rel : bucket_rel,
|
|
&oldtup->t_self,
|
|
&update_ctid,
|
|
&update_xmax,
|
|
m_estate->es_output_cid,
|
|
InvalidSnapshot,
|
|
true);
|
|
switch (result) {
|
|
case HeapTupleSelfUpdated:
|
|
/* already deleted by self; nothing to do */
|
|
break;
|
|
|
|
case HeapTupleMayBeUpdated:
|
|
/* done successfully */
|
|
nprocessed++;
|
|
break;
|
|
|
|
case HeapTupleUpdated:
|
|
if (IsolationUsesXactSnapshot()) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
|
|
errmsg("could not serialize access due to concurrent update")));
|
|
}
|
|
if (!ItemPointerEquals(&oldtup->t_self, &update_ctid)) {
|
|
bool* isnullfornew = NULL;
|
|
Datum* valuesfornew = NULL;
|
|
HeapTuple copyTuple;
|
|
|
|
copyTuple = EvalPlanQualFetch(m_estate,
|
|
bucket_rel == NULL ? rel : bucket_rel,
|
|
LockTupleExclusive,
|
|
&update_ctid,
|
|
update_xmax);
|
|
if (copyTuple == NULL) {
|
|
break;
|
|
}
|
|
valuesfornew = (Datum*)palloc0(m_tupDesc->natts * sizeof(Datum));
|
|
isnullfornew = (bool*)palloc0(m_tupDesc->natts * sizeof(bool));
|
|
heap_deform_tuple(copyTuple, RelationGetDescr(rel), valuesfornew, isnullfornew);
|
|
if (m_scan->EpqCheck(valuesfornew, isnullfornew) == false) {
|
|
break;
|
|
}
|
|
oldtup->t_self = update_ctid;
|
|
oldtup = copyTuple;
|
|
pfree(valuesfornew);
|
|
pfree(isnullfornew);
|
|
goto ldelete;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
elog(ERROR, "unrecognized heap_delete status: %d", result);
|
|
break;
|
|
}
|
|
}
|
|
|
|
(void)ExecClearTuple(m_reslot);
|
|
success = true;
|
|
|
|
/****************
|
|
* step 3: done *
|
|
****************/
|
|
ExecCloseIndices(result_rel_info);
|
|
|
|
m_isCompleted = true;
|
|
m_scan->End(true);
|
|
|
|
if (bucket_rel != NULL) {
|
|
bucketCloseRelation(bucket_rel);
|
|
}
|
|
|
|
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, SELECT_FOR_UPDATE_FUSION)
|
|
{
|
|
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*)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*)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);
|
|
void* last_list = list_nth(targetList, list_length(targetList) - 1);
|
|
list_delete_ptr(targetList, last_list);
|
|
|
|
m_tupDesc = ExecCleanTypeFromTL(targetList, false);
|
|
|
|
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_rel;
|
|
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);
|
|
}
|
|
|
|
/**************************************
|
|
* step 2: begin scan and update xmax *
|
|
**************************************/
|
|
setReceiver();
|
|
|
|
HeapTuple tuple = NULL;
|
|
HeapTuple tmptup = NULL;
|
|
unsigned long nprocessed = 0;
|
|
|
|
HTSU_Result result;
|
|
ItemPointerData update_ctid;
|
|
TransactionId update_xmax;
|
|
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) {
|
|
if (RelationIsPartitioned(m_scan->m_rel)) {
|
|
rel = m_scan->getCurrentRel();
|
|
}
|
|
|
|
CHECK_FOR_INTERRUPTS();
|
|
heap_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 = heap_form_tuple(m_tupDesc, m_tmpvals, m_tmpisnull);
|
|
|
|
Assert(tmptup != NULL);
|
|
|
|
{
|
|
if (m_is_bucket_rel) {
|
|
Assert(tuple->t_bucketId == computeTupleBucketId(result_rel_info->ri_RelationDesc, tuple));
|
|
bucketid = tuple->t_bucketId;
|
|
if (bucketid != InvalidBktId) {
|
|
bucket_rel = bucketGetRelation(rel, NULL, bucketid);
|
|
} else {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
|
errmsg("Invaild hash bucket oid from current tuple for lock.")));
|
|
}
|
|
}
|
|
|
|
(void)ExecStoreTuple(tmptup, /* tuple to store */
|
|
m_reslot, /* slot to store in */
|
|
InvalidBuffer, /* TO DO: survey */
|
|
false); /* don't pfree this pointer */
|
|
|
|
slot_getsomeattrs(m_reslot, m_tupDesc->natts);
|
|
|
|
result = heap_lock_tuple(bucket_rel == NULL ? rel : bucket_rel,
|
|
tuple,
|
|
&buffer,
|
|
&update_ctid,
|
|
&update_xmax,
|
|
m_estate->es_output_cid,
|
|
LockTupleExclusive,
|
|
false);
|
|
ReleaseBuffer(buffer);
|
|
|
|
if (result == HeapTupleSelfUpdated) {
|
|
continue;
|
|
}
|
|
|
|
switch (result) {
|
|
case HeapTupleSelfCreated:
|
|
ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
|
|
errmsg("attempted to lock invisible tuple")));
|
|
break;
|
|
case HeapTupleSelfUpdated:
|
|
/* already deleted by self; nothing to do */
|
|
break;
|
|
|
|
case HeapTupleMayBeUpdated:
|
|
/* done successfully */
|
|
nprocessed++;
|
|
(*m_receiver->receiveSlot)(m_reslot, m_receiver);
|
|
break;
|
|
|
|
case HeapTupleUpdated:
|
|
if (IsolationUsesXactSnapshot()) {
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_T_R_SERIALIZATION_FAILURE),
|
|
errmsg("could not serialize access due to concurrent update")));
|
|
}
|
|
if (!ItemPointerEquals(&tuple->t_self, &update_ctid)) {
|
|
bool* isnullfornew = NULL;
|
|
Datum* valuesfornew = NULL;
|
|
HeapTuple copyTuple;
|
|
copyTuple = EvalPlanQualFetch(m_estate,
|
|
bucket_rel == NULL ? rel : bucket_rel,
|
|
LockTupleExclusive,
|
|
&update_ctid,
|
|
update_xmax);
|
|
if (copyTuple == NULL) {
|
|
break;
|
|
}
|
|
valuesfornew = (Datum*)palloc0(RelationGetDescr(rel)->natts * sizeof(Datum));
|
|
isnullfornew = (bool*)palloc0(RelationGetDescr(rel)->natts * sizeof(bool));
|
|
|
|
heap_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 = heap_form_tuple(m_tupDesc, m_tmpvals, m_tmpisnull);
|
|
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 */
|
|
|
|
slot_getsomeattrs(m_reslot, m_tupDesc->natts);
|
|
nprocessed++;
|
|
(*m_receiver->receiveSlot)(m_reslot, m_receiver);
|
|
tuple->t_self = update_ctid;
|
|
tuple = copyTuple;
|
|
pfree(valuesfornew);
|
|
pfree(isnullfornew);
|
|
}
|
|
break;
|
|
default:
|
|
elog(ERROR, "unrecognized heap_lock_tuple status: %d", result);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
heap_freetuple_ext(tmptup);
|
|
|
|
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;
|
|
}
|
|
if (m_estate->esfRelations) {
|
|
FakeRelationCacheDestroy(m_estate->esfRelations);
|
|
}
|
|
|
|
if (bucket_rel != NULL) {
|
|
bucketCloseRelation(bucket_rel);
|
|
}
|
|
|
|
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, AGG_INDEX_FUSION)
|
|
{
|
|
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;
|
|
|
|
initParams(params);
|
|
m_receiver = NULL;
|
|
m_isInsideRec = true;
|
|
m_scan = ScanFusion::getScanFusion((Node*)aggnode->plan.lefttree, 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 = heap_form_tuple(m_tupDesc, values, isnull);
|
|
(void)ExecStoreTuple(tmptup, reslot, InvalidBuffer, false);
|
|
heap_freetuple_ext(tmptup);
|
|
slot_getsomeattrs(reslot, m_tupDesc->natts);
|
|
|
|
(*m_receiver->receiveSlot)(reslot, m_receiver);
|
|
|
|
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, SORT_INDEX_FUSION)
|
|
{
|
|
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_reslot = NULL;
|
|
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 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)) {
|
|
slot_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 = heap_form_tuple(m_tupDesc, values, isnull);
|
|
(void)ExecStoreTuple(tmptup, reslot, InvalidBuffer, false);
|
|
heap_freetuple_ext(tmptup);
|
|
(*m_receiver->receiveSlot)(reslot, m_receiver);
|
|
|
|
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;
|
|
}
|
|
|
|
#if 0
|
|
NestLoopFusion::NestLoopFusion(MemoryContext context, CachedPlanSource* psrc, List* plantree_list, ParamListInfo params)
|
|
: OpFusion(context, psrc, plantree_list, NESTLOOP_INDEX_FUSION)
|
|
{
|
|
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);
|
|
|
|
NestLoop *nlplan = (NestLoop*)m_planstmt->planTree;
|
|
List *targetList = nlplan->join.plan.targetlist;
|
|
m_tupDesc = ExecTypeFromTL(targetList, false);
|
|
m_attrno = (int16 *) palloc(m_tupDesc->natts * sizeof(int16));
|
|
|
|
int lattnum = list_length(nlplan->join.plan.lefttree->targetlist);
|
|
|
|
int cur_resno = 1;
|
|
ListCell *lc = NULL;
|
|
foreach(lc, targetList)
|
|
{
|
|
TargetEntry *res = (TargetEntry *)lfirst(lc);
|
|
Var *var = (Var *)res->expr;
|
|
Assert(var->varoattno > 0);
|
|
if (var->varno == OUTER_VAR) {
|
|
m_attrno[cur_resno - 1] = var->varoattno;
|
|
} else {
|
|
Assert(var->varno == INNER_VAR);
|
|
m_attrno[cur_resno - 1] = var->varoattno + lattnum;
|
|
}
|
|
cur_resno++;
|
|
}
|
|
|
|
initParams(params);
|
|
m_receiver = NULL;
|
|
m_isInsideRec = true;
|
|
m_lscan = ScanFusion::getScanFusion ((Node*)nlplan->join.plan.lefttree, m_planstmt, m_outParams);
|
|
m_rscan = RScanFusion::getScanFusion((Node*)nlplan->join.plan.righttree, m_planstmt, m_outParams);
|
|
m_reslot = NULL;
|
|
|
|
MemoryContextSwitchTo(oldContext);
|
|
}
|
|
|
|
bool NestLoopFusion::execute(long max_rows, char *completionTag)
|
|
{
|
|
bool success = false;
|
|
|
|
NestLoop *nlplan = (NestLoop*)m_planstmt->planTree;
|
|
int attnum = list_length(m_planstmt->planTree->targetlist);
|
|
int lattnum = list_length(nlplan->join.plan.lefttree->targetlist);
|
|
|
|
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_lscan->refreshParameter(t_thrd.opfusion_cxt.m_params);
|
|
m_lscan->Init(max_rows);
|
|
|
|
setReceiver();
|
|
|
|
long nprocessed1 = 0;
|
|
long nprocessed2 = 0;
|
|
TupleTableSlot* lslot = NULL;
|
|
TupleTableSlot* rslot = NULL;
|
|
while ((lslot = m_lscan->getTupleSlot()) != NULL) {
|
|
|
|
m_rscan->refreshParameter(t_thrd.opfusion_cxt.m_params);
|
|
m_rscan->Init(max_rows);
|
|
|
|
/* fetch tuple from inner relation */
|
|
while ((rslot = m_rscan->getTupleSlot()) != NULL) {
|
|
CHECK_FOR_INTERRUPTS();
|
|
nprocessed2++;
|
|
|
|
/* make nestloop targetlist */
|
|
for (int i = 0; i < attnum; i++) {
|
|
int16 attno = m_attrno[i];
|
|
if (attno <= lattnum) {
|
|
values[i] = lslot->tts_values[attno - 1];
|
|
isnull[i] = lslot->tts_isnull[attno - 1];
|
|
} else {
|
|
values[i] = rslot->tts_values[attno - lattnum - 1];
|
|
isnull[i] = rslot->tts_isnull[attno - lattnum - 1];
|
|
}
|
|
}
|
|
|
|
HeapTuple tmptup = heap_form_tuple(m_tupDesc, values, isnull);
|
|
(void)ExecStoreTuple(tmptup, reslot, InvalidBuffer, false);
|
|
heap_freetuple_ext(tmptup);
|
|
(*t_thrd.opfusion_cxt.m_receiver->receiveSlot)(reslot, t_thrd.opfusion_cxt.m_receiver);
|
|
if (nprocessed2 >= max_rows)
|
|
break;
|
|
}
|
|
m_rscan->End();
|
|
|
|
nprocessed1++;
|
|
if (nprocessed1 >= max_rows || nprocessed2 >= max_rows)
|
|
break;
|
|
}
|
|
m_lscan->End();
|
|
|
|
success = true;
|
|
|
|
/* step 3: done */
|
|
if (t_thrd.opfusion_cxt.m_isInsideRec == true) {
|
|
(*t_thrd.opfusion_cxt.m_receiver->rDestroy)(t_thrd.opfusion_cxt.m_receiver);
|
|
}
|
|
|
|
return success;
|
|
}
|
|
#endif
|