2775 lines
93 KiB
C++
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(>_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;
|
|
}
|