From adf53e20bba965a26861e42fc50c40cf02dbb01b Mon Sep 17 00:00:00 2001 From: Laishenghao Date: Mon, 14 Nov 2022 15:12:08 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E5=A2=9E=E5=8A=A0insert=E8=AF=AD=E5=8F=A5?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E5=8F=B3=E5=80=BC=E5=AD=97=E6=AE=B5=E5=BC=95?= =?UTF-8?q?=E7=94=A8=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/backend/parser/analyze.cpp | 98 ++++++++++- src/common/backend/parser/parse_clause.cpp | 2 + src/common/backend/parser/parse_expr.cpp | 46 ++++++ src/gausskernel/process/tcop/postgres.cpp | 29 +++- src/gausskernel/runtime/executor/execQual.cpp | 64 ++++++-- .../runtime/executor/execUtils.cpp | 122 +++++++++++++- .../runtime/executor/nodeModifyTable.cpp | 8 +- .../runtime/executor/nodeValuesscan.cpp | 21 +++ src/include/executor/executor.h | 3 + src/include/nodes/execnodes.h | 7 + src/include/nodes/parsenodes_common.h | 34 ++++ src/include/nodes/plannodes.h | 3 + src/include/parser/parse_node.h | 2 + .../regress/expected/insert_right_ref.out | 152 ++++++++++++++++++ src/test/regress/parallel_schedule0 | 2 +- src/test/regress/sql/insert_right_ref.sql | 70 ++++++++ 16 files changed, 643 insertions(+), 20 deletions(-) create mode 100644 src/test/regress/expected/insert_right_ref.out create mode 100644 src/test/regress/sql/insert_right_ref.sql diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index 8c9503e2a..827e60f38 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -183,6 +183,7 @@ Query* parse_analyze( } pfree_ext(pstate->p_ref_hook_state); + pstate->rightRefState = nullptr; free_parsestate(pstate); /* For plpy CTAS query. CTAS is a recursive call. CREATE query is the first rewrited. @@ -496,6 +497,10 @@ Query* transformStmt(ParseState* pstate, Node* parseTree, bool isFirstNode, bool /* Mark whether synonym object is in rtables or not. */ result->hasSynonyms = pstate->p_hasSynonyms; + if (nodeTag(parseTree) != T_InsertStmt) { + result->rightRefState = nullptr; + } + return result; } @@ -1527,6 +1532,74 @@ static void CheckUnsupportInsertSelectClause(Query* query) } } + +static void SetInsertAttrnoState(ParseState* pstate, List* attrnos) +{ + RightRefState* rstate = pstate->rightRefState; + Relation relation = (Relation)linitial(pstate->p_target_relation); + rstate->colCnt = RelationGetNumberOfAttributes(relation); + int len = list_length(attrnos); + rstate->explicitAttrLen = len; + rstate->explicitAttrNos = (int*)palloc0(sizeof(int) * len); + + ListCell* attr = list_head(attrnos); + for (int i = 0; i < len; ++i) { + rstate->explicitAttrNos[i] = lfirst_int(attr); + attr = lnext(attr); + } +} + +static void SetUpsertAttrnoState(ParseState* pstate, List *targetList) +{ + if (!targetList) { + return; + } + RightRefState* rstate = pstate->rightRefState; + int len = list_length(targetList); + rstate->usExplicitAttrLen = len; + rstate->usExplicitAttrNos = (int*)palloc0(sizeof(int) * len); + + Relation relation = (Relation)linitial(pstate->p_target_relation); + Form_pg_attribute* attr = relation->rd_att->attrs; + int colNum = RelationGetNumberOfAttributes(relation); + ListCell* target = list_head(targetList); + for (int ni = 0; ni < len; ++ni) { + ResTarget* res = (ResTarget*)lfirst(target); + const char* name = res->name; + for (int ci = 0; ci < colNum; ++ci) { + if (attr[ci]->attisdropped) { + continue; + } + if (strcmp(name, attr[ci]->attname.data) == 0) { + rstate->usExplicitAttrNos[ni] = ci + 1; + break; + } + } + + target = lnext(target); + } +} + +static RightRefState* MakeRightRefState() +{ + RightRefState* refState = (RightRefState*)palloc0(sizeof(RightRefState)); + refState->isSupported = !IsInitdb && DB_IS_CMPT(B_FORMAT); + refState->isInsertHasRightRef = false; + refState->explicitAttrLen = 0; + refState->explicitAttrNos = nullptr; + + refState->colCnt = 0; + refState->values = nullptr; + refState->isNulls = nullptr; + refState->hasExecs = nullptr; + + refState->isUpsert = false; + refState->isUpsertHasRightRef = false; + refState->usExplicitAttrLen = 0; + refState->usExplicitAttrNos = nullptr; + return refState; +} + /* * transformInsertStmt - * transform an Insert Statement @@ -1553,9 +1626,13 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt) /* There can't be any outer WITH to worry about */ AssertEreport(pstate->p_ctenamespace == NIL, MOD_OPT, "para should be NIL"); + RightRefState* rightRefState = MakeRightRefState(); + qry->commandType = CMD_INSERT; pstate->p_is_insert = true; pstate->p_has_ignore = stmt->hasIgnore; + pstate->rightRefState = rightRefState; + /* set io state for backend status for the thread, we will use it to check user space */ pgstat_set_io_state(IOSTATE_WRITE); @@ -1718,6 +1795,9 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt) /* Validate stmt->cols list, or build default list if no list given */ icolumns = checkInsertTargets(pstate, stmt->cols, &attrnos); + + SetInsertAttrnoState(pstate, attrnos); + AssertEreport(list_length(icolumns) == list_length(attrnos), MOD_OPT, "list length inconsistent"); /* @@ -2069,7 +2149,15 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt) /* Process DUPLICATE KEY UPDATE, if any. */ if (stmt->upsertClause) { - qry->upsertClause = transformUpsertClause(pstate, stmt->upsertClause); + if (IS_SUPPORT_RIGHT_REF(rightRefState)) { + pstate->p_varnamespace = NIL; + rightRefState->isUpsert = true; + SetUpsertAttrnoState(pstate, stmt->upsertClause->targetList); + qry->upsertClause = transformUpsertClause(pstate, stmt->upsertClause); + rightRefState->isUpsert = false; + } else { + qry->upsertClause = transformUpsertClause(pstate, stmt->upsertClause); + } } /* * If we have a RETURNING clause, we need to add the target relation to @@ -2118,6 +2206,14 @@ static Query* transformInsertStmt(ParseState* pstate, InsertStmt* stmt) qry->hintState = stmt->hintState; qry->hasIgnore = stmt->hasIgnore; + if (IS_ENABLE_RIGHT_REF(rightRefState)) { + qry->rightRefState = rightRefState; + } else { + qry->rightRefState = nullptr; + pstate->rightRefState = nullptr; + pfree(rightRefState); + } + return qry; } diff --git a/src/common/backend/parser/parse_clause.cpp b/src/common/backend/parser/parse_clause.cpp index 1d38c8196..eb56b4bbc 100644 --- a/src/common/backend/parser/parse_clause.cpp +++ b/src/common/backend/parser/parse_clause.cpp @@ -331,6 +331,8 @@ int setTargetTable(ParseState* pstate, RangeVar* relRv, bool inh, bool alsoSourc */ if (alsoSource) { addRTEtoQuery(pstate, rte, true, true, true); + } else if (IS_SUPPORT_RIGHT_REF(pstate->rightRefState)) { + addRTEtoQuery(pstate, rte, false, false, true); } } return index; diff --git a/src/common/backend/parser/parse_expr.cpp b/src/common/backend/parser/parse_expr.cpp index 9d6f087d2..30df35129 100644 --- a/src/common/backend/parser/parse_expr.cpp +++ b/src/common/backend/parser/parse_expr.cpp @@ -51,6 +51,7 @@ #include "utils/guc.h" #include "utils/guc_tables.h" +extern Node* build_column_default(Relation rel, int attrno, bool isInsertCmd = false, bool needOnUpdate = false); extern Node* makeAConst(Value* v, int location); extern Value* makeStringValue(char* str); static Node* transformParamRef(ParseState* pstate, ParamRef* pref); @@ -96,6 +97,44 @@ static Node* transformPrefixKey(ParseState* pstate, PrefixKey* pkey); #define OrientedIsCOLorPAX(rte) ((rte)->orientation == REL_COL_ORIENTED || (rte)->orientation == REL_PAX_ORIENTED) #define INDEX_KEY_MAX_PREFIX_LENGTH (int)2676 + + +static inline bool IsAutoIncrementColumn(TupleDesc rdAtt, int attrNo) +{ + return rdAtt->constr && rdAtt->constr->cons_autoinc && rdAtt->constr->cons_autoinc->attnum == attrNo; +} + +static void AddDefaultExprNode(ParseState* pstate) +{ + RightRefState* refState = pstate->rightRefState; + if (refState->isInsertHasRightRef) { + return; + } + pstate->rightRefState->isInsertHasRightRef = true; + + Relation relation = (Relation)linitial(pstate->p_target_relation); + TupleDesc rdAtt = relation->rd_att; + int fieldCnt = rdAtt->natts; + refState->constValues = (Const**)palloc0(sizeof(Const) * fieldCnt); + + for (int i = 0; i < fieldCnt; ++i) { + Form_pg_attribute attTup = rdAtt->attrs[i]; + if (IsAutoIncrementColumn(rdAtt, i + 1)) { + refState->constValues[i] = makeConst(attTup->atttypid, -1, attTup->attcollation, + attTup->attlen, (Datum)0, false, attTup->attbyval); + } else if (ISGENERATEDCOL(rdAtt, i)) { + refState->constValues[i] = nullptr; + } else { + Node* expr = build_column_default(relation, i + 1, true); + if (expr && IsA(expr, Const)) { + refState->constValues[i] = (Const*)expr; + } else { + refState->constValues[i] = nullptr; + } + } + } +} + /* * transformExpr - * Analyze and transform expressions. Type checking and type casting is @@ -135,6 +174,13 @@ Node* transformExpr(ParseState* pstate, Node* expr) switch (nodeTag(expr)) { case T_ColumnRef: result = transformColumnRef(pstate, (ColumnRef*)expr); + if (IS_SUPPORT_RIGHT_REF(pstate->rightRefState)) { + if (pstate->rightRefState->isUpsert) { + pstate->rightRefState->isUpsertHasRightRef = true; + } else { + AddDefaultExprNode(pstate); + } + } break; case T_ParamRef: diff --git a/src/gausskernel/process/tcop/postgres.cpp b/src/gausskernel/process/tcop/postgres.cpp index 6f8be28b7..8b97d7e76 100755 --- a/src/gausskernel/process/tcop/postgres.cpp +++ b/src/gausskernel/process/tcop/postgres.cpp @@ -1324,6 +1324,14 @@ PlannedStmt* pg_plan_query(Query* querytree, int cursorOptions, ParamListInfo bo plan->multi_node_hint = multi_node_hint; + if (plan->planTree) { + if (IS_ENABLE_RIGHT_REF(querytree->rightRefState)) { + plan->planTree->rightRefState = querytree->rightRefState; + } else { + plan->planTree->rightRefState = nullptr; + } + } + return plan; } @@ -2217,6 +2225,25 @@ void exec_init_poolhandles(void) #endif } +static bool IsRightRefState(List* plantreeList) +{ + if (!plantreeList || !list_length(plantreeList)) { + return false; + } + + ListCell* cell = list_head(plantreeList); + if (!cell) { + return false; + } + Node* node = (Node*)lfirst(cell); + if (node && IsA(node, PlannedStmt)) { + PlannedStmt* stmt = (PlannedStmt*) node; + return stmt->planTree && IS_ENABLE_RIGHT_REF(stmt->planTree->rightRefState); + } + + return false; +} + /* * exec_simple_query * @@ -2708,7 +2735,7 @@ static void exec_simple_query(const char* query_string, MessageType messageType, SetForceXidFromGTM(true); #endif /* SQL bypass */ - if (runOpfusionCheck) { + if (runOpfusionCheck && !IsRightRefState(plantree_list)) { (void)MemoryContextSwitchTo(oldcontext); void* opFusionObj = OpFusion::FusionFactory( OpFusion::getFusionType(NULL, NULL, plantree_list), oldcontext, NULL, plantree_list, NULL); diff --git a/src/gausskernel/runtime/executor/execQual.cpp b/src/gausskernel/runtime/executor/execQual.cpp index 1a22a3486..c50cebada 100644 --- a/src/gausskernel/runtime/executor/execQual.cpp +++ b/src/gausskernel/runtime/executor/execQual.cpp @@ -621,13 +621,24 @@ static Datum ExecEvalScalarVar(ExprState* exprstate, ExprContext* econtext, bool break; } - Assert(slot != NULL); - attnum = variable->varattno; /* This was checked by ExecInitExpr */ Assert(attnum != InvalidAttrNumber); + RightRefState* refState = econtext->rightRefState; + int index = attnum - 1; + if (IS_ENABLE_INSERT_RIGHT_REF(refState) || + (IS_ENABLE_UPSERT_RIGHT_REF(refState) && refState->hasExecs[index] && index < refState->colCnt)) { + *isNull = refState->isNulls[index]; + return refState->values[index]; + } + + if (slot == nullptr) { + ereport(ERROR,(errcode(ERRCODE_INVALID_ATTRIBUTE), errmodule(MOD_EXECUTOR), + errmsg("attribute number %d does not exists.", attnum))); + } + /* * If it's a user attribute, check validity (bogus system attnums will be * caught inside table's getattr). What we have to check for here is the @@ -6291,7 +6302,6 @@ static bool ExecTargetList(List* targetlist, ExprContext* econtext, Datum* value ExprDoneCond* itemIsDone, ExprDoneCond* isDone) { MemoryContext oldContext; - ListCell* tl = NULL; bool haveDoneSets = false; /* @@ -6299,19 +6309,32 @@ static bool ExecTargetList(List* targetlist, ExprContext* econtext, Datum* value */ oldContext = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); + RightRefState* refState = econtext->rightRefState; + int targetCount = list_length(targetlist); + GenericExprState* targetArr[targetCount]; + + int colCnt = (IS_ENABLE_RIGHT_REF(refState) && refState->colCnt > 0) ? refState->colCnt : 1; + bool hasExecs[colCnt]; + + SortTargetListAsArray(refState, targetlist, targetArr); + + InitOutputValues(refState, targetArr, values, isnull, targetCount, hasExecs); + /* * evaluate all the expressions in the target list */ haveDoneSets = false; /* any exhausted set exprs in tlist? */ - foreach (tl, targetlist) { - GenericExprState* gstate = (GenericExprState*)lfirst(tl); + for (GenericExprState* gstate : targetArr) { TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr; AttrNumber resind = tle->resno - 1; ELOG_FIELD_NAME_START(tle->resname); values[resind] = ExecEvalExpr(gstate->arg, econtext, &isnull[resind], &itemIsDone[resind]); + if (IS_ENABLE_RIGHT_REF(refState) && resind < refState->colCnt) { + hasExecs[resind] = true; + } if (T_Var == nodeTag(tle->expr) && !isnull[resind]) { Var *var = (Var *)tle->expr; @@ -6393,8 +6416,7 @@ static bool ExecTargetList(List* targetlist, ExprContext* econtext, Datum* value * We have some done and some undone sets. Restart the done ones * so that we can deliver a tuple (if possible). */ - foreach (tl, targetlist) { - GenericExprState* gstate = (GenericExprState*)lfirst(tl); + for (GenericExprState* gstate : targetArr) { TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr; AttrNumber resind = tle->resno - 1; @@ -6420,8 +6442,7 @@ static bool ExecTargetList(List* targetlist, ExprContext* econtext, Datum* value * XXX is that still necessary? */ if (*isDone == ExprEndResult) { - foreach (tl, targetlist) { - GenericExprState* gstate = (GenericExprState*)lfirst(tl); + for (GenericExprState* gstate : targetArr) { TargetEntry* tle = (TargetEntry*)gstate->xprstate.expr; AttrNumber resind = tle->resno - 1; @@ -6436,6 +6457,13 @@ static bool ExecTargetList(List* targetlist, ExprContext* econtext, Datum* value } } + + if (IS_ENABLE_RIGHT_REF(econtext->rightRefState)) { + econtext->rightRefState->values = nullptr; + econtext->rightRefState->isNulls = nullptr; + econtext->rightRefState->hasExecs = nullptr; + } + /* Report success */ MemoryContextSwitchTo(oldContext); @@ -6492,7 +6520,7 @@ TupleTableSlot* ExecProject(ProjectionInfo* projInfo, ExprDoneCond* isDone) tableam_tslot_getsomeattrs(econtext->ecxt_outertuple, projInfo->pi_lastOuterVar); } - if (projInfo->pi_lastScanVar > 0) { + if (projInfo->pi_lastScanVar > 0 && econtext->ecxt_scantuple) { tableam_tslot_getsomeattrs(econtext->ecxt_scantuple, projInfo->pi_lastScanVar); } @@ -6501,7 +6529,7 @@ TupleTableSlot* ExecProject(ProjectionInfo* projInfo, ExprDoneCond* isDone) * slots ... a mite ugly, but fast ... */ int numSimpleVars = projInfo->pi_numSimpleVars; - if (numSimpleVars > 0) { + if (numSimpleVars > 0 && !IS_ENABLE_RIGHT_REF(projInfo->pi_exprContext->rightRefState)) { Datum* values = slot->tts_values; bool* isnull = slot->tts_isnull; int* varSlotOffsets = projInfo->pi_varSlotOffsets; @@ -6545,9 +6573,19 @@ TupleTableSlot* ExecProject(ProjectionInfo* projInfo, ExprDoneCond* isDone) * already marked empty. */ if (projInfo->pi_targetlist) { - if (!ExecTargetList( - projInfo->pi_targetlist, econtext, slot->tts_values, slot->tts_isnull, projInfo->pi_itemIsDone, isDone)) + if (IS_ENABLE_RIGHT_REF(econtext->rightRefState)) { + econtext->rightRefState->isUpsert = projInfo->isUpsertHasRightRef; + } + bool flag = !ExecTargetList(projInfo->pi_targetlist, econtext, slot->tts_values, + slot->tts_isnull, projInfo->pi_itemIsDone, isDone); + + if (econtext->rightRefState) { + econtext->rightRefState->isUpsert = false; + } + + if (flag) { return slot; /* no more result rows, return empty slot */ + } } /* diff --git a/src/gausskernel/runtime/executor/execUtils.cpp b/src/gausskernel/runtime/executor/execUtils.cpp index 82c2dfd24..d5b050e51 100644 --- a/src/gausskernel/runtime/executor/execUtils.cpp +++ b/src/gausskernel/runtime/executor/execUtils.cpp @@ -743,6 +743,30 @@ ProjectionInfo* ExecBuildVecProjectionInfo( return projInfo; } +ProjectionInfo* ExecBuildRightRefProjectionInfo(PlanState* planState, TupleDesc inputDesc) +{ + List* targetList = planState->targetlist; + ExprContext* econtext = planState->ps_ExprContext; + TupleTableSlot* slot = planState->ps_ResultTupleSlot; + + ProjectionInfo* projInfo = makeNode(ProjectionInfo); + int len = ExecTargetListLength(targetList); + + econtext->rightRefState = planState->plan->rightRefState; + projInfo->pi_exprContext = econtext; + projInfo->pi_slot = slot; + projInfo->pi_lastInnerVar = 0; + projInfo->pi_lastOuterVar = 0; + projInfo->pi_lastScanVar = 0; + + projInfo->pi_targetlist = targetList; + projInfo->pi_numSimpleVars = 0; + projInfo->pi_directMap = false; + projInfo->pi_itemIsDone = (ExprDoneCond*)palloc(len * sizeof(ExprDoneCond)); + + return projInfo; +} + /* ---------------- * ExecBuildProjectionInfo * @@ -782,6 +806,7 @@ ProjectionInfo* ExecBuildProjectionInfo( projInfo->pi_lastInnerVar = 0; projInfo->pi_lastOuterVar = 0; projInfo->pi_lastScanVar = 0; + projInfo->isUpsertHasRightRef = false; /* * We separate the target list elements into simple Var references and @@ -840,6 +865,11 @@ ProjectionInfo* ExecBuildProjectionInfo( break; } numSimpleVars++; + + if (econtext && IS_ENABLE_RIGHT_REF(econtext->rightRefState)) { + exprlist = lappend(exprlist, gstate); + get_last_attnums((Node*)variable, projInfo); + } } else { /* Not a simple variable, add it to generic targetlist */ exprlist = lappend(exprlist, gstate); @@ -917,8 +947,12 @@ static bool get_last_attnums(Node* node, ProjectionInfo* projInfo) */ void ExecAssignProjectionInfo(PlanState* planstate, TupleDesc inputDesc) { - planstate->ps_ProjInfo = ExecBuildProjectionInfo( - planstate->targetlist, planstate->ps_ExprContext, planstate->ps_ResultTupleSlot, inputDesc); + if (planstate->plan && IS_ENABLE_RIGHT_REF(planstate->plan->rightRefState)) { + planstate->ps_ProjInfo = ExecBuildRightRefProjectionInfo(planstate, inputDesc); + } else { + planstate->ps_ProjInfo = ExecBuildProjectionInfo(planstate->targetlist, planstate->ps_ExprContext, + planstate->ps_ResultTupleSlot, inputDesc); + } } /* ---------------- @@ -2691,4 +2725,88 @@ Tuple ReplaceTupleNullCol(TupleDesc tupleDesc, TupleTableSlot *slot) tableam_tops_free_tuple(oldTup); return newTup; +} + + +void InitOutputValues(RightRefState* refState, GenericExprState* targetArr[], + Datum* values, bool* isnull, int targetCount, bool* hasExecs) +{ + if (!IS_ENABLE_RIGHT_REF(refState)) { + return; + } + + refState->values = values; + refState->isNulls = isnull; + refState->hasExecs = hasExecs; + int colCnt = refState->colCnt; + for (int i = 0; i < colCnt; ++i) { + hasExecs[i] = false; + } + + if (IS_ENABLE_INSERT_RIGHT_REF(refState)) { + for (int i = 0; i < targetCount; ++i) { + Const* con = refState->constValues[i]; + if (con) { + values[i] = con->constvalue; + isnull[i] = con->constisnull; + } else { + values[i] = (Datum)0; + isnull[i] = true; + } + } + } +} + +void SortTargetListAsArray(RightRefState* refState, List* targetList, GenericExprState* targetArr[]) +{ + ListCell* lc = NULL; + if (IS_ENABLE_INSERT_RIGHT_REF(refState)) { + const int len = list_length(targetList); + GenericExprState* tempArr[len]; + int tempIndex = 0; + foreach(lc, targetList) { + tempArr[tempIndex++] = (GenericExprState*)lfirst(lc); + } + + for (int i = 0; i < refState->explicitAttrLen; ++i) { + int explIndex = refState->explicitAttrNos[i] - 1; + targetArr[i] = tempArr[explIndex]; + tempArr[explIndex] = nullptr; + } + + int defaultNodeOffset = refState->explicitAttrLen; + for (int i = 0; i < len; ++i) { + if (tempArr[i]) { + targetArr[defaultNodeOffset++] = tempArr[i]; + } + } + + Assert(defaultNodeOffset == len); + } else if (IS_ENABLE_UPSERT_RIGHT_REF(refState)) { + const int len = list_length(targetList); + GenericExprState* tempArr[len]; + int tempIndex = 0; + foreach(lc, targetList) { + tempArr[tempIndex++] = (GenericExprState*)lfirst(lc); + } + + for (int i = 0; i < refState->usExplicitAttrLen; ++i) { + int explIndex = refState->usExplicitAttrNos[i] - 1; + targetArr[i] = tempArr[explIndex]; + tempArr[explIndex] = nullptr; + } + + int defaultNodeOffset = refState->usExplicitAttrLen; + for (int i = 0; i < len; ++i) { + if (tempArr[i]) { + targetArr[defaultNodeOffset++] = tempArr[i]; + } + } + Assert(defaultNodeOffset == len); + } else { + int index = 0; + foreach(lc, targetList) { + targetArr[index++] = (GenericExprState*)lfirst(lc); + } + } } \ No newline at end of file diff --git a/src/gausskernel/runtime/executor/nodeModifyTable.cpp b/src/gausskernel/runtime/executor/nodeModifyTable.cpp index 8c4216711..a9b7e7db8 100644 --- a/src/gausskernel/runtime/executor/nodeModifyTable.cpp +++ b/src/gausskernel/runtime/executor/nodeModifyTable.cpp @@ -3832,7 +3832,8 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl i = 0; foreach (l, node->plans) { sub_plan = (Plan*)lfirst(l); - + sub_plan->rightRefState = node->plan.rightRefState; + /* * Verify result relation is a valid target for the current operation */ @@ -3977,6 +3978,7 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl /* Need an econtext too */ econtext = CreateExprContext(estate); mt_state->ps.ps_ExprContext = econtext; + ATTACH_RIGHT_REF_STATE(&(mt_state->ps)); /* * Build a projection for each result rel. @@ -4020,6 +4022,7 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl } econtext = mt_state->ps.ps_ExprContext; + ATTACH_RIGHT_REF_STATE(&(mt_state->ps)); /* initialize slot for the existing tuple */ upsertState->us_existing = @@ -4039,7 +4042,8 @@ ModifyTableState* ExecInitModifyTable(ModifyTable* node, EState* estate, int efl result_rel_info->ri_updateProj = ExecBuildProjectionInfo((List*)setexpr, econtext, upsertState->us_updateproj, result_rel_info->ri_RelationDesc->rd_att); - + result_rel_info->ri_updateProj->isUpsertHasRightRef = + IS_ENABLE_RIGHT_REF(econtext->rightRefState) && econtext->rightRefState->isUpsertHasRightRef; /* initialize expression state to evaluate update where clause if exists */ if (node->upsertWhere) { upsertState->us_updateWhere = (List*)ExecInitExpr((Expr*)node->upsertWhere, &mt_state->ps); diff --git a/src/gausskernel/runtime/executor/nodeValuesscan.cpp b/src/gausskernel/runtime/executor/nodeValuesscan.cpp index 044244fa9..6b25c686b 100644 --- a/src/gausskernel/runtime/executor/nodeValuesscan.cpp +++ b/src/gausskernel/runtime/executor/nodeValuesscan.cpp @@ -120,14 +120,34 @@ static TupleTableSlot* ValuesNext(ValuesScanState* node) values = slot->tts_values; is_null = slot->tts_isnull; + RightRefState* refState = econtext->rightRefState; + int targetCount = list_length(expr_state_list); + GenericExprState* targetArr[targetCount]; + + int colCnt = (IS_ENABLE_RIGHT_REF(refState) && refState->colCnt > 0) ? refState->colCnt : 1; + bool hasExecs[colCnt]; + + SortTargetListAsArray(refState, expr_state_list, targetArr); + + InitOutputValues(refState, targetArr, values, is_null, targetCount, hasExecs); + resind = 0; foreach (lc, expr_state_list) { ExprState* exprState = (ExprState*)lfirst(lc); values[resind] = ExecEvalExpr(exprState, econtext, &is_null[resind], NULL); + if (IS_ENABLE_RIGHT_REF(refState) && resind < refState->colCnt) { + hasExecs[resind] = true; + } resind++; } + if (IS_ENABLE_RIGHT_REF(econtext->rightRefState)) { + econtext->rightRefState->values = nullptr; + econtext->rightRefState->isNulls = nullptr; + econtext->rightRefState->hasExecs = nullptr; + } + MemoryContextSwitchTo(old_context); /* @@ -199,6 +219,7 @@ ValuesScanState* ExecInitValuesScan(ValuesScan* node, EState* estate, int eflags */ ExecAssignExprContext(estate, plan_state); scan_state->rowcontext = plan_state->ps_ExprContext; + ATTACH_RIGHT_REF_STATE(plan_state); ExecAssignExprContext(estate, plan_state); /* diff --git a/src/include/executor/executor.h b/src/include/executor/executor.h index 96c21083f..c4590210b 100644 --- a/src/include/executor/executor.h +++ b/src/include/executor/executor.h @@ -298,6 +298,9 @@ extern TupleDesc ExecTypeFromTL(List* targetList, bool hasoid, bool markdropped extern TupleDesc ExecCleanTypeFromTL(List* targetList, bool hasoid, TableAmType tam = TAM_HEAP); extern TupleDesc ExecTypeFromExprList(List* exprList, List* namesList, TableAmType tam = TAM_HEAP); extern void UpdateChangedParamSet(PlanState* node, Bitmapset* newchg); +extern void InitOutputValues(RightRefState* refState, GenericExprState* targetArr[], + Datum* values, bool* isnull, int targetCount, bool* hasExecs); +extern void SortTargetListAsArray(RightRefState* refState, List* targetList, GenericExprState* targetArr[]); typedef struct TupOutputState { TupleTableSlot* slot; diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index e572d4bea..5e56865e6 100755 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -40,6 +40,10 @@ namespace JitExec } #endif +#define ATTACH_RIGHT_REF_STATE(planstate) if ((planstate) && (planstate)->ps_ExprContext && (planstate)->plan) {\ + (planstate)->ps_ExprContext->rightRefState = (planstate)->plan->rightRefState; \ +} + /* struct for utility statement mem usage */ typedef struct UtilityDesc { double cost; /* cost of utility statement */ @@ -215,6 +219,8 @@ typedef struct ExprContext { bool* vec_fun_sel; // selection for vector set-result function. int current_row; bool can_ignore; // indicates whether ERROR can be ignored when type casting + + RightRefState* rightRefState; } ExprContext; /* @@ -326,6 +332,7 @@ typedef struct ProjectionInfo { VectorBatch* pi_batch; vectarget_func jitted_vectarget; /* LLVM function pointer to point to the codegened targetlist expr function */ VectorBatch* pi_setFuncBatch; + bool isUpsertHasRightRef; } ProjectionInfo; /* diff --git a/src/include/nodes/parsenodes_common.h b/src/include/nodes/parsenodes_common.h index 4e4ee18e6..f131dd617 100644 --- a/src/include/nodes/parsenodes_common.h +++ b/src/include/nodes/parsenodes_common.h @@ -1818,6 +1818,19 @@ typedef struct A_ArrayExpr { #define FRAMEOPTION_DEFAULTS (FRAMEOPTION_RANGE | FRAMEOPTION_START_UNBOUNDED_PRECEDING | FRAMEOPTION_END_CURRENT_ROW) + +#define IS_SUPPORT_RIGHT_REF(rightRefState) ((rightRefState) && (rightRefState)->isSupported) + +#define IS_ENABLE_INSERT_RIGHT_REF(rightRefState) (IS_SUPPORT_RIGHT_REF(rightRefState) && \ +(rightRefState)->isInsertHasRightRef && !((rightRefState)->isUpsert)) + +#define IS_ENABLE_UPSERT_RIGHT_REF(rightRefState) (IS_SUPPORT_RIGHT_REF(rightRefState) && \ +(rightRefState)->isUpsertHasRightRef && (rightRefState)->isUpsert) + +#define IS_ENABLE_RIGHT_REF(rightRefState) ((rightRefState) && (rightRefState)->isSupported && \ +((rightRefState)->isInsertHasRightRef || (rightRefState)->isUpsertHasRightRef)) + + /* * XMLSERIALIZE (in raw parse tree only) */ @@ -1865,6 +1878,25 @@ typedef enum TdTruncCastStatus { } TdTruncCastStatus; #define TRUNCAST_VERSION_NUM 92023 + +typedef struct RightRefState { + bool isSupported; + bool isInsertHasRightRef; + int explicitAttrLen; + int* explicitAttrNos; + Const** constValues; + + int colCnt; + Datum* values; + bool* hasExecs; + bool* isNulls; + + bool isUpsert; + bool isUpsertHasRightRef; + int usExplicitAttrLen; + int* usExplicitAttrNos; +} RightRefState; + /* **************************************************************************** * Query Tree * *************************************************************************** */ @@ -1991,6 +2023,8 @@ typedef struct Query { * an error that has no idea about $x when INSERT SELECT query is analyzed. */ int fixed_numParams; List* resultRelations; /* rtable index list of target relation for INSERT/UPDATE/DELETE/MERGE. */ + + RightRefState* rightRefState; } Query; /* ---------------------- diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 8fbe8201c..5ad1e08fb 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -26,6 +26,7 @@ #include "pgxc/nodemgr.h" #include "bulkload/dist_fdw.h" #include "utils/bloom_filter.h" +#include "parsenodes_common.h" #define MAX_SPECIAL_BUCKETMAP_NUM 20 #define BUCKETMAP_DEFAULT_INDEX_BIT (1 << 31) @@ -351,6 +352,8 @@ typedef struct Plan { /* used for ustore partial seq scan */ List* flatList = NULL; /* flattened targetlist representing columns in query */ + + RightRefState* rightRefState; } Plan; /* ---------------- diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h index 4beb68da1..d53085869 100644 --- a/src/include/parser/parse_node.h +++ b/src/include/parser/parse_node.h @@ -263,6 +263,8 @@ struct ParseState { * update statement, then assign to qry->resultRelations. */ List* p_updateRangeVars; /* For multiple-update, use relationClase to generate RangeVar list. */ + + RightRefState* rightRefState; }; /* An element of p_relnamespace or p_varnamespace */ diff --git a/src/test/regress/expected/insert_right_ref.out b/src/test/regress/expected/insert_right_ref.out new file mode 100644 index 000000000..644e14a90 --- /dev/null +++ b/src/test/regress/expected/insert_right_ref.out @@ -0,0 +1,152 @@ +create database rightref with dbcompatibility 'B'; +\c rightref +-- test fields order +create table test_order_t(n1 int default 100, n2 int default 100, s int); +insert into test_order_t values(1000, 1000, n1 + n2); +insert into test_order_t(s, n1, n2) values(n1 + n2, 300, 300); +select * from test_order_t; + n1 | n2 | s +------+------+------ + 1000 | 1000 | 2000 + 300 | 300 | 200 +(2 rows) + +drop table test_order_t; +-- test non-idempotent function +create table non_idempotent_t(c1 float, c2 float, c3 float); +insert into non_idempotent_t values(random(), c1, c1); +select c1 = c2 as f1, c1 = c3 as f2 from non_idempotent_t; + f1 | f2 +----+---- + t | t +(1 row) + +drop table non_idempotent_t; +-- test auto increment +create table auto_increment_t(n int, c1 int primary key auto_increment, c2 int, c3 int); +NOTICE: CREATE TABLE will create implicit sequence "auto_increment_t_c1_seq" for serial column "auto_increment_t.c1" +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "auto_increment_t_pkey" for table "auto_increment_t" +insert into auto_increment_t values(1, c1, c1, c1); +insert into auto_increment_t values(2, 0, c1, c1); +insert into auto_increment_t values(3, 0, c1, c1); +insert into auto_increment_t values(4, -1, c1, c1); +insert into auto_increment_t(n, c2, c3, c1) values(5, c1, c1, 1000); +insert into auto_increment_t values(5, c1, c1, c1); +select * from auto_increment_t order by n; + n | c1 | c2 | c3 +---+------+----+---- + 1 | 1 | 0 | 0 + 2 | 2 | 0 | 0 + 3 | 3 | 0 | 0 + 4 | -1 | -1 | -1 + 5 | 1000 | 0 | 0 + 5 | 1001 | 0 | 0 +(6 rows) + +drop table auto_increment_t; +-- test series +create table test_series_t(c1 int, c2 int, c3 int); +insert into test_series_t values(c2 + 10, generate_series(1, 10), c2 * 2); +select * from test_series_t; + c1 | c2 | c3 +----+----+---- + | 1 | 2 + | 2 | 4 + | 3 | 6 + | 4 | 8 + | 5 | 10 + | 6 | 12 + | 7 | 14 + | 8 | 16 + | 9 | 18 + | 10 | 20 +(10 rows) + +drop table test_series_t; +-- test upsert +-- 1 +create table upser(c1 int, c2 int, c3 int); +create unique index idx_upser_c1 on upser(c1); +insert into upser values (1, 10, 10), (2, 10, 10), (3, 10, 10), (4, 10, 10), (5, 10, 10), (6, 10, 10), (7, 10, 10), + (8, 10, 10), (9, 10, 10), (10, 10, 10); +insert into upser values (5, 100, 100), (6, 100, 100), (7, 100, 100), (8, 100, 100), (9, 100, 100), (10, 100, 100), + (11, 100, 100), (12, 100, 100), (13, 100, 100), (14, 100, 100), (15, 100, 100) + on duplicate key update c2 = 2000, c3 = 2000; +select * from upser order by c1; + c1 | c2 | c3 +----+------+------ + 1 | 10 | 10 + 2 | 10 | 10 + 3 | 10 | 10 + 4 | 10 | 10 + 5 | 2000 | 2000 + 6 | 2000 | 2000 + 7 | 2000 | 2000 + 8 | 2000 | 2000 + 9 | 2000 | 2000 + 10 | 2000 | 2000 + 11 | 100 | 100 + 12 | 100 | 100 + 13 | 100 | 100 + 14 | 100 | 100 + 15 | 100 | 100 +(15 rows) + +-- 2 +truncate upser; +insert into upser values (1, 10, 10), (2, 10, 10), (3, 10, 10), (4, 10, 10), (5, 10, 10), (6, 10, 10), (7, 10, 10), + (8, 10, 10), (9, 10, 10), (10, 10, 10); +insert into upser values (5, 100, 100), (6, 100, 100), (7, 100, 100), (8, 100, 100), (9, 100, 100), (10, 100, 100), + (11, 100, 100), (12, 100, 100), (13, 100, 100), (14, 100, 100), (15, 100, 100) + on duplicate key update c2 = c1 + c2, c3 = c2 + c3; +select * from upser order by c1; + c1 | c2 | c3 +----+-----+----- + 1 | 10 | 10 + 2 | 10 | 10 + 3 | 10 | 10 + 4 | 10 | 10 + 5 | 15 | 25 + 6 | 16 | 26 + 7 | 17 | 27 + 8 | 18 | 28 + 9 | 19 | 29 + 10 | 20 | 30 + 11 | 100 | 100 + 12 | 100 | 100 + 13 | 100 | 100 + 14 | 100 | 100 + 15 | 100 | 100 +(15 rows) + +-- 3 +truncate upser; +insert into upser values (1, 10, 10), (2, 10, 10), (3, 10, 10), (4, 10, 10), (5, 10, 10), (6, 10, 10), + (7, 10, 10), (8, 10, 10), (9, 10, 10), (10, 10, 10); +insert into upser values (5, c1 + 100, 100), (6, c1 + 100, 100), (7, c1 + 100, 100), (8, c1 + 100, 100), + (9, c1 + 100, 100), (10, c1 + 100, 100), (11, c1 + 100, 100), (12, c1 + 100, 100), + (13, c1 + 100, 100), (14, c1 + 100, 100), (15, c1 + 100, c1 + c2) + on duplicate key update c2 = c1 + c2, c3 = c2 + c3; +select * from upser order by c1; + c1 | c2 | c3 +----+-----+----- + 1 | 10 | 10 + 2 | 10 | 10 + 3 | 10 | 10 + 4 | 10 | 10 + 5 | 15 | 25 + 6 | 16 | 26 + 7 | 17 | 27 + 8 | 18 | 28 + 9 | 19 | 29 + 10 | 20 | 30 + 11 | 111 | 100 + 12 | 112 | 100 + 13 | 113 | 100 + 14 | 114 | 100 + 15 | 115 | 130 +(15 rows) + +drop table upser; +\c postgres +drop database rightref; diff --git a/src/test/regress/parallel_schedule0 b/src/test/regress/parallel_schedule0 index 626a21aeb..2df3bfb23 100644 --- a/src/test/regress/parallel_schedule0 +++ b/src/test/regress/parallel_schedule0 @@ -793,7 +793,7 @@ test: disable_vector_engine test: hybrid_row_column test: retry test: hw_replication_slots -test: insert +test: insert insert_right_ref test: copy2 temp test: truncate #test: temp_table diff --git a/src/test/regress/sql/insert_right_ref.sql b/src/test/regress/sql/insert_right_ref.sql new file mode 100644 index 000000000..d34c43682 --- /dev/null +++ b/src/test/regress/sql/insert_right_ref.sql @@ -0,0 +1,70 @@ +create database rightref with dbcompatibility 'B'; +\c rightref + +-- test fields order +create table test_order_t(n1 int default 100, n2 int default 100, s int); +insert into test_order_t values(1000, 1000, n1 + n2); +insert into test_order_t(s, n1, n2) values(n1 + n2, 300, 300); +select * from test_order_t; +drop table test_order_t; + +-- test non-idempotent function +create table non_idempotent_t(c1 float, c2 float, c3 float); +insert into non_idempotent_t values(random(), c1, c1); +select c1 = c2 as f1, c1 = c3 as f2 from non_idempotent_t; +drop table non_idempotent_t; + +-- test auto increment +create table auto_increment_t(n int, c1 int primary key auto_increment, c2 int, c3 int); +insert into auto_increment_t values(1, c1, c1, c1); +insert into auto_increment_t values(2, 0, c1, c1); +insert into auto_increment_t values(3, 0, c1, c1); +insert into auto_increment_t values(4, -1, c1, c1); +insert into auto_increment_t(n, c2, c3, c1) values(5, c1, c1, 1000); +insert into auto_increment_t values(5, c1, c1, c1); +select * from auto_increment_t order by n; +drop table auto_increment_t; + +-- test series +create table test_series_t(c1 int, c2 int, c3 int); +insert into test_series_t values(c2 + 10, generate_series(1, 10), c2 * 2); +select * from test_series_t; +drop table test_series_t; + +-- test upsert +-- 1 +create table upser(c1 int, c2 int, c3 int); +create unique index idx_upser_c1 on upser(c1); +insert into upser values (1, 10, 10), (2, 10, 10), (3, 10, 10), (4, 10, 10), (5, 10, 10), (6, 10, 10), (7, 10, 10), + (8, 10, 10), (9, 10, 10), (10, 10, 10); +insert into upser values (5, 100, 100), (6, 100, 100), (7, 100, 100), (8, 100, 100), (9, 100, 100), (10, 100, 100), + (11, 100, 100), (12, 100, 100), (13, 100, 100), (14, 100, 100), (15, 100, 100) + on duplicate key update c2 = 2000, c3 = 2000; +select * from upser order by c1; + +-- 2 +truncate upser; +insert into upser values (1, 10, 10), (2, 10, 10), (3, 10, 10), (4, 10, 10), (5, 10, 10), (6, 10, 10), (7, 10, 10), + (8, 10, 10), (9, 10, 10), (10, 10, 10); +insert into upser values (5, 100, 100), (6, 100, 100), (7, 100, 100), (8, 100, 100), (9, 100, 100), (10, 100, 100), + (11, 100, 100), (12, 100, 100), (13, 100, 100), (14, 100, 100), (15, 100, 100) + on duplicate key update c2 = c1 + c2, c3 = c2 + c3; +select * from upser order by c1; + +-- 3 +truncate upser; +insert into upser values (1, 10, 10), (2, 10, 10), (3, 10, 10), (4, 10, 10), (5, 10, 10), (6, 10, 10), + (7, 10, 10), (8, 10, 10), (9, 10, 10), (10, 10, 10); + +insert into upser values (5, c1 + 100, 100), (6, c1 + 100, 100), (7, c1 + 100, 100), (8, c1 + 100, 100), + (9, c1 + 100, 100), (10, c1 + 100, 100), (11, c1 + 100, 100), (12, c1 + 100, 100), + (13, c1 + 100, 100), (14, c1 + 100, 100), (15, c1 + 100, c1 + c2) + on duplicate key update c2 = c1 + c2, c3 = c2 + c3; + +select * from upser order by c1; + +drop table upser; + +\c postgres + +drop database rightref; From 7880abfa601f2c901a28659d82d469c4604af7d4 Mon Sep 17 00:00:00 2001 From: Laishenghao Date: Tue, 15 Nov 2022 15:29:58 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E6=A3=80=E8=A7=86=E6=84=8F=E8=A7=81?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=EF=BC=9A=E5=88=A0=E9=99=A4=E4=B8=8D=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E5=88=9D=E5=A7=8B=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/backend/parser/analyze.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/common/backend/parser/analyze.cpp b/src/common/backend/parser/analyze.cpp index 827e60f38..1d046df31 100644 --- a/src/common/backend/parser/analyze.cpp +++ b/src/common/backend/parser/analyze.cpp @@ -1584,19 +1584,6 @@ static RightRefState* MakeRightRefState() { RightRefState* refState = (RightRefState*)palloc0(sizeof(RightRefState)); refState->isSupported = !IsInitdb && DB_IS_CMPT(B_FORMAT); - refState->isInsertHasRightRef = false; - refState->explicitAttrLen = 0; - refState->explicitAttrNos = nullptr; - - refState->colCnt = 0; - refState->values = nullptr; - refState->isNulls = nullptr; - refState->hasExecs = nullptr; - - refState->isUpsert = false; - refState->isUpsertHasRightRef = false; - refState->usExplicitAttrLen = 0; - refState->usExplicitAttrNos = nullptr; return refState; }