diff --git a/src/backend/executor/execAmi.c b/src/backend/executor/execAmi.c index 129daac0a1b7..3caa699c801f 100644 --- a/src/backend/executor/execAmi.c +++ b/src/backend/executor/execAmi.c @@ -24,6 +24,7 @@ #include "executor/nodeDynamicBitmapIndexscan.h" #include "executor/nodeBitmapOr.h" #include "executor/nodeCtescan.h" +#include "executor/nodeDML.h" #include "executor/nodeForeignscan.h" #include "executor/nodeFunctionscan.h" #include "executor/nodeHash.h" @@ -619,6 +620,10 @@ ExecSquelchNode(PlanState *node) ExecSquelchModifyTable((ModifyTableState *) node); return; + case T_DMLState: + ExecSquelchDML((DMLState *) node); + return; + /* * Node types that need custom code to recurse. */ diff --git a/src/backend/executor/nodeDML.c b/src/backend/executor/nodeDML.c index af912775e12d..fb5f76b5c766 100644 --- a/src/backend/executor/nodeDML.c +++ b/src/backend/executor/nodeDML.c @@ -36,6 +36,26 @@ ExecDMLExplainEnd(PlanState *planstate, struct StringInfoData *buf) planstate->instrument->execmemused += DML_MEM; } +/* + * Edit input attr numbers of projection using attributes map + */ +void +RemapProjection(ProjectionInfo *projInfo, AttrMap *map) +{ + int *varNumbers = projInfo->pi_varNumbers; + int numSimpleVars = projInfo->pi_numSimpleVars; + + for (int i = 0; i < numSimpleVars;++i) + varNumbers[i] = attrMap(map, varNumbers[i]); + + if (projInfo->pi_lastInnerVar > 0) + projInfo->pi_lastInnerVar = attrMap(map, projInfo->pi_lastInnerVar); + if (projInfo->pi_lastOuterVar > 0) + projInfo->pi_lastOuterVar = attrMap(map, projInfo->pi_lastOuterVar); + if (projInfo->pi_lastScanVar > 0) + projInfo->pi_lastScanVar = attrMap(map, projInfo->pi_lastScanVar); +} + /* * Executes INSERT and DELETE DML operations. The * action is specified within the TupleTableSlot at @@ -45,158 +65,199 @@ ExecDMLExplainEnd(PlanState *planstate, struct StringInfoData *buf) TupleTableSlot* ExecDML(DMLState *node) { + for (;;) + { + PlanState *outerNode = outerPlanState(node); + DML *plannode = (DML *) node->ps.plan; - PlanState *outerNode = outerPlanState(node); - DML *plannode = (DML *) node->ps.plan; - - Assert(outerNode != NULL); + Assert(outerNode != NULL); - TupleTableSlot *slot = ExecProcNode(outerNode); + /* Temporary restore result tuple slot for use in next projection */ + TupleTableSlot *returningResultTuple = node->ps.ps_ResultTupleSlot; + node->ps.ps_ResultTupleSlot = node->resultTupleSlot; - if (TupIsNull(slot)) - { - return NULL; - } + /* Set target list for projection */ + List *returningTargetList = plannode->plan.targetlist; + plannode->plan.targetlist = plannode->targetListProj; - bool isnull = false; - int action = DatumGetUInt32(slot_getattr(slot, plannode->actionColIdx, &isnull)); - Assert(!isnull); + TupleTableSlot *slot = ExecProcNode(outerNode); + TupleTableSlot *resultSlot = NULL; - bool isUpdate = false; - if (node->ps.state->es_plannedstmt->commandType == CMD_UPDATE) - { - isUpdate = true; - } + if (TupIsNull(slot)) + { + return NULL; + } - Assert(action == DML_INSERT || action == DML_DELETE); + bool isnull = false; + int action = DatumGetUInt32(slot_getattr(slot, plannode->actionColIdx, &isnull)); + Assert(!isnull); + bool isUpdate = false; + if (node->ps.state->es_plannedstmt->commandType == CMD_UPDATE) + { + isUpdate = true; + } - /* - * Reset per-tuple memory context to free any expression evaluation - * storage allocated in the previous tuple cycle. - */ - ExprContext *econtext = node->ps.ps_ExprContext; - ResetExprContext(econtext); + Assert(action == DML_INSERT || action == DML_DELETE); - /* Prepare cleaned-up tuple by projecting it and filtering junk columns */ - econtext->ecxt_outertuple = slot; - TupleTableSlot *projectedSlot = ExecProject(node->ps.ps_ProjInfo, NULL); + /* + * Reset per-tuple memory context to free any expression evaluation + * storage allocated in the previous tuple cycle. + */ + ExprContext *econtext = node->ps.ps_ExprContext; + ResetExprContext(econtext); - /* remove 'junk' columns from tuple */ - node->cleanedUpSlot = ExecFilterJunk(node->junkfilter, projectedSlot); + /* Prepare cleaned-up tuple by projecting it and filtering junk columns */ + econtext->ecxt_outertuple = slot; + TupleTableSlot *projectedSlot = ExecProject(node->ps.ps_ProjInfo, NULL); - /* - * If we are modifying a leaf partition we have to ensure that partition - * selection operation will consider leaf partition's attributes as - * coherent with root partition's attribute numbers, because partition - * selection is performed using root's attribute numbers (all partition - * rules are based on the parent relation's tuple descriptor). In case - * when child partition has different attribute numbers from root's due to - * dropped columns, the partition selection may go wrong without extra - * validation. - */ - if (node->ps.state->es_result_partitions) - { - ResultRelInfo *relInfo = node->ps.state->es_result_relations; + /* remove 'junk' columns from tuple */ + node->cleanedUpSlot = ExecFilterJunk(node->junkfilter, projectedSlot); - /* - * The DML is done on a leaf partition. In order to reuse the map, - * it will be allocated at es_result_relations. - */ - if (RelationGetRelid(relInfo->ri_RelationDesc) != - node->ps.state->es_result_partitions->part->parrelid && - action != DML_DELETE) - makePartitionCheckMap(node->ps.state, relInfo); + /* restore returning result tuple and target list*/ + node->ps.ps_ResultTupleSlot = returningResultTuple; + plannode->plan.targetlist = returningTargetList; /* - * DML node always performs partition selection, and if we want to - * reuse the map built in makePartitionCheckMap, we are allowed to - * reassign es_result_relation_info, because ExecInsert, ExecDelete - * changes it with target partition anyway. Moreover, without - * inheritance plan (ORCA never builds such plans) the - * es_result_relations will contain the only relation. - */ - node->ps.state->es_result_relation_info = relInfo; - } - - if (DML_INSERT == action) - { - /* Respect any given tuple Oid when updating a tuple. */ - if (isUpdate && plannode->tupleoidColIdx != 0) + * If we are modifying a leaf partition we have to ensure that partition + * selection operation will consider leaf partition's attributes as + * coherent with root partition's attribute numbers, because partition + * selection is performed using root's attribute numbers (all partition + * rules are based on the parent relation's tuple descriptor). In case + * when child partition has different attribute numbers from root's due to + * dropped columns, the partition selection may go wrong without extra + * validation. + */ + if (node->ps.state->es_result_partitions) { - Oid oid; - HeapTuple htuple; - - isnull = false; - oid = slot_getattr(slot, plannode->tupleoidColIdx, &isnull); - htuple = ExecFetchSlotHeapTuple(node->cleanedUpSlot); - Assert(htuple == node->cleanedUpSlot->PRIVATE_tts_heaptuple); - HeapTupleSetOid(htuple, oid); + ResultRelInfo *relInfo = node->ps.state->es_result_relations; + + /* + * The DML is done on a leaf partition. In order to reuse the map, + * it will be allocated at es_result_relations. + */ + if (RelationGetRelid(relInfo->ri_RelationDesc) != + node->ps.state->es_result_partitions->part->parrelid && + action != DML_DELETE) + makePartitionCheckMap(node->ps.state, relInfo); + + /* + * DML node always performs partition selection, and if we want to + * reuse the map built in makePartitionCheckMap, we are allowed to + * reassign es_result_relation_info, because ExecInsert, ExecDelete + * changes it with target partition anyway. Moreover, without + * inheritance plan (ORCA never builds such plans) the + * es_result_relations will contain the only relation. + */ + node->ps.state->es_result_relation_info = relInfo; } - /* - * The plan origin is required since ExecInsert performs different - * actions depending on the type of plan (constraint enforcement and - * triggers.) - */ - ExecInsert(node->cleanedUpSlot, - NULL, - node->ps.state, - node->canSetTag, - PLANGEN_OPTIMIZER /* Plan origin */, - isUpdate, - InvalidOid); - } - else /* DML_DELETE */ - { - int32 segid = GpIdentity.segindex; - Datum ctid = slot_getattr(slot, plannode->ctidColIdx, &isnull); - Oid tableoid = InvalidOid; + if (DML_INSERT == action) + { + /* Respect any given tuple Oid when updating a tuple. */ + if (isUpdate && plannode->tupleoidColIdx != 0) + { + Oid oid; + HeapTuple htuple; + + isnull = false; + oid = slot_getattr(slot, plannode->tupleoidColIdx, &isnull); + htuple = ExecFetchSlotHeapTuple(node->cleanedUpSlot); + Assert(htuple == node->cleanedUpSlot->PRIVATE_tts_heaptuple); + HeapTupleSetOid(htuple, oid); + } + + /* + * The plan origin is required since ExecInsert performs different + * actions depending on the type of plan (constraint enforcement and + * triggers.) + */ + resultSlot = ExecInsert(node->cleanedUpSlot, + NULL, + node->ps.state, + node->canSetTag, + PLANGEN_OPTIMIZER /* Plan origin */, + isUpdate, + InvalidOid); + } + else /* DML_DELETE */ + { + int32 segid = GpIdentity.segindex; + Datum ctid = slot_getattr(slot, plannode->ctidColIdx, &isnull); + Oid tableoid = InvalidOid; - Assert(!isnull); + Assert(!isnull); - if (AttributeNumberIsValid(plannode->tableoidColIdx)) - { - Datum dtableoid = slot_getattr(slot, plannode->tableoidColIdx, &isnull); - tableoid = isnull ? InvalidOid : DatumGetObjectId(dtableoid); + if (AttributeNumberIsValid(plannode->tableoidColIdx)) + { + Datum dtableoid = slot_getattr(slot, plannode->tableoidColIdx, &isnull); + tableoid = isnull ? InvalidOid : DatumGetObjectId(dtableoid); + } + + /* + * If tableoid is valid, it means that we are executing UPDATE/DELETE + * on partitioned table (root partition). In order to avoid partition + * pruning in ExecDelete one can use tableoid to build target + * ResultRelInfo for the leaf partition. + */ + if (OidIsValid(tableoid) && node->ps.state->es_result_partitions) + { + ProjectionInfo *projRet = + node->ps.state->es_result_relation_info->ri_projectReturning; + + node->ps.state->es_result_relation_info = + targetid_get_partition(tableoid, node->ps.state, true); + ResultRelInfo *relInfo = node->ps.state->es_result_relation_info; + + if (projRet != NULL && relInfo->ri_projectReturning == NULL) + { + // make linked copy of returning projection with separate remapped input attr numbers + relInfo->ri_projectReturning = makeNode(ProjectionInfo); + + *relInfo->ri_projectReturning = *projRet; + relInfo->ri_projectReturning->pi_varNumbers = + (int *) palloc(projRet->pi_numSimpleVars * sizeof(int)); + memcpy(relInfo->ri_projectReturning->pi_varNumbers, projRet->pi_varNumbers, + projRet->pi_numSimpleVars * sizeof(int)); + + RemapProjection(relInfo->ri_projectReturning, relInfo->ri_partInsertMap); + } + } + + ItemPointer tupleid = (ItemPointer) DatumGetPointer(ctid); + ItemPointerData tuple_ctid = *tupleid; + tupleid = &tuple_ctid; + + if (AttributeNumberIsValid(node->segid_attno)) + { + segid = DatumGetInt32(slot_getattr(slot, node->segid_attno, &isnull)); + Assert(!isnull); + } + + /* Correct tuple count by ignoring deletes when splitting tuples. */ + resultSlot = ExecDelete(tupleid, + segid, + NULL, /* GPDB_91_MERGE_FIXME: oldTuple? */ + node->cleanedUpSlot, + NULL /* DestReceiver */, + node->ps.state, + isUpdate ? false : node->canSetTag, /* if "isUpdate", + ExecInsert() will be run after + ExecDelete() so canSetTag should be set + properly in ExecInsert(). */ + PLANGEN_OPTIMIZER /* Plan origin */, + isUpdate); } /* - * If tableoid is valid, it means that we are executing UPDATE/DELETE - * on partitioned table (root partition). In order to avoid partition - * pruning in ExecDelete one can use tableoid to build target - * ResultRelInfo for the leaf partition. + * If we got a RETURNING result, return it to caller. We'll continue + * the work on next call. */ - if (OidIsValid(tableoid) && node->ps.state->es_result_partitions) - node->ps.state->es_result_relation_info = - targetid_get_partition(tableoid, node->ps.state, true); - - ItemPointer tupleid = (ItemPointer) DatumGetPointer(ctid); - ItemPointerData tuple_ctid = *tupleid; - tupleid = &tuple_ctid; - - if (AttributeNumberIsValid(node->segid_attno)) + if (!TupIsNull(resultSlot)) { - segid = DatumGetInt32(slot_getattr(slot, node->segid_attno, &isnull)); - Assert(!isnull); + return resultSlot; } - - /* Correct tuple count by ignoring deletes when splitting tuples. */ - ExecDelete(tupleid, - segid, - NULL, /* GPDB_91_MERGE_FIXME: oldTuple? */ - node->cleanedUpSlot, - NULL /* DestReceiver */, - node->ps.state, - isUpdate ? false : node->canSetTag, /* if "isUpdate", - ExecInsert() will be run after - ExecDelete() so canSetTag should be set - properly in ExecInsert(). */ - PLANGEN_OPTIMIZER /* Plan origin */, - isUpdate); } - - return slot; } /** @@ -206,12 +267,13 @@ DMLState* ExecInitDML(DML *node, EState *estate, int eflags) { /* check for unsupported flags */ - Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK | EXEC_FLAG_REWIND))); + Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); DMLState *dmlstate = makeNode(DMLState); dmlstate->ps.plan = (Plan *)node; dmlstate->ps.state = estate; dmlstate->canSetTag = node->canSetTag; + dmlstate->resultTupleSlot = NULL; /* * Initialize es_result_relation_info, just like ModifyTable. * GPDB_90_MERGE_FIXME: do we need to consolidate the ModifyTable and DML @@ -222,6 +284,10 @@ ExecInitDML(DML *node, EState *estate, int eflags) CmdType operation = estate->es_plannedstmt->commandType; ResultRelInfo *resultRelInfo = estate->es_result_relation_info; + /* set target list with projection and save returning target list */ + List *returningTargetList = node->plan.targetlist; + node->plan.targetlist = node->targetListProj; + ExecInitResultTupleSlot(estate, &dmlstate->ps); dmlstate->ps.targetlist = (List *) @@ -337,6 +403,37 @@ ExecInitDML(DML *node, EState *estate, int eflags) } } + /* Sort node reads this slot before dml gets executed, fill it here or leave empty */ + dmlstate->resultTupleSlot = dmlstate->ps.ps_ResultTupleSlot; + + /* + * Initialize RETURNING projections if needed. + */ + if (returningTargetList) + { + TupleTableSlot *slot; + + /* Initialize result tuple slot and assign its rowtype */ + TupleDesc tupDesc = ExecTypeFromTL(returningTargetList, false); + + /* Set up a slot for the output of the RETURNING projection(s) */ + ExecInitResultTupleSlot(estate, &dmlstate->ps); + ExecAssignResultType(&dmlstate->ps, tupDesc); + slot = dmlstate->ps.ps_ResultTupleSlot; + + List *rliststate = (List *) ExecInitExpr((Expr *) returningTargetList, &dmlstate->ps); + resultRelInfo->ri_projectReturning = + ExecBuildProjectionInfo(rliststate, dmlstate->ps.ps_ExprContext, slot, + resultRelInfo->ri_RelationDesc->rd_att); + + /* Set up a tuple table slot for use for trigger output tuples */ + if (estate->es_trig_tuple_slot == NULL) + estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate); + } + + /* restore returning target list */ + node->plan.targetlist = returningTargetList; + return dmlstate; } @@ -348,9 +445,23 @@ ExecEndDML(DMLState *node) ReleaseTupleDesc(node->junkfilter->jf_cleanTupType); ExecFreeExprContext(&node->ps); - ExecClearTuple(node->ps.ps_ResultTupleSlot); + if (node->ps.ps_ResultTupleSlot != NULL) + ExecClearTuple(node->ps.ps_ResultTupleSlot); ExecClearTuple(node->cleanedUpSlot); ExecEndNode(outerPlanState(node)); EndPlanStateGpmonPkt(&node->ps); } + +void +ExecSquelchDML(DMLState *node) +{ + /* + * DML nodes must run to completion when asked to Squelch so + * that we don't risk losing modifications which should be performed + * regardless of any LIMIT's or other forms for projections which could + * end up causing a squelch to happen. + */ + while (ExecDML(node) != NULL); +} + /* EOF */ diff --git a/src/backend/executor/nodeMotion.c b/src/backend/executor/nodeMotion.c index 86de436ef3f6..e092a8b85543 100644 --- a/src/backend/executor/nodeMotion.c +++ b/src/backend/executor/nodeMotion.c @@ -279,7 +279,7 @@ execMotionSender(MotionState *node) /* need refactor */ if (node->isExplictGatherMotion) { - numsegments = motion->plan.flow->numsegments; + numsegments = motion->plan.flow != NULL ? motion->plan.flow->numsegments : node->numInputSegs; } diff --git a/src/backend/executor/nodeSplitUpdate.c b/src/backend/executor/nodeSplitUpdate.c index 32b89e7d19d3..ebc7012a7c17 100644 --- a/src/backend/executor/nodeSplitUpdate.c +++ b/src/backend/executor/nodeSplitUpdate.c @@ -173,7 +173,7 @@ SplitUpdateState* ExecInitSplitUpdate(SplitUpdate *node, EState *estate, int eflags) { /* Check for unsupported flags */ - Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK | EXEC_FLAG_REWIND))); + Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK))); bool has_oids; diff --git a/src/backend/gpopt/translate/CCTEListEntry.cpp b/src/backend/gpopt/translate/CCTEListEntry.cpp index f15e4284813f..dfa86ebce34b 100644 --- a/src/backend/gpopt/translate/CCTEListEntry.cpp +++ b/src/backend/gpopt/translate/CCTEListEntry.cpp @@ -45,9 +45,11 @@ CCTEListEntry::CCTEListEntry(CMemoryPool *mp, ULONG query_level, #ifdef GPOS_DEBUG BOOL result = #endif - m_cte_info->Insert( - cte->ctename, - GPOS_NEW(mp) SCTEProducerInfo(cte_producer, cte_query->targetList)); + m_cte_info->Insert(cte->ctename, + GPOS_NEW(mp) SCTEProducerInfo( + cte_producer, cte_query->returningList != NULL + ? cte_query->returningList + : cte_query->targetList)); GPOS_ASSERT(result); } diff --git a/src/backend/gpopt/translate/CTranslatorDXLToPlStmt.cpp b/src/backend/gpopt/translate/CTranslatorDXLToPlStmt.cpp index 0bd321cc028e..ae1f51404d51 100644 --- a/src/backend/gpopt/translate/CTranslatorDXLToPlStmt.cpp +++ b/src/backend/gpopt/translate/CTranslatorDXLToPlStmt.cpp @@ -80,6 +80,8 @@ CTranslatorDXLToPlStmt::CTranslatorDXLToPlStmt( m_md_accessor(md_accessor), m_dxl_to_plstmt_context(dxl_to_plstmt_context), m_cmd_type(CMD_SELECT), + m_has_returning(false), + m_returning_dml_on_replicated(false), m_is_tgt_tbl_distributed(false), m_result_rel_list(NULL), m_num_of_segments(num_of_segments), @@ -263,6 +265,7 @@ CTranslatorDXLToPlStmt::GetPlannedStmtFromDXL(const CDXLNode *dxlnode, m_dxl_to_plstmt_context->GetCurrentMotionId() - 1; planned_stmt->commandType = m_cmd_type; + planned_stmt->hasReturning = m_has_returning; GPOS_ASSERT(plan->nMotionNodes >= 0); if (0 == plan->nMotionNodes && !m_is_tgt_tbl_distributed) @@ -2282,6 +2285,14 @@ CTranslatorDXLToPlStmt::TranslateDXLMotion( flow->flotype = FLOW_UNDEFINED; } + if (m_returning_dml_on_replicated && input_segids_array->Size() == 1) + { + // we set locus type in child node in this case + // to filter doubling output on executor side + flow->locustype = CdbLocusType_Replicated; + } + m_returning_dml_on_replicated = false; + child_plan->flow = flow; motion->motionID = m_dxl_to_plstmt_context->GetNextMotionId(); @@ -4170,8 +4181,9 @@ CTranslatorDXLToPlStmt::TranslateDXLDml( rte->requiredPerms |= acl_mode; m_dxl_to_plstmt_context->AddRTE(rte); - CDXLNode *project_list_dxlnode = (*dml_dxlnode)[0]; - CDXLNode *child_dxlnode = (*dml_dxlnode)[1]; + CDXLNode *project_list_output_dxlnode = (*dml_dxlnode)[0]; + CDXLNode *project_list_dxlnode = (*dml_dxlnode)[1]; + CDXLNode *child_dxlnode = (*dml_dxlnode)[2]; CDXLTranslateContext child_context(m_mp, false, output_context->GetColIdToParamIdMap()); @@ -4189,6 +4201,19 @@ CTranslatorDXLToPlStmt::TranslateDXLDml( NULL, // translate context for the base table child_contexts, output_context); + // set targetlist as list of target entries to be computed for parent nodes + plan->targetlist = + TranslateDXLProjList(project_list_output_dxlnode, &base_table_context, + child_contexts, output_context); + + m_has_returning = m_has_returning || plan->targetlist != NIL; + + if (plan->targetlist != NIL && + md_rel->GetRelDistribution() == IMDRelation::EreldistrReplicated) + { + m_returning_dml_on_replicated = true; + } + // Create target list with nulls if rel has dropped cols. DELETE may have // empty target list if there no after trigger present. Skip creating in // such case. @@ -4238,7 +4263,8 @@ CTranslatorDXLToPlStmt::TranslateDXLDml( GPOS_ASSERT(0 != dml->actionColIdx); - plan->targetlist = dml_target_list; + // save targetlist for projection of results from outer nodes + dml->targetListProj = dml_target_list; plan->lefttree = child_plan; plan->nMotionNodes = child_plan->nMotionNodes; diff --git a/src/backend/gpopt/translate/CTranslatorQueryToDXL.cpp b/src/backend/gpopt/translate/CTranslatorQueryToDXL.cpp index 0388d87a56a9..3f6028687668 100644 --- a/src/backend/gpopt/translate/CTranslatorQueryToDXL.cpp +++ b/src/backend/gpopt/translate/CTranslatorQueryToDXL.cpp @@ -492,11 +492,6 @@ CTranslatorQueryToDXL::TranslateSelectQueryToDXL() // We therefore need to check permissions before we go into optimization for all RTEs, including the ones not explicitly referred in the query, e.g. views. CTranslatorUtils::CheckRTEPermissions(m_query->rtable); - // RETURNING is not supported yet. - if (m_query->returningList) - GPOS_RAISE(gpdxl::ExmaDXL, gpdxl::ExmiQuery2DXLUnsupportedFeature, - GPOS_WSZ_LIT("RETURNING clause")); - CDXLNode *child_dxlnode = NULL; IntToUlongMap *sort_group_attno_to_colid_mapping = GPOS_NEW(m_mp) IntToUlongMap(m_mp); @@ -817,7 +812,12 @@ CTranslatorQueryToDXL::TranslateInsertQueryToDXL() query_dxlnode = project_dxlnode; } - return GPOS_NEW(m_mp) CDXLNode(m_mp, insert_dxlnode, query_dxlnode); + CDXLNode *log_insert_dxlnode = + GPOS_NEW(m_mp) CDXLNode(m_mp, insert_dxlnode, query_dxlnode); + + log_insert_dxlnode = ProcessReturningList(log_insert_dxlnode, table_descr); + + return log_insert_dxlnode; } //--------------------------------------------------------------------------- @@ -834,6 +834,16 @@ CTranslatorQueryToDXL::TranslateCTASToDXL() GPOS_ASSERT(CMD_SELECT == m_query->commandType); //GPOS_ASSERT(NULL != m_query->intoClause); + if (m_query->hasModifyingCTE) + { + // GPDB cannot have two writer segworker groups for one query. + // Furtherly, during execution stage an error will be thrown. + // However, showing the error early during translating stage would be more effective + GPOS_RAISE( + gpdxl::ExmaDXL, gpdxl::ExmiQuery2DXLUnsupportedFeature, + GPOS_WSZ_LIT("cannot create plan with several writing gangs")); + } + m_is_ctas_query = true; CDXLNode *query_dxlnode = TranslateSelectQueryToDXL(); @@ -1137,6 +1147,60 @@ CTranslatorQueryToDXL::GetSystemColId(INT attribute_number) return res; } +//--------------------------------------------------------------------------- +// @function: +// CTranslatorQueryToDXL::ProcessReturningList +// +// @doc: Wrap dxl node in logical project with return columns from returningList +// +//--------------------------------------------------------------------------- +CDXLNode * +CTranslatorQueryToDXL::ProcessReturningList(CDXLNode *dml_dxlnode, + CDXLTableDescr *table_descr) +{ + GPOS_ASSERT(dml_dxlnode != NULL); + + CRefCount::SafeRelease(m_dxl_query_output_cols); + + if (m_query->returningList != NULL) + { + IntToUlongMap *sort_group_attno_to_colid_mapping = + GPOS_NEW(m_mp) IntToUlongMap(m_mp); + IntToUlongMap *output_attno_to_colid_mapping = + GPOS_NEW(m_mp) IntToUlongMap(m_mp); + + // DML nodes output columns as in descriptor, temporary replace mapping + CMappingVarColId *var_to_colid_map_saved = m_var_to_colid_map; + m_var_to_colid_map = GPOS_NEW(m_mp) CMappingVarColId(m_mp); + m_var_to_colid_map->LoadTblColumns( + m_query_level, m_query->resultRelation, table_descr); + + dml_dxlnode = TranslateTargetListToDXLProject( + m_query->returningList, dml_dxlnode, + sort_group_attno_to_colid_mapping, output_attno_to_colid_mapping, + m_query->groupClause); + + // this array is filled earlier with target list and was used to get colids by their indices earlier + // now fill it with what will truly be returned + m_dxl_query_output_cols = CreateDXLOutputCols( + m_query->returningList, output_attno_to_colid_mapping); + + GPOS_DELETE(m_var_to_colid_map); + m_var_to_colid_map = var_to_colid_map_saved; + + output_attno_to_colid_mapping->Release(); + sort_group_attno_to_colid_mapping->Release(); + } + else + { + // we can safely set it to empty array here as we aren't outputting in this path + m_dxl_query_output_cols = GPOS_NEW(m_mp) CDXLNodeArray(m_mp); + } + + // return project_dxlnode; + return dml_dxlnode; +} + //--------------------------------------------------------------------------- // @function: // CTranslatorQueryToDXL::TranslateDeleteQueryToDXL @@ -1214,7 +1278,12 @@ CTranslatorQueryToDXL::TranslateDeleteQueryToDXL() CDXLLogicalDelete(m_mp, table_descr, ctid_colid, segid_colid, delete_colid_array, tableoid_colid); - return GPOS_NEW(m_mp) CDXLNode(m_mp, delete_dxlop, query_dxlnode); + CDXLNode *log_delete_dxlnode = + GPOS_NEW(m_mp) CDXLNode(m_mp, delete_dxlop, query_dxlnode); + + log_delete_dxlnode = ProcessReturningList(log_delete_dxlnode, table_descr); + + return log_delete_dxlnode; } //--------------------------------------------------------------------------- @@ -1328,7 +1397,12 @@ CTranslatorQueryToDXL::TranslateUpdateQueryToDXL() m_mp, table_descr, ctid_colid, segmentid_colid, delete_colid_array, insert_colid_array, has_oids, tuple_oid_colid, tableoid_colid); - return GPOS_NEW(m_mp) CDXLNode(m_mp, pdxlopupdate, query_dxlnode); + CDXLNode *log_update_dxlnode = + GPOS_NEW(m_mp) CDXLNode(m_mp, pdxlopupdate, query_dxlnode); + + log_update_dxlnode = ProcessReturningList(log_update_dxlnode, table_descr); + + return log_update_dxlnode; } //--------------------------------------------------------------------------- @@ -4606,7 +4680,7 @@ CTranslatorQueryToDXL::ConstructCTEProducerList(List *cte_list, // translate query representing the cte table to its DXL representation CDXLNode *cte_child_dxlnode = - query_to_dxl_translator.TranslateSelectQueryToDXL(); + query_to_dxl_translator.TranslateQueryToDXL(); // get the output columns of the cte table CDXLNodeArray *cte_query_output_colds_dxlnode_array = diff --git a/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-1.mdp b/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-1.mdp index 49090951fc8e..1f65b9c99246 100644 --- a/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-1.mdp +++ b/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-1.mdp @@ -207,10 +207,7 @@ - - - - + @@ -244,11 +241,12 @@ - + - + + @@ -259,15 +257,13 @@ - - - - - - - - - + + + + + + + @@ -281,8 +277,8 @@ - - + + @@ -298,7 +294,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-2.mdp b/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-2.mdp index 4412465aadb9..843c2cd47fbd 100644 --- a/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-2.mdp +++ b/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-2.mdp @@ -198,10 +198,7 @@ - - - - + @@ -251,11 +248,12 @@ - + - + + @@ -266,15 +264,13 @@ - - - - - - - - - + + + + + + + @@ -288,8 +284,8 @@ - - + + @@ -305,7 +301,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-3.mdp b/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-3.mdp index f6b3a2994cfa..a086c796e473 100644 --- a/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-3.mdp +++ b/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-3.mdp @@ -228,10 +228,7 @@ - - - - + @@ -281,11 +278,12 @@ - + - + + @@ -296,15 +294,13 @@ - - - - - - - - - + + + + + + + @@ -318,8 +314,8 @@ - - + + @@ -335,7 +331,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-4.mdp b/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-4.mdp index 1edb39f961e8..17d6e96ba7ea 100644 --- a/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-4.mdp +++ b/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-4.mdp @@ -245,10 +245,7 @@ - - - - + @@ -335,11 +332,12 @@ - + - + + @@ -350,15 +348,13 @@ - - - - - - - - - + + + + + + + @@ -372,8 +368,8 @@ - - + + @@ -389,7 +385,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-5.mdp b/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-5.mdp index dbf0477c35ad..54b89404c80a 100644 --- a/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-5.mdp +++ b/src/backend/gporca/data/dxl/minidump/AddRedistributeBeforeInsert-5.mdp @@ -214,10 +214,7 @@ - - - - + @@ -251,11 +248,12 @@ - + - + + @@ -266,15 +264,13 @@ - - - - - - - - - + + + + + + + @@ -288,8 +284,8 @@ - - + + @@ -305,7 +301,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CPhysicalParallelUnionAllTest/ParallelAppend-Insert.mdp b/src/backend/gporca/data/dxl/minidump/CPhysicalParallelUnionAllTest/ParallelAppend-Insert.mdp index 68e9e4d3b960..05297ab7ec54 100644 --- a/src/backend/gporca/data/dxl/minidump/CPhysicalParallelUnionAllTest/ParallelAppend-Insert.mdp +++ b/src/backend/gporca/data/dxl/minidump/CPhysicalParallelUnionAllTest/ParallelAppend-Insert.mdp @@ -142,9 +142,7 @@ INSERT INTO t VALUES (11),(12),(13); - - - + @@ -191,11 +189,12 @@ INSERT INTO t VALUES (11),(12),(13); - + - + + @@ -203,14 +202,14 @@ INSERT INTO t VALUES (11),(12),(13); - - - - - - - - + + + + + + + + @@ -221,7 +220,7 @@ INSERT INTO t VALUES (11),(12),(13); - + diff --git a/src/backend/gporca/data/dxl/minidump/CTAS-Random.mdp b/src/backend/gporca/data/dxl/minidump/CTAS-Random.mdp index 97c43a0ca69d..89311fe170bf 100644 --- a/src/backend/gporca/data/dxl/minidump/CTAS-Random.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTAS-Random.mdp @@ -213,8 +213,8 @@ - - + + @@ -236,7 +236,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTAS-With-Global-Local-Agg.mdp b/src/backend/gporca/data/dxl/minidump/CTAS-With-Global-Local-Agg.mdp index d1447b6788f2..fd0a0fc4cc87 100644 --- a/src/backend/gporca/data/dxl/minidump/CTAS-With-Global-Local-Agg.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTAS-With-Global-Local-Agg.mdp @@ -717,7 +717,7 @@ - + @@ -733,7 +733,7 @@ - + @@ -757,9 +757,9 @@ - + - + @@ -773,8 +773,8 @@ - - + + @@ -785,10 +785,10 @@ - - + + - + diff --git a/src/backend/gporca/data/dxl/minidump/CTAS-random-distr.mdp b/src/backend/gporca/data/dxl/minidump/CTAS-random-distr.mdp index 774f0fa5e24f..81505553e034 100644 --- a/src/backend/gporca/data/dxl/minidump/CTAS-random-distr.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTAS-random-distr.mdp @@ -178,9 +178,9 @@ - - - + + + @@ -208,8 +208,8 @@ - - + + @@ -228,7 +228,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTAS-random-distributed-from-replicated-distributed-table.mdp b/src/backend/gporca/data/dxl/minidump/CTAS-random-distributed-from-replicated-distributed-table.mdp index bbaf037be151..cb5dd26fd068 100644 --- a/src/backend/gporca/data/dxl/minidump/CTAS-random-distributed-from-replicated-distributed-table.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTAS-random-distributed-from-replicated-distributed-table.mdp @@ -243,8 +243,8 @@ - - + + @@ -266,8 +266,8 @@ - - + + @@ -283,7 +283,7 @@ - + @@ -316,7 +316,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTAS-with-Limit.mdp b/src/backend/gporca/data/dxl/minidump/CTAS-with-Limit.mdp index e1db851c6cdc..892b40a0e1b0 100644 --- a/src/backend/gporca/data/dxl/minidump/CTAS-with-Limit.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTAS-with-Limit.mdp @@ -656,8 +656,8 @@ - - + + @@ -679,7 +679,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTAS-with-hashed-distributed-external-table.mdp b/src/backend/gporca/data/dxl/minidump/CTAS-with-hashed-distributed-external-table.mdp index 4163127856d3..5278b5afe947 100644 --- a/src/backend/gporca/data/dxl/minidump/CTAS-with-hashed-distributed-external-table.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTAS-with-hashed-distributed-external-table.mdp @@ -247,9 +247,9 @@ EXPLAIN CREATE TABLE Test AS SELECT * FROM test_gpfdist_ext DISTRIBUTED BY (a); - - - + + + @@ -277,7 +277,7 @@ EXPLAIN CREATE TABLE Test AS SELECT * FROM test_gpfdist_ext DISTRIBUTED BY (a); - + diff --git a/src/backend/gporca/data/dxl/minidump/CTAS-with-randomly-distributed-external-table.mdp b/src/backend/gporca/data/dxl/minidump/CTAS-with-randomly-distributed-external-table.mdp index 50ea84fb45c8..6b856921ebcc 100644 --- a/src/backend/gporca/data/dxl/minidump/CTAS-with-randomly-distributed-external-table.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTAS-with-randomly-distributed-external-table.mdp @@ -234,9 +234,9 @@ EXPLAIN CREATE TABLE Test AS SELECT * FROM test_ext DISTRIBUTED RANDOMLY; - - - + + + @@ -264,8 +264,8 @@ EXPLAIN CREATE TABLE Test AS SELECT * FROM test_ext DISTRIBUTED RANDOMLY; - - + + @@ -284,7 +284,7 @@ EXPLAIN CREATE TABLE Test AS SELECT * FROM test_ext DISTRIBUTED RANDOMLY; - + diff --git a/src/backend/gporca/data/dxl/minidump/CTAS.mdp b/src/backend/gporca/data/dxl/minidump/CTAS.mdp index 2323355c74bb..00618b0ce76b 100644 --- a/src/backend/gporca/data/dxl/minidump/CTAS.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTAS.mdp @@ -223,8 +223,8 @@ - - + + @@ -246,7 +246,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/CTAS_OrderedAgg_multiple_cols.mdp b/src/backend/gporca/data/dxl/minidump/CTAS_OrderedAgg_multiple_cols.mdp index 7d05fe0c4deb..be5008e1ae8b 100644 --- a/src/backend/gporca/data/dxl/minidump/CTAS_OrderedAgg_multiple_cols.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTAS_OrderedAgg_multiple_cols.mdp @@ -1815,9 +1815,9 @@ EXPLAIN CREATE TABLE test AS - - - + + + @@ -1845,7 +1845,7 @@ EXPLAIN CREATE TABLE test AS - + @@ -1913,16 +1913,16 @@ EXPLAIN CREATE TABLE test AS - + - - + + - - + + @@ -1930,16 +1930,16 @@ EXPLAIN CREATE TABLE test AS - + - - + + - + - + @@ -1953,13 +1953,13 @@ EXPLAIN CREATE TABLE test AS - - + + - + @@ -1968,23 +1968,23 @@ EXPLAIN CREATE TABLE test AS - - + + - - - - - - - - - - + + + + + + + + + + @@ -2024,16 +2024,16 @@ EXPLAIN CREATE TABLE test AS - + - - + + - - + + @@ -2041,16 +2041,16 @@ EXPLAIN CREATE TABLE test AS - + - - + + - + - + @@ -2064,13 +2064,13 @@ EXPLAIN CREATE TABLE test AS - - + + - + @@ -2079,15 +2079,15 @@ EXPLAIN CREATE TABLE test AS - - + + - + @@ -2095,23 +2095,23 @@ EXPLAIN CREATE TABLE test AS - - + + - - - - - - - - - - + + + + + + + + + + @@ -2152,16 +2152,16 @@ EXPLAIN CREATE TABLE test AS - + - - + + - - + + @@ -2169,16 +2169,16 @@ EXPLAIN CREATE TABLE test AS - + - - + + - + - + @@ -2192,13 +2192,13 @@ EXPLAIN CREATE TABLE test AS - - + + - + @@ -2207,15 +2207,15 @@ EXPLAIN CREATE TABLE test AS - - + + - + @@ -2223,23 +2223,23 @@ EXPLAIN CREATE TABLE test AS - - + + - - - - - - - - - - + + + + + + + + + + @@ -2360,8 +2360,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2375,8 +2375,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2390,8 +2390,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2408,11 +2408,11 @@ EXPLAIN CREATE TABLE test AS - - + + - - + + @@ -2423,11 +2423,11 @@ EXPLAIN CREATE TABLE test AS - - + + - - + + @@ -2442,18 +2442,18 @@ EXPLAIN CREATE TABLE test AS - - + + - - + + - + @@ -2461,8 +2461,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2471,8 +2471,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2481,9 +2481,9 @@ EXPLAIN CREATE TABLE test AS - - - + + + @@ -2494,8 +2494,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2506,10 +2506,10 @@ EXPLAIN CREATE TABLE test AS - + - + @@ -2523,8 +2523,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2535,10 +2535,10 @@ EXPLAIN CREATE TABLE test AS - + - + @@ -2547,16 +2547,16 @@ EXPLAIN CREATE TABLE test AS - + - - + + - - + + @@ -2605,8 +2605,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2620,8 +2620,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2635,8 +2635,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2653,11 +2653,11 @@ EXPLAIN CREATE TABLE test AS - - + + - - + + @@ -2668,11 +2668,11 @@ EXPLAIN CREATE TABLE test AS - - + + - - + + @@ -2687,18 +2687,18 @@ EXPLAIN CREATE TABLE test AS - - + + - - + + - + @@ -2706,8 +2706,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2716,8 +2716,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2726,8 +2726,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2737,9 +2737,9 @@ EXPLAIN CREATE TABLE test AS - - - + + + @@ -2751,10 +2751,10 @@ EXPLAIN CREATE TABLE test AS - + - + @@ -2768,8 +2768,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2780,10 +2780,10 @@ EXPLAIN CREATE TABLE test AS - + - + @@ -2792,16 +2792,16 @@ EXPLAIN CREATE TABLE test AS - + - - + + - - + + @@ -2852,8 +2852,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2867,8 +2867,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2882,8 +2882,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2900,11 +2900,11 @@ EXPLAIN CREATE TABLE test AS - - + + - - + + @@ -2915,11 +2915,11 @@ EXPLAIN CREATE TABLE test AS - - + + - - + + @@ -2934,18 +2934,18 @@ EXPLAIN CREATE TABLE test AS - - + + - - + + - + @@ -2953,8 +2953,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2963,8 +2963,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2973,8 +2973,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -2984,9 +2984,9 @@ EXPLAIN CREATE TABLE test AS - - - + + + @@ -2998,10 +2998,10 @@ EXPLAIN CREATE TABLE test AS - + - + @@ -3015,8 +3015,8 @@ EXPLAIN CREATE TABLE test AS - - + + @@ -3027,10 +3027,10 @@ EXPLAIN CREATE TABLE test AS - + - + @@ -3039,16 +3039,16 @@ EXPLAIN CREATE TABLE test AS - + - - + + - - + + diff --git a/src/backend/gporca/data/dxl/minidump/CTE-Preds2.mdp b/src/backend/gporca/data/dxl/minidump/CTE-Preds2.mdp index 4ca0b999e5d2..5fd7b4e18c73 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE-Preds2.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE-Preds2.mdp @@ -537,7 +537,6 @@ - diff --git a/src/backend/gporca/data/dxl/minidump/CTE15HAReplicated.mdp b/src/backend/gporca/data/dxl/minidump/CTE15HAReplicated.mdp index 045b35906fc4..c3854d359d48 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE15HAReplicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE15HAReplicated.mdp @@ -660,7 +660,6 @@ - diff --git a/src/backend/gporca/data/dxl/minidump/CTE15Replicated.mdp b/src/backend/gporca/data/dxl/minidump/CTE15Replicated.mdp index de05b6cc2743..2bff029224f9 100644 --- a/src/backend/gporca/data/dxl/minidump/CTE15Replicated.mdp +++ b/src/backend/gporca/data/dxl/minidump/CTE15Replicated.mdp @@ -689,7 +689,6 @@ - diff --git a/src/backend/gporca/data/dxl/minidump/CannotPullGrpColAboveAgg.mdp b/src/backend/gporca/data/dxl/minidump/CannotPullGrpColAboveAgg.mdp index 244cd8480c86..cdc56a8ecdbe 100644 --- a/src/backend/gporca/data/dxl/minidump/CannotPullGrpColAboveAgg.mdp +++ b/src/backend/gporca/data/dxl/minidump/CannotPullGrpColAboveAgg.mdp @@ -4452,28 +4452,6 @@ The SQL along with the DDL are in TINC repo. In the aggregates directory under q - - - - - - - - - - - - - - - - - - - - - - @@ -4571,27 +4549,6 @@ The SQL along with the DDL are in TINC repo. In the aggregates directory under q - - - - - - - - - - - - - - - - - - - - - diff --git a/src/backend/gporca/data/dxl/minidump/ConvertHashToRandomInsert.mdp b/src/backend/gporca/data/dxl/minidump/ConvertHashToRandomInsert.mdp index 6b3fa10d1d77..77262d6ea606 100644 --- a/src/backend/gporca/data/dxl/minidump/ConvertHashToRandomInsert.mdp +++ b/src/backend/gporca/data/dxl/minidump/ConvertHashToRandomInsert.mdp @@ -597,10 +597,7 @@ explain analyze insert into t1 select t2.a,t3.b from t2, t3 where t2.a = t3.a; - - - - + @@ -643,11 +640,12 @@ explain analyze insert into t1 select t2.a,t3.b from t2, t3 where t2.a = t3.a; - + - + + @@ -658,11 +656,10 @@ explain analyze insert into t1 select t2.a,t3.b from t2, t3 where t2.a = t3.a; - - - - - + + + + @@ -676,7 +673,7 @@ explain analyze insert into t1 select t2.a,t3.b from t2, t3 where t2.a = t3.a; - + diff --git a/src/backend/gporca/data/dxl/minidump/DML-ComputeScalar-With-Outerref.mdp b/src/backend/gporca/data/dxl/minidump/DML-ComputeScalar-With-Outerref.mdp index 7fa3c0255346..007fd1db1896 100644 --- a/src/backend/gporca/data/dxl/minidump/DML-ComputeScalar-With-Outerref.mdp +++ b/src/backend/gporca/data/dxl/minidump/DML-ComputeScalar-With-Outerref.mdp @@ -236,9 +236,7 @@ - - - + @@ -319,11 +317,12 @@ - + - + + @@ -331,14 +330,14 @@ - - - - - - - - + + + + + + + + @@ -349,7 +348,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DML-Filter-With-OuterRef.mdp b/src/backend/gporca/data/dxl/minidump/DML-Filter-With-OuterRef.mdp index 7d25616af5ce..58dce1e53a92 100644 --- a/src/backend/gporca/data/dxl/minidump/DML-Filter-With-OuterRef.mdp +++ b/src/backend/gporca/data/dxl/minidump/DML-Filter-With-OuterRef.mdp @@ -261,9 +261,7 @@ - - - + @@ -340,11 +338,12 @@ - + - + + @@ -352,14 +351,14 @@ - - - - - - - - + + + + + + + + @@ -370,7 +369,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DML-Replicated-Input.mdp b/src/backend/gporca/data/dxl/minidump/DML-Replicated-Input.mdp index 9deff61aa45e..1b74d0f25f54 100644 --- a/src/backend/gporca/data/dxl/minidump/DML-Replicated-Input.mdp +++ b/src/backend/gporca/data/dxl/minidump/DML-Replicated-Input.mdp @@ -269,11 +269,7 @@ WHERE sach_vsnr = 465132477; - - - - - + @@ -339,15 +335,16 @@ WHERE sach_vsnr = 465132477; - + - + + @@ -361,16 +358,14 @@ WHERE sach_vsnr = 465132477; - - - - - - - - - - + + + + + + + + @@ -387,8 +382,8 @@ WHERE sach_vsnr = 465132477; - - + + @@ -410,11 +405,11 @@ WHERE sach_vsnr = 465132477; - + - + diff --git a/src/backend/gporca/data/dxl/minidump/DML-UnionAll-With-OuterRef.mdp b/src/backend/gporca/data/dxl/minidump/DML-UnionAll-With-OuterRef.mdp index 75b6255961de..cf6730e1e209 100644 --- a/src/backend/gporca/data/dxl/minidump/DML-UnionAll-With-OuterRef.mdp +++ b/src/backend/gporca/data/dxl/minidump/DML-UnionAll-With-OuterRef.mdp @@ -291,9 +291,7 @@ - - - + @@ -387,11 +385,12 @@ - + - + + @@ -399,14 +398,14 @@ - - - - - - - - + + + + + + + + @@ -417,7 +416,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DML-UnionAll-With-Universal-Child.mdp b/src/backend/gporca/data/dxl/minidump/DML-UnionAll-With-Universal-Child.mdp index 1575731dddc4..5cf7bf756773 100644 --- a/src/backend/gporca/data/dxl/minidump/DML-UnionAll-With-Universal-Child.mdp +++ b/src/backend/gporca/data/dxl/minidump/DML-UnionAll-With-Universal-Child.mdp @@ -209,9 +209,7 @@ - - - + @@ -270,11 +268,12 @@ - + - + + @@ -282,14 +281,14 @@ - - - - - - - - + + + + + + + + @@ -300,8 +299,8 @@ - - + + @@ -319,7 +318,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DML-Volatile-Function.mdp b/src/backend/gporca/data/dxl/minidump/DML-Volatile-Function.mdp index 150968b16a45..0ca5cc05ff86 100644 --- a/src/backend/gporca/data/dxl/minidump/DML-Volatile-Function.mdp +++ b/src/backend/gporca/data/dxl/minidump/DML-Volatile-Function.mdp @@ -201,9 +201,7 @@ - - - + @@ -249,11 +247,12 @@ - + - + + @@ -261,14 +260,14 @@ - - - - - - - - + + + + + + + + @@ -279,8 +278,8 @@ - - + + @@ -298,7 +297,7 @@ - + @@ -330,7 +329,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DML-With-CorrelatedNLJ-With-Universal-Child.mdp b/src/backend/gporca/data/dxl/minidump/DML-With-CorrelatedNLJ-With-Universal-Child.mdp index 2d217a035871..5ba8d79fd3ea 100644 --- a/src/backend/gporca/data/dxl/minidump/DML-With-CorrelatedNLJ-With-Universal-Child.mdp +++ b/src/backend/gporca/data/dxl/minidump/DML-With-CorrelatedNLJ-With-Universal-Child.mdp @@ -230,9 +230,7 @@ - - - + @@ -283,15 +281,16 @@ - + - + + @@ -299,14 +298,14 @@ - - - - - - - - + + + + + + + + @@ -317,8 +316,8 @@ - - + + @@ -336,7 +335,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DML-With-HJ-And-UniversalChild.mdp b/src/backend/gporca/data/dxl/minidump/DML-With-HJ-And-UniversalChild.mdp index 3ba8bf406547..d7e47246ce50 100644 --- a/src/backend/gporca/data/dxl/minidump/DML-With-HJ-And-UniversalChild.mdp +++ b/src/backend/gporca/data/dxl/minidump/DML-With-HJ-And-UniversalChild.mdp @@ -712,22 +712,23 @@ - + + - - - - - - - - + + + + + + + + @@ -741,7 +742,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DML-With-Join-With-Universal-Child.mdp b/src/backend/gporca/data/dxl/minidump/DML-With-Join-With-Universal-Child.mdp index e50b711dcf67..91fa9c36533a 100644 --- a/src/backend/gporca/data/dxl/minidump/DML-With-Join-With-Universal-Child.mdp +++ b/src/backend/gporca/data/dxl/minidump/DML-With-Join-With-Universal-Child.mdp @@ -185,9 +185,7 @@ - - - + @@ -238,11 +236,12 @@ - + - + + @@ -250,14 +249,14 @@ - - - - - - - - + + + + + + + + @@ -268,8 +267,8 @@ - - + + @@ -287,7 +286,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DML-With-MasterOnlyTable-1.mdp b/src/backend/gporca/data/dxl/minidump/DML-With-MasterOnlyTable-1.mdp index 72a1d843aa12..a9ea9b9892c1 100644 --- a/src/backend/gporca/data/dxl/minidump/DML-With-MasterOnlyTable-1.mdp +++ b/src/backend/gporca/data/dxl/minidump/DML-With-MasterOnlyTable-1.mdp @@ -367,22 +367,23 @@ - + + - - - - - - - - + + + + + + + + @@ -396,7 +397,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DML-With-WindowFunc-OuterRef.mdp b/src/backend/gporca/data/dxl/minidump/DML-With-WindowFunc-OuterRef.mdp index 9eb9b5249fd2..0867830e9e5c 100644 --- a/src/backend/gporca/data/dxl/minidump/DML-With-WindowFunc-OuterRef.mdp +++ b/src/backend/gporca/data/dxl/minidump/DML-With-WindowFunc-OuterRef.mdp @@ -367,7 +367,7 @@ - + @@ -383,7 +383,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DMLCollapseProject.mdp b/src/backend/gporca/data/dxl/minidump/DMLCollapseProject.mdp index 3e6e8eedd88e..fa0a8c3a5ee6 100644 --- a/src/backend/gporca/data/dxl/minidump/DMLCollapseProject.mdp +++ b/src/backend/gporca/data/dxl/minidump/DMLCollapseProject.mdp @@ -292,12 +292,7 @@ - - - - - - + @@ -364,11 +359,12 @@ - + - + + @@ -385,17 +381,14 @@ - - - - - - - - - - - + + + + + + + + @@ -415,8 +408,8 @@ - - + + @@ -443,13 +436,13 @@ - + - + @@ -486,7 +479,7 @@ - + @@ -496,7 +489,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Delete-With-Limit-In-Subquery.mdp b/src/backend/gporca/data/dxl/minidump/Delete-With-Limit-In-Subquery.mdp index 10d155f5c64a..5a69ae0e56f9 100644 --- a/src/backend/gporca/data/dxl/minidump/Delete-With-Limit-In-Subquery.mdp +++ b/src/backend/gporca/data/dxl/minidump/Delete-With-Limit-In-Subquery.mdp @@ -722,22 +722,23 @@ - + + - - - - - - - - + + + + + + + + @@ -751,7 +752,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DeleteInCTE.mdp b/src/backend/gporca/data/dxl/minidump/DeleteInCTE.mdp new file mode 100644 index 000000000000..e4f0ccde6e14 --- /dev/null +++ b/src/backend/gporca/data/dxl/minidump/DeleteInCTE.mdp @@ -0,0 +1,374 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/backend/gporca/data/dxl/minidump/DeleteMismatchedDistribution.mdp b/src/backend/gporca/data/dxl/minidump/DeleteMismatchedDistribution.mdp index 7981139961e2..3a8cede85a9d 100644 --- a/src/backend/gporca/data/dxl/minidump/DeleteMismatchedDistribution.mdp +++ b/src/backend/gporca/data/dxl/minidump/DeleteMismatchedDistribution.mdp @@ -338,24 +338,23 @@ - + + - - - - - - - - - - + + + + + + + + @@ -369,7 +368,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DeleteRandomDistr.mdp b/src/backend/gporca/data/dxl/minidump/DeleteRandomDistr.mdp index 3e0c0bb5176e..8e7954e657aa 100644 --- a/src/backend/gporca/data/dxl/minidump/DeleteRandomDistr.mdp +++ b/src/backend/gporca/data/dxl/minidump/DeleteRandomDistr.mdp @@ -203,23 +203,22 @@ - + + - - - - - - - - - + + + + + + + @@ -233,7 +232,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DeleteRandomlyDistributedTable.mdp b/src/backend/gporca/data/dxl/minidump/DeleteRandomlyDistributedTable.mdp index 7b1cbff5f6ba..5cca4ce4d5af 100644 --- a/src/backend/gporca/data/dxl/minidump/DeleteRandomlyDistributedTable.mdp +++ b/src/backend/gporca/data/dxl/minidump/DeleteRandomlyDistributedTable.mdp @@ -196,22 +196,22 @@ - + - + + - - - - - - - - + + + + + + + @@ -225,7 +225,7 @@ - + @@ -244,7 +244,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DeleteRandomlyDistributedTableJoin.mdp b/src/backend/gporca/data/dxl/minidump/DeleteRandomlyDistributedTableJoin.mdp index 6b11f1b80d69..ab26edfe3494 100644 --- a/src/backend/gporca/data/dxl/minidump/DeleteRandomlyDistributedTableJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/DeleteRandomlyDistributedTableJoin.mdp @@ -278,22 +278,22 @@ - + - + + - - - - - - - - + + + + + + + @@ -307,7 +307,7 @@ - + @@ -385,7 +385,7 @@ - + @@ -409,7 +409,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DeleteReturning.mdp b/src/backend/gporca/data/dxl/minidump/DeleteReturning.mdp new file mode 100644 index 000000000000..b6191cdec8d1 --- /dev/null +++ b/src/backend/gporca/data/dxl/minidump/DeleteReturning.mdp @@ -0,0 +1,356 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/backend/gporca/data/dxl/minidump/DeleteReturningProjection.mdp b/src/backend/gporca/data/dxl/minidump/DeleteReturningProjection.mdp new file mode 100644 index 000000000000..c400d72167e5 --- /dev/null +++ b/src/backend/gporca/data/dxl/minidump/DeleteReturningProjection.mdp @@ -0,0 +1,363 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/backend/gporca/data/dxl/minidump/DeleteWithAfterTriggerDroppedCol.mdp b/src/backend/gporca/data/dxl/minidump/DeleteWithAfterTriggerDroppedCol.mdp index fc5736e6dd0a..6cb830e68c75 100644 --- a/src/backend/gporca/data/dxl/minidump/DeleteWithAfterTriggerDroppedCol.mdp +++ b/src/backend/gporca/data/dxl/minidump/DeleteWithAfterTriggerDroppedCol.mdp @@ -251,7 +251,7 @@ drop function func_trigger(); - + @@ -261,16 +261,21 @@ drop function func_trigger(); + + + + + - - - - - - - - + + + + + + + + @@ -287,7 +292,7 @@ drop function func_trigger(); - + diff --git a/src/backend/gporca/data/dxl/minidump/DeleteWithTriggers.mdp b/src/backend/gporca/data/dxl/minidump/DeleteWithTriggers.mdp index 12c7c39db808..58488b4151ac 100644 --- a/src/backend/gporca/data/dxl/minidump/DeleteWithTriggers.mdp +++ b/src/backend/gporca/data/dxl/minidump/DeleteWithTriggers.mdp @@ -233,7 +233,7 @@ - + @@ -249,18 +249,27 @@ + + + + + + + + + + + - - - - - - - - - - + + + + + + + + @@ -283,8 +292,8 @@ - - + + @@ -307,7 +316,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/DontAddRedistributeBeforeInsert-1.mdp b/src/backend/gporca/data/dxl/minidump/DontAddRedistributeBeforeInsert-1.mdp index 5720e5ef715e..ef558d4f4c7b 100644 --- a/src/backend/gporca/data/dxl/minidump/DontAddRedistributeBeforeInsert-1.mdp +++ b/src/backend/gporca/data/dxl/minidump/DontAddRedistributeBeforeInsert-1.mdp @@ -265,10 +265,7 @@ Physical plan: - - - - + @@ -355,11 +352,12 @@ Physical plan: - + - + + @@ -370,15 +368,13 @@ Physical plan: - - - - - - - - - + + + + + + + @@ -392,7 +388,7 @@ Physical plan: - + diff --git a/src/backend/gporca/data/dxl/minidump/DontAddRedistributeBeforeInsert-2.mdp b/src/backend/gporca/data/dxl/minidump/DontAddRedistributeBeforeInsert-2.mdp index 92b167af0a71..dead8bc5f687 100644 --- a/src/backend/gporca/data/dxl/minidump/DontAddRedistributeBeforeInsert-2.mdp +++ b/src/backend/gporca/data/dxl/minidump/DontAddRedistributeBeforeInsert-2.mdp @@ -168,9 +168,7 @@ - - - + @@ -221,11 +219,12 @@ - + - + + @@ -233,14 +232,13 @@ - - - - - - - - + + + + + + + @@ -251,7 +249,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ExpandFullOuterJoin.mdp b/src/backend/gporca/data/dxl/minidump/ExpandFullOuterJoin.mdp index 669c876297da..61555de1f6c1 100644 --- a/src/backend/gporca/data/dxl/minidump/ExpandFullOuterJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/ExpandFullOuterJoin.mdp @@ -332,7 +332,6 @@ select * from t full join s on t1 = s1; - @@ -428,7 +427,6 @@ select * from t full join s on t1 = s1; - diff --git a/src/backend/gporca/data/dxl/minidump/IndexApply-Heterogeneous-DTS.mdp b/src/backend/gporca/data/dxl/minidump/IndexApply-Heterogeneous-DTS.mdp index 69929c639448..6723f7796710 100644 --- a/src/backend/gporca/data/dxl/minidump/IndexApply-Heterogeneous-DTS.mdp +++ b/src/backend/gporca/data/dxl/minidump/IndexApply-Heterogeneous-DTS.mdp @@ -628,8 +628,6 @@ WHERE tt.event_ts >= tq.ets AND - - diff --git a/src/backend/gporca/data/dxl/minidump/IndexApply-Heterogeneous-NoDTS.mdp b/src/backend/gporca/data/dxl/minidump/IndexApply-Heterogeneous-NoDTS.mdp index 7048d28b8497..56725a5297a8 100644 --- a/src/backend/gporca/data/dxl/minidump/IndexApply-Heterogeneous-NoDTS.mdp +++ b/src/backend/gporca/data/dxl/minidump/IndexApply-Heterogeneous-NoDTS.mdp @@ -721,8 +721,6 @@ ORDER BY 1 asc ; - - diff --git a/src/backend/gporca/data/dxl/minidump/IndexApply-InnerSelect-Heterogeneous-DTS.mdp b/src/backend/gporca/data/dxl/minidump/IndexApply-InnerSelect-Heterogeneous-DTS.mdp index 39a1300e9c27..e3e2627b6535 100644 --- a/src/backend/gporca/data/dxl/minidump/IndexApply-InnerSelect-Heterogeneous-DTS.mdp +++ b/src/backend/gporca/data/dxl/minidump/IndexApply-InnerSelect-Heterogeneous-DTS.mdp @@ -667,8 +667,6 @@ WHERE tt.event_ts >= tq.ets AND - - diff --git a/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExcept.mdp b/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExcept.mdp index 84b94940e4b7..dcc00ede0925 100644 --- a/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExcept.mdp +++ b/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExcept.mdp @@ -203,9 +203,7 @@ ON (a.col = b.col); - - - + @@ -296,11 +294,12 @@ ON (a.col = b.col); - + - + + @@ -308,14 +307,14 @@ ON (a.col = b.col); - - - - - - - - + + + + + + + + @@ -326,7 +325,7 @@ ON (a.col = b.col); - + diff --git a/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExceptAll.mdp b/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExceptAll.mdp index 5eca98beb654..2a010f80027a 100644 --- a/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExceptAll.mdp +++ b/src/backend/gporca/data/dxl/minidump/InnerJoinOverJoinExceptAll.mdp @@ -241,9 +241,7 @@ ON (a.col = b.col); - - - + @@ -334,11 +332,12 @@ ON (a.col = b.col); - + - + + @@ -346,14 +345,14 @@ ON (a.col = b.col); - - - - - - - - + + + + + + + + @@ -364,7 +363,7 @@ ON (a.col = b.col); - + @@ -430,8 +429,8 @@ ON (a.col = b.col); - - + + @@ -440,7 +439,7 @@ ON (a.col = b.col); - + @@ -539,7 +538,7 @@ ON (a.col = b.col); - + diff --git a/src/backend/gporca/data/dxl/minidump/Insert-Parquet-Partitioned-SortDisabled.mdp b/src/backend/gporca/data/dxl/minidump/Insert-Parquet-Partitioned-SortDisabled.mdp index f68421f2a3a1..efa3b4db8047 100644 --- a/src/backend/gporca/data/dxl/minidump/Insert-Parquet-Partitioned-SortDisabled.mdp +++ b/src/backend/gporca/data/dxl/minidump/Insert-Parquet-Partitioned-SortDisabled.mdp @@ -196,10 +196,7 @@ - - - - + @@ -229,11 +226,12 @@ - + - + + @@ -244,11 +242,10 @@ - - - - - + + + + @@ -262,7 +259,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Insert-Parquet.mdp b/src/backend/gporca/data/dxl/minidump/Insert-Parquet.mdp index 9007526f494c..f59e13d45ba8 100644 --- a/src/backend/gporca/data/dxl/minidump/Insert-Parquet.mdp +++ b/src/backend/gporca/data/dxl/minidump/Insert-Parquet.mdp @@ -177,10 +177,7 @@ - - - - + @@ -210,11 +207,12 @@ - + - + + @@ -225,11 +223,10 @@ - - - - - + + + + @@ -243,7 +240,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Insert-With-HJ-CTE-Agg.mdp b/src/backend/gporca/data/dxl/minidump/Insert-With-HJ-CTE-Agg.mdp index 42b2a550c565..a2388fe5232b 100644 --- a/src/backend/gporca/data/dxl/minidump/Insert-With-HJ-CTE-Agg.mdp +++ b/src/backend/gporca/data/dxl/minidump/Insert-With-HJ-CTE-Agg.mdp @@ -382,13 +382,7 @@ - - - - - - - + @@ -509,11 +503,12 @@ - + - + + @@ -533,18 +528,14 @@ - - - - - - - - - - - - + + + + + + + + @@ -559,7 +550,7 @@ - + @@ -573,7 +564,7 @@ - + @@ -663,7 +654,7 @@ - + @@ -756,7 +747,6 @@ - diff --git a/src/backend/gporca/data/dxl/minidump/Insert.mdp b/src/backend/gporca/data/dxl/minidump/Insert.mdp index 8fb25b77b944..f99e9ca05059 100644 --- a/src/backend/gporca/data/dxl/minidump/Insert.mdp +++ b/src/backend/gporca/data/dxl/minidump/Insert.mdp @@ -179,11 +179,12 @@ - + + @@ -194,15 +195,14 @@ - - - - - - - - - + + + + + + + + @@ -216,7 +216,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertAssertSort.mdp b/src/backend/gporca/data/dxl/minidump/InsertAssertSort.mdp index e93774f5ed43..c6d10bf5e445 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertAssertSort.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertAssertSort.mdp @@ -303,11 +303,7 @@ explain insert into tbl_cds_buysell_orders_new (a,b,c) - - - - - + @@ -366,11 +362,12 @@ explain insert into tbl_cds_buysell_orders_new (a,b,c) - + - + + @@ -396,16 +393,12 @@ explain insert into tbl_cds_buysell_orders_new (a,b,c) - - - - - - - - - - + + + + + + @@ -434,8 +427,8 @@ explain insert into tbl_cds_buysell_orders_new (a,b,c) - - + + @@ -477,8 +470,8 @@ explain insert into tbl_cds_buysell_orders_new (a,b,c) - - + + @@ -497,20 +490,20 @@ explain insert into tbl_cds_buysell_orders_new (a,b,c) - + - + - + - + - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertCheckConstraint.mdp b/src/backend/gporca/data/dxl/minidump/InsertCheckConstraint.mdp index 981881230aec..ec9ece7aae38 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertCheckConstraint.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertCheckConstraint.mdp @@ -280,7 +280,7 @@ - + @@ -289,6 +289,7 @@ + @@ -305,17 +306,14 @@ - - - - - - - - - - - + + + + + + + + @@ -335,8 +333,8 @@ - - + + @@ -363,8 +361,8 @@ - - + + @@ -404,8 +402,8 @@ - - + + @@ -422,7 +420,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertConstTuple.mdp b/src/backend/gporca/data/dxl/minidump/InsertConstTuple.mdp index a3482d548076..ee66d005c3bf 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertConstTuple.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertConstTuple.mdp @@ -193,7 +193,7 @@ - + @@ -202,6 +202,7 @@ + @@ -212,15 +213,14 @@ - - - - - - - - - + + + + + + + + @@ -234,7 +234,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertConstTupleRandomDistribution.mdp b/src/backend/gporca/data/dxl/minidump/InsertConstTupleRandomDistribution.mdp index 1832797fd3d9..6261027d8fb6 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertConstTupleRandomDistribution.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertConstTupleRandomDistribution.mdp @@ -169,10 +169,7 @@ - - - - + @@ -209,11 +206,12 @@ - + - + + @@ -224,15 +222,13 @@ - - - - - - - - - + + + + + + + @@ -246,8 +242,8 @@ - - + + @@ -263,7 +259,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertConstTupleVolatileFunction.mdp b/src/backend/gporca/data/dxl/minidump/InsertConstTupleVolatileFunction.mdp index 63d137b0f72a..5941e87d359b 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertConstTupleVolatileFunction.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertConstTupleVolatileFunction.mdp @@ -187,10 +187,7 @@ - - - - + @@ -231,11 +228,12 @@ - + - + + @@ -246,15 +244,14 @@ - - - - - - - - - + + + + + + + + @@ -268,8 +265,8 @@ - - + + @@ -290,8 +287,8 @@ - - + + @@ -308,12 +305,12 @@ - + - - + + diff --git a/src/backend/gporca/data/dxl/minidump/InsertConstTupleVolatileFunctionMOTable.mdp b/src/backend/gporca/data/dxl/minidump/InsertConstTupleVolatileFunctionMOTable.mdp index 0e8b62f5c330..859a37cad689 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertConstTupleVolatileFunctionMOTable.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertConstTupleVolatileFunctionMOTable.mdp @@ -177,10 +177,7 @@ - - - - + @@ -221,11 +218,12 @@ - + - + + @@ -236,15 +234,13 @@ - - - - - - - - - + + + + + + + @@ -258,8 +254,8 @@ - - + + @@ -276,15 +272,15 @@ - + - - + + diff --git a/src/backend/gporca/data/dxl/minidump/InsertDirectedDispatchNullValue.mdp b/src/backend/gporca/data/dxl/minidump/InsertDirectedDispatchNullValue.mdp index 10d1979d9519..83603077172a 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertDirectedDispatchNullValue.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertDirectedDispatchNullValue.mdp @@ -163,10 +163,7 @@ - - - - + @@ -211,15 +208,16 @@ - + - + + @@ -233,16 +231,14 @@ - - - - - - - - - - + + + + + + + + @@ -259,8 +255,8 @@ - - + + @@ -284,8 +280,8 @@ - - + + @@ -302,7 +298,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertInCTE.mdp b/src/backend/gporca/data/dxl/minidump/InsertInCTE.mdp new file mode 100644 index 000000000000..ad2b193dbb99 --- /dev/null +++ b/src/backend/gporca/data/dxl/minidump/InsertInCTE.mdp @@ -0,0 +1,581 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/backend/gporca/data/dxl/minidump/InsertIntoNonNullAfterDroppingColumn.mdp b/src/backend/gporca/data/dxl/minidump/InsertIntoNonNullAfterDroppingColumn.mdp index 44b2c3d09545..5d0d4b08f392 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertIntoNonNullAfterDroppingColumn.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertIntoNonNullAfterDroppingColumn.mdp @@ -148,9 +148,7 @@ EXPLAIN INSERT INTO ColumnMapping VALUES (NULL); - - - + @@ -182,16 +180,17 @@ EXPLAIN INSERT INTO ColumnMapping VALUES (NULL); - - + + - + - + + @@ -199,14 +198,14 @@ EXPLAIN INSERT INTO ColumnMapping VALUES (NULL); - - - - - - - - + + + + + + + + @@ -217,8 +216,8 @@ EXPLAIN INSERT INTO ColumnMapping VALUES (NULL); - - + + @@ -236,8 +235,8 @@ EXPLAIN INSERT INTO ColumnMapping VALUES (NULL); - - + + @@ -254,11 +253,11 @@ EXPLAIN INSERT INTO ColumnMapping VALUES (NULL); - + - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertIntoReturning.mdp b/src/backend/gporca/data/dxl/minidump/InsertIntoReturning.mdp new file mode 100644 index 000000000000..76f59a69ccf4 --- /dev/null +++ b/src/backend/gporca/data/dxl/minidump/InsertIntoReturning.mdp @@ -0,0 +1,299 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/backend/gporca/data/dxl/minidump/InsertIntoReturningProjection.mdp b/src/backend/gporca/data/dxl/minidump/InsertIntoReturningProjection.mdp new file mode 100644 index 000000000000..92b077e42f44 --- /dev/null +++ b/src/backend/gporca/data/dxl/minidump/InsertIntoReturningProjection.mdp @@ -0,0 +1,342 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/backend/gporca/data/dxl/minidump/InsertMasterOnlyTable.mdp b/src/backend/gporca/data/dxl/minidump/InsertMasterOnlyTable.mdp index 3f095c78d4dd..27eb4d2e9179 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertMasterOnlyTable.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertMasterOnlyTable.mdp @@ -197,10 +197,7 @@ - - - - + @@ -234,11 +231,12 @@ - + - + + @@ -249,15 +247,13 @@ - - - - - - - - - + + + + + + + @@ -271,7 +267,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertMasterOnlyTableConstTuple.mdp b/src/backend/gporca/data/dxl/minidump/InsertMasterOnlyTableConstTuple.mdp index 116f585d839c..f29e0d1c90d9 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertMasterOnlyTableConstTuple.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertMasterOnlyTableConstTuple.mdp @@ -135,10 +135,7 @@ - - - - + @@ -175,11 +172,12 @@ - + - + + @@ -190,15 +188,13 @@ - - - - - - - - - + + + + + + + @@ -212,7 +208,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertMismatchedDistrubution-2.mdp b/src/backend/gporca/data/dxl/minidump/InsertMismatchedDistrubution-2.mdp index 26b336933f48..28680674e7d5 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertMismatchedDistrubution-2.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertMismatchedDistrubution-2.mdp @@ -225,11 +225,7 @@ explain insert into pt2 select * from r; - - - - - + @@ -265,11 +261,12 @@ explain insert into pt2 select * from r; - + - + + @@ -283,16 +280,14 @@ explain insert into pt2 select * from r; - - - - - - - - - - + + + + + + + + @@ -309,7 +304,7 @@ explain insert into pt2 select * from r; - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertMismatchedDistrubution.mdp b/src/backend/gporca/data/dxl/minidump/InsertMismatchedDistrubution.mdp index c5e4b7a1884e..358a2adff453 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertMismatchedDistrubution.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertMismatchedDistrubution.mdp @@ -225,11 +225,7 @@ explain insert into pt2 select * from r; - - - - - + @@ -265,11 +261,12 @@ explain insert into pt2 select * from r; - + - + + @@ -283,16 +280,14 @@ explain insert into pt2 select * from r; - - - - - - - - - - + + + + + + + + @@ -309,7 +304,7 @@ explain insert into pt2 select * from r; - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertNULLNotNULLConstraint.mdp b/src/backend/gporca/data/dxl/minidump/InsertNULLNotNULLConstraint.mdp index 486cb39248be..4c9e0ee29ea4 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertNULLNotNULLConstraint.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertNULLNotNULLConstraint.mdp @@ -132,9 +132,7 @@ - - - + @@ -167,15 +165,16 @@ - + - + + @@ -183,14 +182,14 @@ - - - - - - - - + + + + + + + + @@ -201,8 +200,8 @@ - - + + @@ -220,8 +219,8 @@ - - + + @@ -238,7 +237,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertNoEnforceConstraints.mdp b/src/backend/gporca/data/dxl/minidump/InsertNoEnforceConstraints.mdp index 31b1fef69723..8448beb5926e 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertNoEnforceConstraints.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertNoEnforceConstraints.mdp @@ -167,10 +167,7 @@ insert into constraints_tab values (NULL, -1); - - - - + @@ -207,11 +204,12 @@ insert into constraints_tab values (NULL, -1); - + - + + @@ -222,15 +220,14 @@ insert into constraints_tab values (NULL, -1); - - - - - - - - - + + + + + + + + @@ -244,7 +241,7 @@ insert into constraints_tab values (NULL, -1); - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertNonSingleton.mdp b/src/backend/gporca/data/dxl/minidump/InsertNonSingleton.mdp index 6317e6d9ce13..4e2f5d9f023e 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertNonSingleton.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertNonSingleton.mdp @@ -241,9 +241,7 @@ EXPLAIN INSERT INTO snackbox SELECT a FROM (SELECT c FROM hottoast LIMIT 3) hott - - - + @@ -302,11 +300,12 @@ EXPLAIN INSERT INTO snackbox SELECT a FROM (SELECT c FROM hottoast LIMIT 3) hott - + - + + @@ -314,14 +313,13 @@ EXPLAIN INSERT INTO snackbox SELECT a FROM (SELECT c FROM hottoast LIMIT 3) hott - - - - - - - - + + + + + + + @@ -332,7 +330,7 @@ EXPLAIN INSERT INTO snackbox SELECT a FROM (SELECT c FROM hottoast LIMIT 3) hott - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertNotNullCols.mdp b/src/backend/gporca/data/dxl/minidump/InsertNotNullCols.mdp index c06d5da9a90b..0cd3dd708282 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertNotNullCols.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertNotNullCols.mdp @@ -213,11 +213,12 @@ - + + @@ -228,15 +229,14 @@ - - - - - - - - - + + + + + + + + @@ -250,8 +250,8 @@ - - + + @@ -268,7 +268,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertPrimaryKeyFromMOTable.mdp b/src/backend/gporca/data/dxl/minidump/InsertPrimaryKeyFromMOTable.mdp index b662c37bb44c..bf8d59806a88 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertPrimaryKeyFromMOTable.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertPrimaryKeyFromMOTable.mdp @@ -199,10 +199,7 @@ - - - - + @@ -236,11 +233,12 @@ - + - + + @@ -251,15 +249,14 @@ - - - - - - - - - + + + + + + + + @@ -273,8 +270,8 @@ - - + + @@ -295,8 +292,8 @@ - - + + @@ -313,7 +310,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertProjectSort.mdp b/src/backend/gporca/data/dxl/minidump/InsertProjectSort.mdp index 126aa598aec9..8db83ecbd83b 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertProjectSort.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertProjectSort.mdp @@ -231,11 +231,7 @@ explain insert into tbl_cds_buysell_orders_new (a,b,c) - - - - - + @@ -294,11 +290,12 @@ explain insert into tbl_cds_buysell_orders_new (a,b,c) - + - + + @@ -324,16 +321,12 @@ explain insert into tbl_cds_buysell_orders_new (a,b,c) - - - - - - - - - - + + + + + + @@ -351,18 +344,18 @@ explain insert into tbl_cds_buysell_orders_new (a,b,c) - + - + - + - + - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertRandomDistr.mdp b/src/backend/gporca/data/dxl/minidump/InsertRandomDistr.mdp index 77e8d43e42fd..91f1645a644c 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertRandomDistr.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertRandomDistr.mdp @@ -195,10 +195,7 @@ - - - - + @@ -232,11 +229,12 @@ - + - + + @@ -247,15 +245,13 @@ - - - - - - - - - + + + + + + + @@ -269,7 +265,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertReplicatedIntoSerialHashDistributedTable.mdp b/src/backend/gporca/data/dxl/minidump/InsertReplicatedIntoSerialHashDistributedTable.mdp index 6fc44f056968..81dd333ef59a 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertReplicatedIntoSerialHashDistributedTable.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertReplicatedIntoSerialHashDistributedTable.mdp @@ -272,11 +272,7 @@ EXPLAIN (COSTS OFF) INSERT INTO distributed_table (val1, val2) SELECT val1, val2 - - - - - + @@ -320,11 +316,12 @@ EXPLAIN (COSTS OFF) INSERT INTO distributed_table (val1, val2) SELECT val1, val2 - + - + + @@ -336,18 +333,16 @@ EXPLAIN (COSTS OFF) INSERT INTO distributed_table (val1, val2) SELECT val1, val2 - + - - - - - - - - - - + + + + + + + + @@ -364,7 +359,7 @@ EXPLAIN (COSTS OFF) INSERT INTO distributed_table (val1, val2) SELECT val1, val2 - + @@ -415,7 +410,7 @@ EXPLAIN (COSTS OFF) INSERT INTO distributed_table (val1, val2) SELECT val1, val2 - + @@ -441,7 +436,7 @@ EXPLAIN (COSTS OFF) INSERT INTO distributed_table (val1, val2) SELECT val1, val2 - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertSort.mdp b/src/backend/gporca/data/dxl/minidump/InsertSort.mdp index 69208c10a1a5..e3e3cbf5432d 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertSort.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertSort.mdp @@ -317,9 +317,7 @@ explain insert into nim1 select * from nim order by 1; - - - + @@ -358,11 +356,12 @@ explain insert into nim1 select * from nim order by 1; - + - + + @@ -370,14 +369,14 @@ explain insert into nim1 select * from nim order by 1; - - - - - - - - + + + + + + + + @@ -388,7 +387,7 @@ explain insert into nim1 select * from nim order by 1; - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertSortDistributed2MasterOnly.mdp b/src/backend/gporca/data/dxl/minidump/InsertSortDistributed2MasterOnly.mdp index 69ed58315668..b8bba0e041cf 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertSortDistributed2MasterOnly.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertSortDistributed2MasterOnly.mdp @@ -238,11 +238,7 @@ explain insert into masteronly select * from distributedtable order by 1; - - - - - + @@ -285,11 +281,12 @@ explain insert into masteronly select * from distributedtable order by 1; - + - + + @@ -303,16 +300,13 @@ explain insert into masteronly select * from distributedtable order by 1; - - - - - - - - - - + + + + + + + @@ -329,8 +323,8 @@ explain insert into masteronly select * from distributedtable order by 1; - - + + @@ -349,7 +343,7 @@ explain insert into masteronly select * from distributedtable order by 1; - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertWithDroppedCol.mdp b/src/backend/gporca/data/dxl/minidump/InsertWithDroppedCol.mdp index 0c7287f305cc..a158d691dbd0 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertWithDroppedCol.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertWithDroppedCol.mdp @@ -201,10 +201,7 @@ - - - - + @@ -241,15 +238,16 @@ - + - + + @@ -260,15 +258,14 @@ - - - - - - - - - + + + + + + + + @@ -282,8 +279,8 @@ - - + + @@ -304,8 +301,8 @@ - - + + @@ -324,7 +321,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/InsertWithTriggers.mdp b/src/backend/gporca/data/dxl/minidump/InsertWithTriggers.mdp index f4013ae61377..57387f2f2a09 100644 --- a/src/backend/gporca/data/dxl/minidump/InsertWithTriggers.mdp +++ b/src/backend/gporca/data/dxl/minidump/InsertWithTriggers.mdp @@ -266,7 +266,7 @@ - + @@ -286,18 +286,27 @@ + + + + + + + + + + + - - - - - - - - - - + + + + + + + + @@ -314,8 +323,8 @@ - - + + @@ -332,7 +341,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/LeftJoinNullsNotColocated.mdp b/src/backend/gporca/data/dxl/minidump/LeftJoinNullsNotColocated.mdp index 5b8f2c06474e..1a1c4a2e1088 100644 --- a/src/backend/gporca/data/dxl/minidump/LeftJoinNullsNotColocated.mdp +++ b/src/backend/gporca/data/dxl/minidump/LeftJoinNullsNotColocated.mdp @@ -427,9 +427,9 @@ on id2 = id3 distributed by (id1); - - - + + + @@ -457,7 +457,7 @@ on id2 = id3 distributed by (id1); - + diff --git a/src/backend/gporca/data/dxl/minidump/LeftOuter2InnerUnionAllAntiSemiJoin-Tpcds.mdp b/src/backend/gporca/data/dxl/minidump/LeftOuter2InnerUnionAllAntiSemiJoin-Tpcds.mdp index 9f7bf474084d..b1317acb6562 100644 --- a/src/backend/gporca/data/dxl/minidump/LeftOuter2InnerUnionAllAntiSemiJoin-Tpcds.mdp +++ b/src/backend/gporca/data/dxl/minidump/LeftOuter2InnerUnionAllAntiSemiJoin-Tpcds.mdp @@ -11731,29 +11731,8 @@ select * from v2 left outer join v1 on v2.i_item_sk = v1.ss_item_sk limit 5; - - - - - - - - - - - - - - - - - - - - - @@ -11786,34 +11765,6 @@ select * from v2 left outer join v1 on v2.i_item_sk = v1.ss_item_sk limit 5; - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -11841,27 +11792,6 @@ select * from v2 left outer join v1 on v2.i_item_sk = v1.ss_item_sk limit 5; - - - - - - - - - - - - - - - - - - - - - diff --git a/src/backend/gporca/data/dxl/minidump/MultipleUpdateWithJoinOnDistCol.mdp b/src/backend/gporca/data/dxl/minidump/MultipleUpdateWithJoinOnDistCol.mdp index ff3c9dc87d38..7cebead5d885 100644 --- a/src/backend/gporca/data/dxl/minidump/MultipleUpdateWithJoinOnDistCol.mdp +++ b/src/backend/gporca/data/dxl/minidump/MultipleUpdateWithJoinOnDistCol.mdp @@ -240,10 +240,7 @@ - - - - + @@ -302,11 +299,12 @@ - + + @@ -317,15 +315,14 @@ - - - - - - - - - + + + + + + + + @@ -345,8 +342,8 @@ - - + + @@ -356,7 +353,7 @@ - + @@ -373,7 +370,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/OrderedAgg_multiple_diffcol.mdp b/src/backend/gporca/data/dxl/minidump/OrderedAgg_multiple_diffcol.mdp index 2b29a828df9c..e2d0f5d2f8a7 100644 --- a/src/backend/gporca/data/dxl/minidump/OrderedAgg_multiple_diffcol.mdp +++ b/src/backend/gporca/data/dxl/minidump/OrderedAgg_multiple_diffcol.mdp @@ -541,9 +541,6 @@ EXPLAIN SELECT percentile_cont(0.5) WITHIN GROUP(ORDER BY a1), percentile_cont(0 - - - @@ -664,9 +661,6 @@ EXPLAIN SELECT percentile_cont(0.5) WITHIN GROUP(ORDER BY a1), percentile_cont(0 - - - diff --git a/src/backend/gporca/data/dxl/minidump/OrderedAgg_multiple_samecol.mdp b/src/backend/gporca/data/dxl/minidump/OrderedAgg_multiple_samecol.mdp index b99595f541f8..c4e315ec7943 100644 --- a/src/backend/gporca/data/dxl/minidump/OrderedAgg_multiple_samecol.mdp +++ b/src/backend/gporca/data/dxl/minidump/OrderedAgg_multiple_samecol.mdp @@ -541,9 +541,6 @@ EXPLAIN SELECT percentile_cont(0.5) WITHIN GROUP(ORDER BY a2), percentile_disc(0 - - - diff --git a/src/backend/gporca/data/dxl/minidump/OrderedAgg_multiple_samecol_difforderespec.mdp b/src/backend/gporca/data/dxl/minidump/OrderedAgg_multiple_samecol_difforderespec.mdp index a91acbc71c57..26ddecd518d6 100644 --- a/src/backend/gporca/data/dxl/minidump/OrderedAgg_multiple_samecol_difforderespec.mdp +++ b/src/backend/gporca/data/dxl/minidump/OrderedAgg_multiple_samecol_difforderespec.mdp @@ -553,9 +553,6 @@ EXPLAIN SELECT percentile_cont(0.5) WITHIN GROUP(ORDER BY a2), percentile_cont(0 - - - @@ -677,9 +674,6 @@ EXPLAIN SELECT percentile_cont(0.5) WITHIN GROUP(ORDER BY a2), percentile_cont(0 - - - diff --git a/src/backend/gporca/data/dxl/minidump/OrderedAgg_single.mdp b/src/backend/gporca/data/dxl/minidump/OrderedAgg_single.mdp index 95e60be9f7ee..eb2ec7e5fba2 100644 --- a/src/backend/gporca/data/dxl/minidump/OrderedAgg_single.mdp +++ b/src/backend/gporca/data/dxl/minidump/OrderedAgg_single.mdp @@ -499,9 +499,6 @@ EXPLAIN SELECT percentile_cont(0.5) WITHIN GROUP(ORDER BY a2) FROM foo; - - - diff --git a/src/backend/gporca/data/dxl/minidump/OrderedAgg_with_nonOrderedAgg.mdp b/src/backend/gporca/data/dxl/minidump/OrderedAgg_with_nonOrderedAgg.mdp index d6ab31fed4a1..80a9a76037e4 100644 --- a/src/backend/gporca/data/dxl/minidump/OrderedAgg_with_nonOrderedAgg.mdp +++ b/src/backend/gporca/data/dxl/minidump/OrderedAgg_with_nonOrderedAgg.mdp @@ -596,9 +596,6 @@ EXPLAIN SELECT percentile_cont(0.5) WITHIN GROUP(ORDER BY a1), percentile_disc(0 - - - @@ -722,9 +719,6 @@ EXPLAIN SELECT percentile_cont(0.5) WITHIN GROUP(ORDER BY a1), percentile_disc(0 - - - diff --git a/src/backend/gporca/data/dxl/minidump/OrderedAgg_with_nonconst_fraction.mdp b/src/backend/gporca/data/dxl/minidump/OrderedAgg_with_nonconst_fraction.mdp index 1f999454b3c6..fa4690a3c9e2 100644 --- a/src/backend/gporca/data/dxl/minidump/OrderedAgg_with_nonconst_fraction.mdp +++ b/src/backend/gporca/data/dxl/minidump/OrderedAgg_with_nonconst_fraction.mdp @@ -526,10 +526,6 @@ EXPLAIN SELECT percentile_cont(floor(random()*0.1)+0.5) WITHIN GROUP(ORDER BY a1 - - - - diff --git a/src/backend/gporca/data/dxl/minidump/PartitionedDelete.mdp b/src/backend/gporca/data/dxl/minidump/PartitionedDelete.mdp index fccf463ab69c..b2169cdfd2c1 100644 --- a/src/backend/gporca/data/dxl/minidump/PartitionedDelete.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartitionedDelete.mdp @@ -234,23 +234,23 @@ - + + - - - - - - - - - + + + + + + + + @@ -267,7 +267,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartitionedInsert.mdp b/src/backend/gporca/data/dxl/minidump/PartitionedInsert.mdp index 403ab31b3373..7e5fa4b19314 100644 --- a/src/backend/gporca/data/dxl/minidump/PartitionedInsert.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartitionedInsert.mdp @@ -173,10 +173,7 @@ - - - - + @@ -213,15 +210,16 @@ - + - + + @@ -232,15 +230,14 @@ - - - - - - - - - + + + + + + + + @@ -254,7 +251,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/PartitionedUpdate.mdp b/src/backend/gporca/data/dxl/minidump/PartitionedUpdate.mdp index 7125f4e8e7fd..60a0662d7f4c 100644 --- a/src/backend/gporca/data/dxl/minidump/PartitionedUpdate.mdp +++ b/src/backend/gporca/data/dxl/minidump/PartitionedUpdate.mdp @@ -197,9 +197,7 @@ - - - + @@ -246,11 +244,12 @@ - + + @@ -261,15 +260,14 @@ - - - - - - - - - + + + + + + + + @@ -292,8 +290,8 @@ - - + + @@ -303,7 +301,7 @@ - + @@ -323,7 +321,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ProjectSetFunction.mdp b/src/backend/gporca/data/dxl/minidump/ProjectSetFunction.mdp index 5866ae295619..2f338bb6b281 100644 --- a/src/backend/gporca/data/dxl/minidump/ProjectSetFunction.mdp +++ b/src/backend/gporca/data/dxl/minidump/ProjectSetFunction.mdp @@ -187,10 +187,7 @@ - - - - + @@ -230,11 +227,12 @@ - + - + + @@ -245,15 +243,13 @@ - - - - - - - - - + + + + + + + @@ -267,8 +263,8 @@ - - + + @@ -284,7 +280,7 @@ - + @@ -313,7 +309,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ReplicatedTable-CTAS.mdp b/src/backend/gporca/data/dxl/minidump/ReplicatedTable-CTAS.mdp index b3afb84e8ed8..9f9d398b16d6 100644 --- a/src/backend/gporca/data/dxl/minidump/ReplicatedTable-CTAS.mdp +++ b/src/backend/gporca/data/dxl/minidump/ReplicatedTable-CTAS.mdp @@ -99,8 +99,8 @@ - - + + @@ -122,8 +122,8 @@ - - + + @@ -133,7 +133,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ReplicatedTableInsert.mdp b/src/backend/gporca/data/dxl/minidump/ReplicatedTableInsert.mdp index 13ec859f53ab..9f0c87fbc721 100644 --- a/src/backend/gporca/data/dxl/minidump/ReplicatedTableInsert.mdp +++ b/src/backend/gporca/data/dxl/minidump/ReplicatedTableInsert.mdp @@ -149,10 +149,7 @@ - - - - + @@ -178,11 +175,12 @@ - + - + + @@ -193,15 +191,13 @@ - - - - - - - - - + + + + + + + @@ -209,7 +205,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/ReplicatedTableSequenceInsert.mdp b/src/backend/gporca/data/dxl/minidump/ReplicatedTableSequenceInsert.mdp index d391b006caf2..15b0bc04f0d9 100644 --- a/src/backend/gporca/data/dxl/minidump/ReplicatedTableSequenceInsert.mdp +++ b/src/backend/gporca/data/dxl/minidump/ReplicatedTableSequenceInsert.mdp @@ -253,10 +253,7 @@ - - - - + @@ -300,11 +297,12 @@ - + - + + @@ -315,15 +313,13 @@ - - - - - - - - - + + + + + + + @@ -337,7 +333,7 @@ - + @@ -377,8 +373,8 @@ - - + + diff --git a/src/backend/gporca/data/dxl/minidump/ReplicatedTableWithAggNoMotion.mdp b/src/backend/gporca/data/dxl/minidump/ReplicatedTableWithAggNoMotion.mdp index f43d5ebd109c..2300987fa5f3 100644 --- a/src/backend/gporca/data/dxl/minidump/ReplicatedTableWithAggNoMotion.mdp +++ b/src/backend/gporca/data/dxl/minidump/ReplicatedTableWithAggNoMotion.mdp @@ -225,9 +225,7 @@ - - - + @@ -283,26 +281,26 @@ - + - + + - + - - - - - - - - + + + + + + + @@ -311,11 +309,11 @@ - + - + @@ -328,7 +326,7 @@ - + @@ -349,7 +347,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/SelfUpdate.mdp b/src/backend/gporca/data/dxl/minidump/SelfUpdate.mdp index 77cd608aec88..47ab30ae0fb9 100644 --- a/src/backend/gporca/data/dxl/minidump/SelfUpdate.mdp +++ b/src/backend/gporca/data/dxl/minidump/SelfUpdate.mdp @@ -178,9 +178,7 @@ update t1 set b = c; - - - + @@ -216,11 +214,12 @@ update t1 set b = c; - + + @@ -234,16 +233,13 @@ update t1 set b = c; - - - - - - - - - - + + + + + + + @@ -266,8 +262,8 @@ update t1 set b = c; - - + + @@ -279,7 +275,7 @@ update t1 set b = c; - + @@ -299,7 +295,7 @@ update t1 set b = c; - + diff --git a/src/backend/gporca/data/dxl/minidump/SqlFuncDmlScalar.mdp b/src/backend/gporca/data/dxl/minidump/SqlFuncDmlScalar.mdp index db336e845026..1fb80cc3a3cb 100644 --- a/src/backend/gporca/data/dxl/minidump/SqlFuncDmlScalar.mdp +++ b/src/backend/gporca/data/dxl/minidump/SqlFuncDmlScalar.mdp @@ -245,9 +245,7 @@ LIMIT 999 - - - + @@ -295,26 +293,27 @@ LIMIT 999 - + - + + - + - - - - - - - - + + + + + + + + @@ -325,8 +324,8 @@ LIMIT 999 - - + + @@ -344,7 +343,7 @@ LIMIT 999 - + @@ -376,7 +375,7 @@ LIMIT 999 - + @@ -393,7 +392,7 @@ LIMIT 999 - + diff --git a/src/backend/gporca/data/dxl/minidump/SqlFuncDmlTvf.mdp b/src/backend/gporca/data/dxl/minidump/SqlFuncDmlTvf.mdp index 9142075dd62a..cb221dad297e 100644 --- a/src/backend/gporca/data/dxl/minidump/SqlFuncDmlTvf.mdp +++ b/src/backend/gporca/data/dxl/minidump/SqlFuncDmlTvf.mdp @@ -254,10 +254,7 @@ LIMIT 999 - - - - + @@ -306,11 +303,12 @@ LIMIT 999 - + - + + @@ -319,17 +317,16 @@ LIMIT 999 - + - - - - - - - - - + + + + + + + + @@ -343,8 +340,8 @@ LIMIT 999 - - + + @@ -365,7 +362,7 @@ LIMIT 999 - + @@ -423,7 +420,7 @@ LIMIT 999 - + diff --git a/src/backend/gporca/data/dxl/minidump/TVF-With-Deep-Subq-Args.mdp b/src/backend/gporca/data/dxl/minidump/TVF-With-Deep-Subq-Args.mdp index 63532f9c7b9e..9ba6c5a5de5b 100644 --- a/src/backend/gporca/data/dxl/minidump/TVF-With-Deep-Subq-Args.mdp +++ b/src/backend/gporca/data/dxl/minidump/TVF-With-Deep-Subq-Args.mdp @@ -430,8 +430,6 @@ SELECT generate_series(( - - @@ -488,8 +486,6 @@ SELECT generate_series(( - - @@ -643,8 +639,6 @@ SELECT generate_series(( - - @@ -701,8 +695,6 @@ SELECT generate_series(( - - diff --git a/src/backend/gporca/data/dxl/minidump/TaintedReplicatedAgg.mdp b/src/backend/gporca/data/dxl/minidump/TaintedReplicatedAgg.mdp index 452dbf9774c7..1e6bb12152da 100644 --- a/src/backend/gporca/data/dxl/minidump/TaintedReplicatedAgg.mdp +++ b/src/backend/gporca/data/dxl/minidump/TaintedReplicatedAgg.mdp @@ -219,9 +219,7 @@ - - - + @@ -268,11 +266,12 @@ - + - + + @@ -280,14 +279,13 @@ - - - - - - - - + + + + + + + @@ -298,7 +296,7 @@ - + @@ -322,9 +320,9 @@ - + - + diff --git a/src/backend/gporca/data/dxl/minidump/TaintedReplicatedFilter.mdp b/src/backend/gporca/data/dxl/minidump/TaintedReplicatedFilter.mdp index db85e1a0d25d..d01ba048ca9e 100644 --- a/src/backend/gporca/data/dxl/minidump/TaintedReplicatedFilter.mdp +++ b/src/backend/gporca/data/dxl/minidump/TaintedReplicatedFilter.mdp @@ -206,10 +206,7 @@ - - - - + @@ -251,11 +248,12 @@ - + - + + @@ -266,15 +264,13 @@ - - - - - - - - - + + + + + + + @@ -288,7 +284,7 @@ - + @@ -325,7 +321,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/TaintedReplicatedLimit.mdp b/src/backend/gporca/data/dxl/minidump/TaintedReplicatedLimit.mdp index 1eaca5f5ab04..140861caaaac 100644 --- a/src/backend/gporca/data/dxl/minidump/TaintedReplicatedLimit.mdp +++ b/src/backend/gporca/data/dxl/minidump/TaintedReplicatedLimit.mdp @@ -215,9 +215,7 @@ - - - + @@ -256,11 +254,12 @@ - + - + + @@ -268,14 +267,13 @@ - - - - - - - - + + + + + + + @@ -286,7 +284,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/TaintedReplicatedWindowAgg.mdp b/src/backend/gporca/data/dxl/minidump/TaintedReplicatedWindowAgg.mdp index ea47d8fc6fe3..db11251d92ad 100644 --- a/src/backend/gporca/data/dxl/minidump/TaintedReplicatedWindowAgg.mdp +++ b/src/backend/gporca/data/dxl/minidump/TaintedReplicatedWindowAgg.mdp @@ -220,9 +220,7 @@ - - - + @@ -271,11 +269,12 @@ - + - + + @@ -283,14 +282,13 @@ - - - - - - - - + + + + + + + @@ -301,7 +299,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/Union-On-HJNs.mdp b/src/backend/gporca/data/dxl/minidump/Union-On-HJNs.mdp index ca4ac97176f1..ccdbe8d87f44 100644 --- a/src/backend/gporca/data/dxl/minidump/Union-On-HJNs.mdp +++ b/src/backend/gporca/data/dxl/minidump/Union-On-HJNs.mdp @@ -1031,8 +1031,6 @@ - - @@ -1279,9 +1277,7 @@ - - @@ -1364,8 +1360,6 @@ - - @@ -2424,8 +2418,6 @@ - - @@ -2577,7 +2569,6 @@ - @@ -2663,9 +2654,7 @@ - - @@ -2748,8 +2737,6 @@ - - diff --git a/src/backend/gporca/data/dxl/minidump/UpdateCardinalityAssert.mdp b/src/backend/gporca/data/dxl/minidump/UpdateCardinalityAssert.mdp index 0e701f0c3b67..5263cb84d408 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateCardinalityAssert.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateCardinalityAssert.mdp @@ -218,9 +218,7 @@ - - - + @@ -275,11 +273,12 @@ - + + @@ -290,15 +289,14 @@ - - - - - - - - - + + + + + + + + @@ -318,8 +316,8 @@ - - + + @@ -329,7 +327,7 @@ - + @@ -346,7 +344,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateCheckConstraint.mdp b/src/backend/gporca/data/dxl/minidump/UpdateCheckConstraint.mdp index d57ae91617d9..24344ff05d26 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateCheckConstraint.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateCheckConstraint.mdp @@ -294,11 +294,12 @@ - + + @@ -315,17 +316,14 @@ - - - - - - - - - - - + + + + + + + + @@ -351,8 +349,8 @@ - - + + @@ -398,8 +396,8 @@ - - + + @@ -411,7 +409,7 @@ - + @@ -437,7 +435,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateDistKeyMismatchedDistribution.mdp b/src/backend/gporca/data/dxl/minidump/UpdateDistKeyMismatchedDistribution.mdp index e0fe02081aaf..7422bf0dcbe8 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateDistKeyMismatchedDistribution.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateDistKeyMismatchedDistribution.mdp @@ -234,9 +234,7 @@ - - - + @@ -302,11 +300,12 @@ - + + @@ -320,16 +319,14 @@ - - - - - - - - - - + + + + + + + + @@ -352,8 +349,8 @@ - - + + @@ -363,7 +360,7 @@ - + @@ -383,7 +380,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateDistKeyWithNestedJoin.mdp b/src/backend/gporca/data/dxl/minidump/UpdateDistKeyWithNestedJoin.mdp index fe9965b45a91..642a028f0345 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateDistKeyWithNestedJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateDistKeyWithNestedJoin.mdp @@ -602,9 +602,7 @@ WHERE t1.id1 = t2.id1; - - - + @@ -680,11 +678,12 @@ WHERE t1.id1 = t2.id1; - + + @@ -695,15 +694,14 @@ WHERE t1.id1 = t2.id1; - - - - - - - - - + + + + + + + + @@ -723,8 +721,8 @@ WHERE t1.id1 = t2.id1; - - + + @@ -734,7 +732,7 @@ WHERE t1.id1 = t2.id1; - + @@ -751,7 +749,7 @@ WHERE t1.id1 = t2.id1; - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateDistrKey.mdp b/src/backend/gporca/data/dxl/minidump/UpdateDistrKey.mdp index ec554d353b14..24a02103d822 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateDistrKey.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateDistrKey.mdp @@ -213,9 +213,7 @@ - - - + @@ -270,11 +268,12 @@ - + + @@ -285,15 +284,14 @@ - - - - - - - - - + + + + + + + + @@ -313,8 +311,8 @@ - - + + @@ -324,7 +322,7 @@ - + @@ -341,7 +339,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateDroppedCols.mdp b/src/backend/gporca/data/dxl/minidump/UpdateDroppedCols.mdp index 5999d1537c19..0f232f6eaf80 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateDroppedCols.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateDroppedCols.mdp @@ -198,9 +198,7 @@ - - - + @@ -247,11 +245,12 @@ - + + @@ -262,15 +261,14 @@ - - - - - - - - - + + + + + + + + @@ -290,8 +288,8 @@ - - + + @@ -301,7 +299,7 @@ - + @@ -318,7 +316,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateInCTE.mdp b/src/backend/gporca/data/dxl/minidump/UpdateInCTE.mdp new file mode 100644 index 000000000000..6f4238702826 --- /dev/null +++ b/src/backend/gporca/data/dxl/minidump/UpdateInCTE.mdp @@ -0,0 +1,391 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/backend/gporca/data/dxl/minidump/UpdateNoCardinalityAssert.mdp b/src/backend/gporca/data/dxl/minidump/UpdateNoCardinalityAssert.mdp index 31e7fd14bfe7..b5384c6547c0 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateNoCardinalityAssert.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateNoCardinalityAssert.mdp @@ -391,11 +391,12 @@ - + + @@ -406,15 +407,14 @@ - - - - - - - - - + + + + + + + + @@ -434,8 +434,8 @@ - - + + @@ -445,7 +445,7 @@ - + @@ -462,7 +462,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateNoDistKeyMismatchedDistribution.mdp b/src/backend/gporca/data/dxl/minidump/UpdateNoDistKeyMismatchedDistribution.mdp index d7dd7212dca9..ddf01da09f00 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateNoDistKeyMismatchedDistribution.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateNoDistKeyMismatchedDistribution.mdp @@ -235,9 +235,7 @@ - - - + @@ -303,11 +301,12 @@ - + + @@ -321,16 +320,14 @@ - - - - - - - - - - + + + + + + + + @@ -353,13 +350,13 @@ - - + + - + @@ -379,7 +376,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateNoEnforceConstraints.mdp b/src/backend/gporca/data/dxl/minidump/UpdateNoEnforceConstraints.mdp index 6d2e6ade0c3e..03e1e4cae250 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateNoEnforceConstraints.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateNoEnforceConstraints.mdp @@ -190,10 +190,7 @@ update constraints_tab SET notnullcol = NULL, positivecol =-1; - - - - + @@ -239,11 +236,12 @@ update constraints_tab SET notnullcol = NULL, positivecol =-1; - + + @@ -257,19 +255,17 @@ update constraints_tab SET notnullcol = NULL, positivecol =-1; - - - - - - - - - - + + + + + + + + - + @@ -289,7 +285,7 @@ update constraints_tab SET notnullcol = NULL, positivecol =-1; - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateNotNullCols.mdp b/src/backend/gporca/data/dxl/minidump/UpdateNotNullCols.mdp index f1d00b8d3a30..1e2afe996581 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateNotNullCols.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateNotNullCols.mdp @@ -328,11 +328,12 @@ - + + @@ -343,15 +344,14 @@ - - - - - - - - - + + + + + + + + @@ -371,8 +371,8 @@ - - + + @@ -399,8 +399,8 @@ - - + + @@ -412,7 +412,7 @@ - + @@ -432,7 +432,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdatePartTable.mdp b/src/backend/gporca/data/dxl/minidump/UpdatePartTable.mdp index bad577f8ed51..395d4577acb5 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdatePartTable.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdatePartTable.mdp @@ -198,9 +198,7 @@ - - - + @@ -247,11 +245,12 @@ - + + @@ -262,15 +261,14 @@ - - - - - - - - - + + + + + + + + @@ -290,8 +288,8 @@ - - + + @@ -301,7 +299,7 @@ - + @@ -318,7 +316,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateRandomDistr.mdp b/src/backend/gporca/data/dxl/minidump/UpdateRandomDistr.mdp index e7979bb06aa8..f7877ec5cfb4 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateRandomDistr.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateRandomDistr.mdp @@ -196,11 +196,12 @@ - + + @@ -211,18 +212,16 @@ - - - - - - - - - + + + + + + + - + @@ -239,7 +238,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateReturning.mdp b/src/backend/gporca/data/dxl/minidump/UpdateReturning.mdp new file mode 100644 index 000000000000..d45885a90dab --- /dev/null +++ b/src/backend/gporca/data/dxl/minidump/UpdateReturning.mdp @@ -0,0 +1,397 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/backend/gporca/data/dxl/minidump/UpdateReturningProjection.mdp b/src/backend/gporca/data/dxl/minidump/UpdateReturningProjection.mdp new file mode 100644 index 000000000000..803accc20f9c --- /dev/null +++ b/src/backend/gporca/data/dxl/minidump/UpdateReturningProjection.mdp @@ -0,0 +1,439 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/backend/gporca/data/dxl/minidump/UpdateUniqueConstraint-2.mdp b/src/backend/gporca/data/dxl/minidump/UpdateUniqueConstraint-2.mdp index f05bcb78f115..96e743c856ee 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateUniqueConstraint-2.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateUniqueConstraint-2.mdp @@ -699,9 +699,7 @@ - - - + @@ -765,11 +763,12 @@ - + + @@ -780,15 +779,14 @@ - - - - - - - - - + + + + + + + + @@ -808,8 +806,8 @@ - - + + @@ -821,7 +819,7 @@ - + @@ -838,7 +836,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateUniqueConstraint.mdp b/src/backend/gporca/data/dxl/minidump/UpdateUniqueConstraint.mdp index 890d4d335bcb..088daabd29d1 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateUniqueConstraint.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateUniqueConstraint.mdp @@ -196,11 +196,12 @@ - + + @@ -211,15 +212,14 @@ - - - - - - - - - + + + + + + + + @@ -239,13 +239,13 @@ - - + + - + @@ -266,8 +266,8 @@ - - + + @@ -277,7 +277,7 @@ - + @@ -294,7 +294,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateVolatileFunction.mdp b/src/backend/gporca/data/dxl/minidump/UpdateVolatileFunction.mdp index 43e7b17a4839..3b59bfc0f46a 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateVolatileFunction.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateVolatileFunction.mdp @@ -411,11 +411,12 @@ - + + @@ -426,15 +427,14 @@ - - - - - - - - - + + + + + + + + @@ -454,8 +454,8 @@ - - + + @@ -465,7 +465,7 @@ - + @@ -482,7 +482,7 @@ - + @@ -498,8 +498,8 @@ - - + + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateWindowGatherMerge.mdp b/src/backend/gporca/data/dxl/minidump/UpdateWindowGatherMerge.mdp index 55d3f01242eb..5502c0a3cac2 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateWindowGatherMerge.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateWindowGatherMerge.mdp @@ -629,9 +629,7 @@ - - - + @@ -709,11 +707,12 @@ - + + @@ -724,15 +723,13 @@ - - - - - - - - - + + + + + + + @@ -752,13 +749,13 @@ - - + + - + @@ -775,7 +772,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateWithHashJoin.mdp b/src/backend/gporca/data/dxl/minidump/UpdateWithHashJoin.mdp index e7e1b1db9797..3f5f1fb65c70 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateWithHashJoin.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateWithHashJoin.mdp @@ -240,9 +240,7 @@ - - - + @@ -301,11 +299,12 @@ - + + @@ -316,18 +315,17 @@ - - - - - - - - - + + + + + + + + - + @@ -344,7 +342,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateWithOids.mdp b/src/backend/gporca/data/dxl/minidump/UpdateWithOids.mdp index 26f539d0c504..0f8161342c09 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateWithOids.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateWithOids.mdp @@ -157,9 +157,7 @@ - - - + @@ -208,11 +206,12 @@ - + + @@ -223,16 +222,15 @@ - - - - - - - - - - + + + + + + + + + @@ -255,8 +253,8 @@ - - + + @@ -266,7 +264,7 @@ - + @@ -286,7 +284,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateWithTriggers.mdp b/src/backend/gporca/data/dxl/minidump/UpdateWithTriggers.mdp index e626def55975..09e41d931102 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateWithTriggers.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateWithTriggers.mdp @@ -204,7 +204,7 @@ - + @@ -220,21 +220,30 @@ + + + + + + + + + + + - - - - - - - - - - + + + + + + + + - + @@ -254,7 +263,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdateZeroRows.mdp b/src/backend/gporca/data/dxl/minidump/UpdateZeroRows.mdp index 8e063421b638..83ad08c75077 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdateZeroRows.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdateZeroRows.mdp @@ -359,9 +359,7 @@ - - - + @@ -410,11 +408,12 @@ - + + @@ -428,16 +427,14 @@ - - - - - - - - - - + + + + + + + + @@ -460,8 +457,8 @@ - - + + @@ -491,8 +488,8 @@ - - + + @@ -530,7 +527,7 @@ - + @@ -553,7 +550,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdatingDistributionColumn.mdp b/src/backend/gporca/data/dxl/minidump/UpdatingDistributionColumn.mdp index 32023291390b..9a241eff7a37 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdatingDistributionColumn.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdatingDistributionColumn.mdp @@ -209,9 +209,7 @@ - - - + @@ -270,11 +268,12 @@ - + + @@ -285,15 +284,14 @@ - - - - - - - - - + + + + + + + + @@ -313,8 +311,8 @@ - - + + @@ -324,7 +322,7 @@ - + @@ -341,7 +339,7 @@ - + @@ -358,7 +356,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdatingMultipleColumn.mdp b/src/backend/gporca/data/dxl/minidump/UpdatingMultipleColumn.mdp index cd589c11448f..4f2c3a8e6559 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdatingMultipleColumn.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdatingMultipleColumn.mdp @@ -547,10 +547,7 @@ - - - - + @@ -584,11 +581,12 @@ - + + @@ -599,15 +597,14 @@ - - - - - - - - - + + + + + + + + @@ -627,8 +624,8 @@ - - + + @@ -638,7 +635,7 @@ - + @@ -655,7 +652,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdatingNonDistColSameTable.mdp b/src/backend/gporca/data/dxl/minidump/UpdatingNonDistColSameTable.mdp index 0c724aa138bc..3ff155a899f1 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdatingNonDistColSameTable.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdatingNonDistColSameTable.mdp @@ -196,9 +196,7 @@ - - - + @@ -234,11 +232,12 @@ - + + @@ -252,19 +251,17 @@ - - - - - - - - - - + + + + + + + + - + @@ -284,7 +281,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/UpdatingNonDistributionColumnFunc.mdp b/src/backend/gporca/data/dxl/minidump/UpdatingNonDistributionColumnFunc.mdp index 30bd81e5f423..f96568bff261 100644 --- a/src/backend/gporca/data/dxl/minidump/UpdatingNonDistributionColumnFunc.mdp +++ b/src/backend/gporca/data/dxl/minidump/UpdatingNonDistributionColumnFunc.mdp @@ -188,9 +188,7 @@ - - - + @@ -239,11 +237,12 @@ - + + @@ -254,18 +253,17 @@ - - - - - - - - - + + + + + + + + - + @@ -282,7 +280,7 @@ - + @@ -299,7 +297,7 @@ - + diff --git a/src/backend/gporca/data/dxl/minidump/VolatileFunctionsBelowScalarAgg.mdp b/src/backend/gporca/data/dxl/minidump/VolatileFunctionsBelowScalarAgg.mdp index f1036d65cf1c..358f00c38bc7 100644 --- a/src/backend/gporca/data/dxl/minidump/VolatileFunctionsBelowScalarAgg.mdp +++ b/src/backend/gporca/data/dxl/minidump/VolatileFunctionsBelowScalarAgg.mdp @@ -192,9 +192,7 @@ - - - + @@ -251,11 +249,12 @@ - + - + + @@ -263,14 +262,14 @@ - - - - - - - - + + + + + + + + @@ -281,7 +280,7 @@ - + @@ -323,9 +322,9 @@ - + - + diff --git a/src/backend/gporca/data/dxl/parse_tests/q57-DMLDelete.xml b/src/backend/gporca/data/dxl/parse_tests/q57-DMLDelete.xml index bda6d69b845d..f7c7cca57496 100644 --- a/src/backend/gporca/data/dxl/parse_tests/q57-DMLDelete.xml +++ b/src/backend/gporca/data/dxl/parse_tests/q57-DMLDelete.xml @@ -6,6 +6,7 @@ + diff --git a/src/backend/gporca/data/dxl/parse_tests/q58-DMLInsert.xml b/src/backend/gporca/data/dxl/parse_tests/q58-DMLInsert.xml index ed5c5fb4b0c1..dd121facafba 100644 --- a/src/backend/gporca/data/dxl/parse_tests/q58-DMLInsert.xml +++ b/src/backend/gporca/data/dxl/parse_tests/q58-DMLInsert.xml @@ -28,6 +28,7 @@ + diff --git a/src/backend/gporca/data/dxl/parse_tests/q60-DMLUpdate.xml b/src/backend/gporca/data/dxl/parse_tests/q60-DMLUpdate.xml index 038c6673722a..83ab9390b69f 100644 --- a/src/backend/gporca/data/dxl/parse_tests/q60-DMLUpdate.xml +++ b/src/backend/gporca/data/dxl/parse_tests/q60-DMLUpdate.xml @@ -6,6 +6,7 @@ + diff --git a/src/backend/gporca/libgpopt/include/gpopt/base/CColRef.h b/src/backend/gporca/libgpopt/include/gpopt/base/CColRef.h index e7686ed27e3e..352a75e4a961 100644 --- a/src/backend/gporca/libgpopt/include/gpopt/base/CColRef.h +++ b/src/backend/gporca/libgpopt/include/gpopt/base/CColRef.h @@ -197,6 +197,12 @@ class CColRef : public gpos::DbgPrintMixin m_used = EUsed; } + void + MarkUsage(EUsedStatus used) + { + m_used = used; + } + void MarkAsUnknown() { diff --git a/src/backend/gporca/libgpopt/include/gpopt/base/CUtils.h b/src/backend/gporca/libgpopt/include/gpopt/base/CUtils.h index f89184af36c2..2a1645813e85 100644 --- a/src/backend/gporca/libgpopt/include/gpopt/base/CUtils.h +++ b/src/backend/gporca/libgpopt/include/gpopt/base/CUtils.h @@ -776,6 +776,12 @@ class CUtils // check if the given operator is a logical DML operator static BOOL FLogicalDML(COperator *pop); + // recursively checks if the given expression has logical DML operator + static BOOL FHasLogicalDML(CMemoryPool *mp, CExpression *pexpr); + + // is operator responsible for CTAS operation + static BOOL FCTAS(COperator *pop); + // return regular string from wide-character string static CHAR *CreateMultiByteCharStringFromWCString(CMemoryPool *mp, WCHAR *wsz); diff --git a/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalDML.h b/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalDML.h index 5a83a1c009a7..df3b3e2ce679 100644 --- a/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalDML.h +++ b/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalDML.h @@ -13,7 +13,7 @@ #include "gpos/base.h" -#include "gpopt/operators/CLogical.h" +#include "gpopt/operators/CLogicalReturning.h" namespace gpopt { @@ -28,7 +28,7 @@ class CTableDescriptor; // Logical DML operator // //--------------------------------------------------------------------------- -class CLogicalDML : public CLogical +class CLogicalDML : public CLogicalReturning { public: // enum of DML operators @@ -46,9 +46,6 @@ class CLogicalDML : public CLogical // dml operator EDMLOperator m_edmlop; - // table descriptor - CTableDescriptor *m_ptabdesc; - // source columns CColRefArray *m_pdrgpcrSource; @@ -81,9 +78,9 @@ class CLogicalDML : public CLogical // ctor CLogicalDML(CMemoryPool *mp, EDMLOperator edmlop, CTableDescriptor *ptabdesc, CColRefArray *colref_array, - CBitSet *pbsModified, CColRef *pcrAction, CColRef *pcrCtid, - CColRef *pcrSegmentId, CColRef *pcrTupleOid, - CColRef *pcrTableOid); + CColRefArray *pdrgpcrOutput, CBitSet *pbsModified, + CColRef *pcrAction, CColRef *pcrCtid, CColRef *pcrSegmentId, + CColRef *pcrTupleOid, CColRef *pcrTableOid); // dtor virtual ~CLogicalDML(); @@ -151,13 +148,6 @@ class CLogicalDML : public CLogical return m_pcrSegmentId; } - // return table's descriptor - CTableDescriptor * - Ptabdesc() const - { - return m_ptabdesc; - } - // tuple oid column CColRef * PcrTupleOid() const @@ -224,10 +214,6 @@ class CLogicalDML : public CLogical // candidate set of xforms virtual CXformSet *PxfsCandidates(CMemoryPool *mp) const; - // derive key collections - virtual CKeyCollection *DeriveKeyCollection( - CMemoryPool *mp, CExpressionHandle &exprhdl) const; - // derive statistics virtual IStatistics *PstatsDerive(CMemoryPool *mp, CExpressionHandle &exprhdl, diff --git a/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalDelete.h b/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalDelete.h index 9c270733c3f1..4e5b6fa93073 100644 --- a/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalDelete.h +++ b/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalDelete.h @@ -13,7 +13,7 @@ #include "gpos/base.h" -#include "gpopt/operators/CLogical.h" +#include "gpopt/operators/CLogicalReturning.h" namespace gpopt { @@ -28,12 +28,9 @@ class CTableDescriptor; // Logical Delete operator // //--------------------------------------------------------------------------- -class CLogicalDelete : public CLogical +class CLogicalDelete : public CLogicalReturning { private: - // table descriptor - CTableDescriptor *m_ptabdesc; - // columns to delete CColRefArray *m_pdrgpcr; @@ -49,6 +46,9 @@ class CLogicalDelete : public CLogical // private copy ctor CLogicalDelete(const CLogicalDelete &); + // initialize locally used columns + void InitUsedColumns(); + public: // ctor explicit CLogicalDelete(CMemoryPool *mp); @@ -58,6 +58,12 @@ class CLogicalDelete : public CLogical CColRefArray *colref_array, CColRef *pcrCtid, CColRef *pcrSegmentId, CColRef *pcrTableOid); + // ctor + CLogicalDelete(CMemoryPool *mp, CTableDescriptor *ptabdesc, + CColRefArray *colref_array, CColRef *pcrCtid, + CColRef *pcrSegmentId, CColRef *pcrTableOid, + CColRefArray *pdrgpcrOutput); + // dtor virtual ~CLogicalDelete(); @@ -103,13 +109,6 @@ class CLogicalDelete : public CLogical return m_pcrTableOid; } - // return table's descriptor - CTableDescriptor * - Ptabdesc() const - { - return m_ptabdesc; - } - // operator specific hash function virtual ULONG HashValue() const; @@ -174,10 +173,6 @@ class CLogicalDelete : public CLogical // candidate set of xforms virtual CXformSet *PxfsCandidates(CMemoryPool *mp) const; - // derive key collections - virtual CKeyCollection *DeriveKeyCollection( - CMemoryPool *mp, CExpressionHandle &exprhdl) const; - // derive statistics virtual IStatistics *PstatsDerive(CMemoryPool *mp, CExpressionHandle &exprhdl, diff --git a/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalInsert.h b/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalInsert.h index 6de2023612ce..43a5c3db7075 100644 --- a/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalInsert.h +++ b/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalInsert.h @@ -13,7 +13,7 @@ #include "gpos/base.h" -#include "gpopt/operators/CLogical.h" +#include "gpopt/operators/CLogicalReturning.h" namespace gpopt { @@ -28,18 +28,18 @@ class CTableDescriptor; // Logical Insert operator // //--------------------------------------------------------------------------- -class CLogicalInsert : public CLogical +class CLogicalInsert : public CLogicalReturning { private: - // table descriptor - CTableDescriptor *m_ptabdesc; - // source columns CColRefArray *m_pdrgpcrSource; // private copy ctor CLogicalInsert(const CLogicalInsert &); + // initialize locally used columns + void InitUsedColumns(); + public: // ctor explicit CLogicalInsert(CMemoryPool *mp); @@ -48,6 +48,10 @@ class CLogicalInsert : public CLogical CLogicalInsert(CMemoryPool *mp, CTableDescriptor *ptabdesc, CColRefArray *colref_array); + // ctor + CLogicalInsert(CMemoryPool *mp, CTableDescriptor *ptabdesc, + CColRefArray *colref_array, CColRefArray *pdrgpcrOutput); + // dtor virtual ~CLogicalInsert(); @@ -72,13 +76,6 @@ class CLogicalInsert : public CLogical return m_pdrgpcrSource; } - // return table's descriptor - CTableDescriptor * - Ptabdesc() const - { - return m_ptabdesc; - } - // operator specific hash function virtual ULONG HashValue() const; @@ -143,10 +140,6 @@ class CLogicalInsert : public CLogical // candidate set of xforms virtual CXformSet *PxfsCandidates(CMemoryPool *mp) const; - // derive key collections - virtual CKeyCollection *DeriveKeyCollection( - CMemoryPool *mp, CExpressionHandle &exprhdl) const; - // derive statistics virtual IStatistics *PstatsDerive(CMemoryPool *mp, CExpressionHandle &exprhdl, diff --git a/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalReturning.h b/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalReturning.h new file mode 100644 index 000000000000..bf9b879d8db1 --- /dev/null +++ b/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalReturning.h @@ -0,0 +1,96 @@ +//--------------------------------------------------------------------------- +// Copyright (c) 2025 Greengage Community +// +// @filename: +// CLogicalReturning.h +// +// @doc: +// Base class of operators that have returning columns +//--------------------------------------------------------------------------- +#ifndef GPOS_CLogicalReturning_H +#define GPOS_CLogicalReturning_H + +#include "gpos/base.h" + +#include "gpopt/operators/CExpressionHandle.h" +#include "gpopt/operators/CLogical.h" +#include "naucrates/base/IDatum.h" + +namespace gpopt +{ +using namespace gpnaucrates; + +//--------------------------------------------------------------------------- +// @class: +// CLogicalReturning +// +// @doc: +// Base class of logical operators that have returning columns +// +//--------------------------------------------------------------------------- +class CLogicalReturning : public CLogical +{ +private: + // private copy ctor + CLogicalReturning(const CLogicalReturning &); + +protected: + // table descriptor + CTableDescriptor *m_ptabdesc; + + // returning columns + CColRefArray *m_pdrgpcrOutput; + +public: + // ctor + CLogicalReturning(CMemoryPool *mp); + + // ctor + CLogicalReturning(CMemoryPool *mp, CTableDescriptor *ptabdesc); + + // ctor + CLogicalReturning(CMemoryPool *mp, CTableDescriptor *ptabdesc, + CColRefArray *pdrgpcrOutput); + + // dtor + virtual ~CLogicalReturning(); + + // output columns + CColRefArray * + PdrgpcrOutput() const + { + return m_pdrgpcrOutput; + } + + // return table's descriptor + CTableDescriptor * + Ptabdesc() const + { + return m_ptabdesc; + } + + // operator specific hash function + virtual ULONG HashValue() const; + + // match function + virtual BOOL MatchesReturning(CLogicalReturning *popReturning) const; + + // return a copy of output columns + virtual CColRefArray *CopyRemappedColumns(CMemoryPool *mp, + UlongToColRefMap *colref_mapping, + BOOL must_exist); + + // derive key collections + virtual CKeyCollection *DeriveKeyCollection( + CMemoryPool *mp, CExpressionHandle &exprhdl) const; + + // debug print + virtual IOstream &OsPrint(IOstream &) const; + +}; // class CLogicalReturning + +} // namespace gpopt + +#endif // !GPOS_CLogicalReturning_H + +// EOF diff --git a/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalUpdate.h b/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalUpdate.h index 4c290bed7ff2..b2953ad7531e 100644 --- a/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalUpdate.h +++ b/src/backend/gporca/libgpopt/include/gpopt/operators/CLogicalUpdate.h @@ -13,7 +13,7 @@ #include "gpos/base.h" -#include "gpopt/operators/CLogical.h" +#include "gpopt/operators/CLogicalReturning.h" namespace gpopt { @@ -28,12 +28,9 @@ class CTableDescriptor; // Logical Update operator // //--------------------------------------------------------------------------- -class CLogicalUpdate : public CLogical +class CLogicalUpdate : public CLogicalReturning { private: - // table descriptor - CTableDescriptor *m_ptabdesc; - // columns to delete CColRefArray *m_pdrgpcrDelete; @@ -55,6 +52,9 @@ class CLogicalUpdate : public CLogical // private copy ctor CLogicalUpdate(const CLogicalUpdate &); + // initialize locally used columns + void InitUsedColumns(); + public: // ctor explicit CLogicalUpdate(CMemoryPool *mp); @@ -65,6 +65,13 @@ class CLogicalUpdate : public CLogical CColRef *pcrCtid, CColRef *pcrSegmentId, CColRef *pcrTupleOid, CColRef *pcrTableOid); + // ctor + CLogicalUpdate(CMemoryPool *mp, CTableDescriptor *ptabdesc, + CColRefArray *pdrgpcrDelete, CColRefArray *pdrgpcrInsert, + CColRef *pcrCtid, CColRef *pcrSegmentId, + CColRef *pcrTupleOid, CColRef *pcrTableOid, + CColRefArray *pdrgpcrOutput); + // dtor virtual ~CLogicalUpdate(); @@ -124,13 +131,6 @@ class CLogicalUpdate : public CLogical return m_pcrTableOid; } - // return table's descriptor - CTableDescriptor * - Ptabdesc() const - { - return m_ptabdesc; - } - // operator specific hash function virtual ULONG HashValue() const; @@ -195,10 +195,6 @@ class CLogicalUpdate : public CLogical // candidate set of xforms virtual CXformSet *PxfsCandidates(CMemoryPool *mp) const; - // derive key collections - virtual CKeyCollection *DeriveKeyCollection( - CMemoryPool *mp, CExpressionHandle &exprhdl) const; - // derive statistics virtual IStatistics *PstatsDerive(CMemoryPool *mp, CExpressionHandle &exprhdl, diff --git a/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalDML.h b/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalDML.h index 377c02fe210c..c569b816950f 100644 --- a/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalDML.h +++ b/src/backend/gporca/libgpopt/include/gpopt/operators/CPhysicalDML.h @@ -42,6 +42,9 @@ class CPhysicalDML : public CPhysical // array of source columns CColRefArray *m_pdrgpcrSource; + // returning columns + CColRefArray *m_pdrgpcrOutput; + // set of modified columns from the target table CBitSet *m_pbsModified; @@ -67,9 +70,18 @@ class CPhysicalDML : public CPhysical // required order spec COrderSpec *m_pos; + // output distribution + CDistributionSpec *m_pdsOutput; + // required columns by local members CColRefSet *m_pcrsRequiredLocal; + // in case of CTAS dml node will output source columns + const BOOL m_isCTAS; + + // is there any triggers for this kind of dml operation + const BOOL m_hasTriggers; + // compute required order spec COrderSpec *PosComputeRequired(CMemoryPool *mp, CTableDescriptor *ptabdesc); @@ -83,9 +95,9 @@ class CPhysicalDML : public CPhysical // ctor CPhysicalDML(CMemoryPool *mp, CLogicalDML::EDMLOperator edmlop, CTableDescriptor *ptabdesc, CColRefArray *pdrgpcrSource, - CBitSet *pbsModified, CColRef *pcrAction, CColRef *pcrCtid, - CColRef *pcrSegmentId, CColRef *pcrTupleOid, - CColRef *prcTableOid); + CColRefArray *pdrgpcrOutput, CBitSet *pbsModified, + CColRef *pcrAction, CColRef *pcrCtid, CColRef *pcrSegmentId, + CColRef *pcrTupleOid, CColRef *prcTableOid); // dtor virtual ~CPhysicalDML(); @@ -160,6 +172,13 @@ class CPhysicalDML : public CPhysical return m_pdrgpcrSource; } + // output columns + virtual CColRefArray * + PdrgpcrOutput() const + { + return m_pdrgpcrOutput; + } + // match function virtual BOOL Matches(COperator *pop) const; @@ -173,6 +192,13 @@ class CPhysicalDML : public CPhysical return false; } + // does dml node has corresponding triggers + virtual BOOL + HasTriggers() const + { + return m_hasTriggers; + } + //------------------------------------------------------------------------------------- // Required Plan Properties //------------------------------------------------------------------------------------- diff --git a/src/backend/gporca/libgpopt/include/gpopt/xforms/CXformUtils.h b/src/backend/gporca/libgpopt/include/gpopt/xforms/CXformUtils.h index 23882b086d91..7493d84475e7 100644 --- a/src/backend/gporca/libgpopt/include/gpopt/xforms/CXformUtils.h +++ b/src/backend/gporca/libgpopt/include/gpopt/xforms/CXformUtils.h @@ -351,6 +351,12 @@ class CXformUtils // comparator used in sorting arrays of project elements based on the column id of the first entry static INT ICmpPrjElemsArr(const void *pvFst, const void *pvSnd); + // private warehouse for checking triggers on the + // given table that match the given DML operation + static BOOL FTriggersExistInner(CLogicalDML::EDMLOperator edmlop, + CTableDescriptor *ptabdesc, + BOOL shouldCheck, BOOL fBefore); + public: // helper function for implementation xforms on binary operators // with predicates (e.g. joins) @@ -423,14 +429,19 @@ class CXformUtils static CExpression *PexprLogicalDMLOverProject( CMemoryPool *mp, CExpression *pexprChild, CLogicalDML::EDMLOperator edmlop, CTableDescriptor *ptabdesc, - CColRefArray *colref_array, CColRef *pcrCtid, CColRef *pcrSegmentId, - CColRef *pcrTableOid); + CColRefArray *colref_array, CColRefArray *pdrgpcrOutput, + CColRef *pcrCtid, CColRef *pcrSegmentId, CColRef *pcrTableOid); // check whether there are any BEFORE or AFTER triggers on the // given table that match the given DML operation static BOOL FTriggersExist(CLogicalDML::EDMLOperator edmlop, CTableDescriptor *ptabdesc, BOOL fBefore); + // check whether there are any triggers on the + // given table that match the given DML operation + static BOOL FTriggersExist(CLogicalDML::EDMLOperator edmlop, + CTableDescriptor *ptabdesc); + // does the given trigger type match the given logical DML type static BOOL FTriggerApplies(CLogicalDML::EDMLOperator edmlop, const IMDTrigger *pmdtrigger); diff --git a/src/backend/gporca/libgpopt/src/base/CQueryContext.cpp b/src/backend/gporca/libgpopt/src/base/CQueryContext.cpp index ae84429e585b..cffe56cbe0ce 100644 --- a/src/backend/gporca/libgpopt/src/base/CQueryContext.cpp +++ b/src/backend/gporca/libgpopt/src/base/CQueryContext.cpp @@ -211,12 +211,11 @@ CQueryContext::PqcGenerate(CMemoryPool *mp, CExpression *pexpr, CDistributionSpec *pds = NULL; - BOOL fDML = CUtils::FLogicalDML(pexpr->Pop()); - poptctxt->MarkDMLQuery(fDML); - - // DML commands do not have distribution requirement. Otherwise the - // distribution requirement is Singleton. - if (fDML) + // Dont require distribution for DML with empty output or CTAS. + // Otherwise the distribution requirement is Singleton. + if ((CUtils::FLogicalDML(pexpr->Pop()) && + pdrgpulQueryOutputColRefId->Size() == 0) || + CUtils::FCTAS(pexpr->Pop())) { pds = GPOS_NEW(mp) CDistributionSpecAny(COperator::EopSentinel); } @@ -240,6 +239,27 @@ CQueryContext::PqcGenerate(CMemoryPool *mp, CExpression *pexpr, // Required CTEs are obtained from the CTEInfo global information in the optimizer context CCTEReq *pcter = poptctxt->Pcteinfo()->PcterProducers(mp); + // check if query has DML operation and mark it + BOOL fDML = CUtils::FHasLogicalDML(mp, pexpr); + if (!fDML) + { + // also check all required CTEs + CExpressionArray *pdrgpexpr = poptctxt->Pcteinfo()->PdrgPexpr(mp); + + for (ULONG ul = 0; ul < pdrgpexpr->Size(); ul++) + { + CExpression *pCteExpr = (*pdrgpexpr)[ul]; + if (CUtils::FHasLogicalDML(mp, pCteExpr)) + { + fDML = true; + break; + } + } + + pdrgpexpr->Release(); + } + poptctxt->MarkDMLQuery(fDML); + // NB: Partition propagation requirements are not initialized here. They are // constructed later based on derived relation properties (CPartInfo) by // CReqdPropPlan::InitReqdPartitionPropagation(). diff --git a/src/backend/gporca/libgpopt/src/base/CUtils.cpp b/src/backend/gporca/libgpopt/src/base/CUtils.cpp index a1fddc6f565d..60bb8922177d 100644 --- a/src/backend/gporca/libgpopt/src/base/CUtils.cpp +++ b/src/backend/gporca/libgpopt/src/base/CUtils.cpp @@ -10,6 +10,7 @@ //--------------------------------------------------------------------------- #include "gpopt/base/CUtils.h" +#include "gpos/common/CDynamicPtrArray.h" #include "gpos/common/clibwrapper.h" #include "gpos/common/syslibwrapper.h" #include "gpos/io/CFileDescriptor.h" @@ -3276,6 +3277,55 @@ CUtils::FLogicalDML(COperator *pop) COperator::EopLogicalUpdate == op_id; } +BOOL +CUtils::FCTAS(COperator *pop) +{ + GPOS_ASSERT(NULL != pop); + + if (COperator::EopLogicalInsert != pop->Eopid()) + { + return false; + } + + CLogicalInsert *popInsert = CLogicalInsert::PopConvert(pop); + + GPOS_ASSERT(NULL != popInsert->Ptabdesc()); + GPOS_ASSERT(NULL != popInsert->Ptabdesc()->MDId()); + + return IMDId::EmdidGPDBCtas == popInsert->Ptabdesc()->MDId()->MdidType(); +} + +// recursively checks if the given expression has logical DML operator +BOOL +CUtils::FHasLogicalDML(CMemoryPool *mp, CExpression *pexpr) +{ + GPOS_ASSERT(NULL != mp); + GPOS_ASSERT(NULL != pexpr); + + CDynamicPtrArray *exprsToCheck = + GPOS_NEW(mp) CDynamicPtrArray(mp); + exprsToCheck->Append(pexpr); + + while (exprsToCheck->Size() > 0) + { + CExpression *pexprCurrent = exprsToCheck->RemoveLast(); + + if (FLogicalDML(pexprCurrent->Pop())) + { + exprsToCheck->Release(); + return true; + } + + for (ULONG ul = 0; ul < pexprCurrent->Arity(); ++ul) + { + exprsToCheck->Append((*pexprCurrent)[ul]); + } + } + + exprsToCheck->Release(); + return false; +} + // return regular string from wide-character string CHAR * CUtils::CreateMultiByteCharStringFromWCString(CMemoryPool *mp, WCHAR *wsz) @@ -3440,6 +3490,7 @@ CUtils::PdrgpcrRemapAndCreate(CMemoryPool *mp, CColRefArray *colref_array, { // not found in hashmap, so create a new colref and add to hashmap pcrMapped = col_factory->PcrCopy(colref); + pcrMapped->MarkUsage(colref->GetUsage(true, true)); #ifdef GPOS_DEBUG BOOL result = diff --git a/src/backend/gporca/libgpopt/src/operators/CExpressionPreprocessor.cpp b/src/backend/gporca/libgpopt/src/operators/CExpressionPreprocessor.cpp index 01fcb7a97b13..ab38d15284c4 100644 --- a/src/backend/gporca/libgpopt/src/operators/CExpressionPreprocessor.cpp +++ b/src/backend/gporca/libgpopt/src/operators/CExpressionPreprocessor.cpp @@ -1542,7 +1542,7 @@ CExpressionPreprocessor::PexprAddEqualityPreds(CMemoryPool *mp, CExpression *pexprPred = NULL; COperator *pop = pexpr->Pop(); - if (CUtils::FLogicalDML(pop)) + if (CUtils::FHasLogicalDML(mp, pexpr)) { pexprPred = CUtils::PexprScalarConstBool(mp, true); } @@ -1943,7 +1943,7 @@ CExpressionPreprocessor::PexprPruneEmptySubtrees(CMemoryPool *mp, GPOS_ASSERT(NULL != pexpr); COperator *pop = pexpr->Pop(); - if (pop->FLogical() && !CUtils::FLogicalDML(pop)) + if (pop->FLogical() && !CUtils::FHasLogicalDML(mp, pexpr)) { // if maxcard = 0: return a const table get with same output columns and zero tuples if (0 == pexpr->DeriveMaxCard()) diff --git a/src/backend/gporca/libgpopt/src/operators/CLogicalDML.cpp b/src/backend/gporca/libgpopt/src/operators/CLogicalDML.cpp index dad8e05ab9db..febeda6cd7bf 100644 --- a/src/backend/gporca/libgpopt/src/operators/CLogicalDML.cpp +++ b/src/backend/gporca/libgpopt/src/operators/CLogicalDML.cpp @@ -34,8 +34,7 @@ const WCHAR CLogicalDML::m_rgwszDml[EdmlSentinel][10] = { // //--------------------------------------------------------------------------- CLogicalDML::CLogicalDML(CMemoryPool *mp) - : CLogical(mp), - m_ptabdesc(NULL), + : CLogicalReturning(mp), m_pdrgpcrSource(NULL), m_pbsModified(NULL), m_pcrAction(NULL), @@ -57,13 +56,13 @@ CLogicalDML::CLogicalDML(CMemoryPool *mp) //--------------------------------------------------------------------------- CLogicalDML::CLogicalDML(CMemoryPool *mp, EDMLOperator edmlop, CTableDescriptor *ptabdesc, - CColRefArray *pdrgpcrSource, CBitSet *pbsModified, + CColRefArray *pdrgpcrSource, + CColRefArray *pdrgpcrOutput, CBitSet *pbsModified, CColRef *pcrAction, CColRef *pcrCtid, CColRef *pcrSegmentId, CColRef *pcrTupleOid, CColRef *pcrTableOid) - : CLogical(mp), + : CLogicalReturning(mp, ptabdesc, pdrgpcrOutput), m_edmlop(edmlop), - m_ptabdesc(ptabdesc), m_pdrgpcrSource(pdrgpcrSource), m_pbsModified(pbsModified), m_pcrAction(pcrAction), @@ -73,7 +72,6 @@ CLogicalDML::CLogicalDML(CMemoryPool *mp, EDMLOperator edmlop, m_pcrTupleOid(pcrTupleOid) { GPOS_ASSERT(EdmlSentinel != edmlop); - GPOS_ASSERT(NULL != ptabdesc); GPOS_ASSERT(NULL != pdrgpcrSource); GPOS_ASSERT(NULL != pbsModified); GPOS_ASSERT(NULL != pcrAction); @@ -114,7 +112,6 @@ CLogicalDML::CLogicalDML(CMemoryPool *mp, EDMLOperator edmlop, //--------------------------------------------------------------------------- CLogicalDML::~CLogicalDML() { - CRefCount::SafeRelease(m_ptabdesc); CRefCount::SafeRelease(m_pdrgpcrSource); CRefCount::SafeRelease(m_pbsModified); } @@ -137,12 +134,12 @@ CLogicalDML::Matches(COperator *pop) const CLogicalDML *popDML = CLogicalDML::PopConvert(pop); - return m_pcrAction == popDML->PcrAction() && + return CLogicalReturning::MatchesReturning(popDML) && + m_pcrAction == popDML->PcrAction() && m_pcrTableOid == popDML->PcrTableOid() && m_pcrCtid == popDML->PcrCtid() && m_pcrSegmentId == popDML->PcrSegmentId() && m_pcrTupleOid == popDML->PcrTupleOid() && - m_ptabdesc->MDId()->Equals(popDML->Ptabdesc()->MDId()) && m_pdrgpcrSource->Equals(popDML->PdrgpcrSource()); } @@ -157,8 +154,8 @@ CLogicalDML::Matches(COperator *pop) const ULONG CLogicalDML::HashValue() const { - ULONG ulHash = gpos::CombineHashes(COperator::HashValue(), - m_ptabdesc->MDId()->HashValue()); + ULONG ulHash = CLogicalReturning::HashValue(); + ulHash = gpos::CombineHashes(ulHash, gpos::HashPtr(m_pcrAction)); ulHash = gpos::CombineHashes(ulHash, CUtils::UlHashColArray(m_pdrgpcrSource)); @@ -189,6 +186,9 @@ CLogicalDML::PopCopyWithRemappedColumns(CMemoryPool *mp, { CColRefArray *colref_array = CUtils::PdrgpcrRemap(mp, m_pdrgpcrSource, colref_mapping, must_exist); + + CColRefArray *pdrgpcrOutput = + CLogicalReturning::CopyRemappedColumns(mp, colref_mapping, must_exist); CColRef *pcrAction = CUtils::PcrRemap(m_pcrAction, colref_mapping, must_exist); @@ -225,9 +225,11 @@ CLogicalDML::PopCopyWithRemappedColumns(CMemoryPool *mp, m_ptabdesc->AddRef(); - return GPOS_NEW(mp) - CLogicalDML(mp, m_edmlop, m_ptabdesc, colref_array, m_pbsModified, - pcrAction, pcrCtid, pcrSegmentId, pcrTupleOid, pcrTableOid); + CLogicalDML *result = GPOS_NEW(mp) CLogicalDML( + mp, m_edmlop, m_ptabdesc, colref_array, pdrgpcrOutput, m_pbsModified, + pcrAction, pcrCtid, pcrSegmentId, pcrTupleOid, pcrTableOid); + + return result; } //--------------------------------------------------------------------------- @@ -245,6 +247,7 @@ CLogicalDML::DeriveOutputColumns(CMemoryPool *mp, { CColRefSet *pcrsOutput = GPOS_NEW(mp) CColRefSet(mp); pcrsOutput->Include(m_pdrgpcrSource); + pcrsOutput->Include(m_pdrgpcrOutput); if (NULL != m_pcrCtid) { GPOS_ASSERT(NULL != m_pcrSegmentId); @@ -276,27 +279,13 @@ CLogicalDML::DerivePropertyConstraint(CMemoryPool *mp, { CColRefSet *pcrsOutput = GPOS_NEW(mp) CColRefSet(mp); pcrsOutput->Include(m_pdrgpcrSource); + pcrsOutput->Include(m_pdrgpcrOutput); CPropConstraint *ppc = PpcDeriveConstraintRestrict(mp, exprhdl, pcrsOutput); pcrsOutput->Release(); return ppc; } -//--------------------------------------------------------------------------- -// @function: -// CLogicalDML::PkcDeriveKeys -// -// @doc: -// Derive key collection -// -//--------------------------------------------------------------------------- -CKeyCollection * -CLogicalDML::DeriveKeyCollection(CMemoryPool *, // mp - CExpressionHandle &exprhdl) const -{ - return PkcDeriveKeysPassThru(exprhdl, 0 /* ulChild */); -} - //--------------------------------------------------------------------------- // @function: // CLogicalDML::DeriveMaxCard @@ -386,7 +375,9 @@ CLogicalDML::OsPrint(IOstream &os) const m_pcrSegmentId->OsPrint(os); } - return os; + os << ", "; + + return CLogicalReturning::OsPrint(os); } // EOF diff --git a/src/backend/gporca/libgpopt/src/operators/CLogicalDelete.cpp b/src/backend/gporca/libgpopt/src/operators/CLogicalDelete.cpp index 10f126f7f925..e6fe179af29c 100644 --- a/src/backend/gporca/libgpopt/src/operators/CLogicalDelete.cpp +++ b/src/backend/gporca/libgpopt/src/operators/CLogicalDelete.cpp @@ -30,8 +30,7 @@ using namespace gpopt; // //--------------------------------------------------------------------------- CLogicalDelete::CLogicalDelete(CMemoryPool *mp) - : CLogical(mp), - m_ptabdesc(NULL), + : CLogicalReturning(mp), m_pdrgpcr(NULL), m_pcrCtid(NULL), m_pcrSegmentId(NULL), @@ -51,25 +50,34 @@ CLogicalDelete::CLogicalDelete(CMemoryPool *mp) CLogicalDelete::CLogicalDelete(CMemoryPool *mp, CTableDescriptor *ptabdesc, CColRefArray *colref_array, CColRef *pcrCtid, CColRef *pcrSegmentId, CColRef *pcrTableOid) - : CLogical(mp), - m_ptabdesc(ptabdesc), + : CLogicalReturning(mp, ptabdesc), m_pdrgpcr(colref_array), m_pcrCtid(pcrCtid), m_pcrSegmentId(pcrSegmentId), m_pcrTableOid(pcrTableOid) { - GPOS_ASSERT(NULL != ptabdesc); - GPOS_ASSERT(NULL != colref_array); - GPOS_ASSERT(NULL != pcrCtid); - GPOS_ASSERT(NULL != pcrSegmentId); + InitUsedColumns(); +} - m_pcrsLocalUsed->Include(m_pdrgpcr); - m_pcrsLocalUsed->Include(m_pcrCtid); - m_pcrsLocalUsed->Include(m_pcrSegmentId); - if (NULL != m_pcrTableOid) - { - m_pcrsLocalUsed->Include(m_pcrTableOid); - } +//--------------------------------------------------------------------------- +// @function: +// CLogicalDelete::CLogicalDelete +// +// @doc: +// Ctor +// +//--------------------------------------------------------------------------- +CLogicalDelete::CLogicalDelete(CMemoryPool *mp, CTableDescriptor *ptabdesc, + CColRefArray *colref_array, CColRef *pcrCtid, + CColRef *pcrSegmentId, CColRef *pcrTableOid, + CColRefArray *pdrgpcrOutput) + : CLogicalReturning(mp, ptabdesc, pdrgpcrOutput), + m_pdrgpcr(colref_array), + m_pcrCtid(pcrCtid), + m_pcrSegmentId(pcrSegmentId), + m_pcrTableOid(pcrTableOid) +{ + InitUsedColumns(); } //--------------------------------------------------------------------------- @@ -82,10 +90,33 @@ CLogicalDelete::CLogicalDelete(CMemoryPool *mp, CTableDescriptor *ptabdesc, //--------------------------------------------------------------------------- CLogicalDelete::~CLogicalDelete() { - CRefCount::SafeRelease(m_ptabdesc); CRefCount::SafeRelease(m_pdrgpcr); } +//--------------------------------------------------------------------------- +// @function: +// CLogicalDelete::InitUsedColumns +// +// @doc: +// Initialize locally used columns +// +//--------------------------------------------------------------------------- +void +CLogicalDelete::InitUsedColumns() +{ + GPOS_ASSERT(NULL != m_pdrgpcr); + GPOS_ASSERT(NULL != m_pcrCtid); + GPOS_ASSERT(NULL != m_pcrSegmentId); + + m_pcrsLocalUsed->Include(m_pdrgpcr); + m_pcrsLocalUsed->Include(m_pcrCtid); + m_pcrsLocalUsed->Include(m_pcrSegmentId); + if (NULL != m_pcrTableOid) + { + m_pcrsLocalUsed->Include(m_pcrTableOid); + } +} + //--------------------------------------------------------------------------- // @function: // CLogicalDelete::Matches @@ -104,10 +135,10 @@ CLogicalDelete::Matches(COperator *pop) const CLogicalDelete *popDelete = CLogicalDelete::PopConvert(pop); - return m_pcrCtid == popDelete->PcrCtid() && + return CLogicalReturning::MatchesReturning(popDelete) && + m_pcrCtid == popDelete->PcrCtid() && m_pcrSegmentId == popDelete->PcrSegmentId() && m_pcrTableOid == popDelete->PcrTableOid() && - m_ptabdesc->MDId()->Equals(popDelete->Ptabdesc()->MDId()) && m_pdrgpcr->Equals(popDelete->Pdrgpcr()); } @@ -122,8 +153,8 @@ CLogicalDelete::Matches(COperator *pop) const ULONG CLogicalDelete::HashValue() const { - ULONG ulHash = gpos::CombineHashes(COperator::HashValue(), - m_ptabdesc->MDId()->HashValue()); + ULONG ulHash = CLogicalReturning::HashValue(); + ulHash = gpos::CombineHashes(ulHash, CUtils::UlHashColArray(m_pdrgpcr)); ulHash = gpos::CombineHashes(ulHash, gpos::HashPtr(m_pcrCtid)); ulHash = @@ -148,6 +179,10 @@ CLogicalDelete::PopCopyWithRemappedColumns(CMemoryPool *mp, { CColRefArray *colref_array = CUtils::PdrgpcrRemap(mp, m_pdrgpcr, colref_mapping, must_exist); + + CColRefArray *pdrgpcrOutput = + CLogicalReturning::CopyRemappedColumns(mp, colref_mapping, must_exist); + CColRef *pcrCtid = CUtils::PcrRemap(m_pcrCtid, colref_mapping, must_exist); CColRef *pcrSegmentId = CUtils::PcrRemap(m_pcrSegmentId, colref_mapping, must_exist); @@ -158,9 +193,13 @@ CLogicalDelete::PopCopyWithRemappedColumns(CMemoryPool *mp, pcrTableOid = CUtils::PcrRemap(m_pcrTableOid, colref_mapping, must_exist); } + m_ptabdesc->AddRef(); - return GPOS_NEW(mp) CLogicalDelete(mp, m_ptabdesc, colref_array, pcrCtid, - pcrSegmentId, pcrTableOid); + CLogicalDelete *result = + GPOS_NEW(mp) CLogicalDelete(mp, m_ptabdesc, colref_array, pcrCtid, + pcrSegmentId, pcrTableOid, pdrgpcrOutput); + + return result; } //--------------------------------------------------------------------------- @@ -178,22 +217,9 @@ CLogicalDelete::DeriveOutputColumns(CMemoryPool *mp, { CColRefSet *pcrsOutput = GPOS_NEW(mp) CColRefSet(mp); pcrsOutput->Include(m_pdrgpcr); - return pcrsOutput; -} + pcrsOutput->Include(m_pdrgpcrOutput); -//--------------------------------------------------------------------------- -// @function: -// CLogicalDelete::PkcDeriveKeys -// -// @doc: -// Derive key collection -// -//--------------------------------------------------------------------------- -CKeyCollection * -CLogicalDelete::DeriveKeyCollection(CMemoryPool *, // mp - CExpressionHandle &exprhdl) const -{ - return PkcDeriveKeysPassThru(exprhdl, 0 /* ulChild */); + return pcrsOutput; } //--------------------------------------------------------------------------- @@ -276,7 +302,7 @@ CLogicalDelete::OsPrint(IOstream &os) const os << ", "; } - return os; + return CLogicalReturning::OsPrint(os); } // EOF diff --git a/src/backend/gporca/libgpopt/src/operators/CLogicalInsert.cpp b/src/backend/gporca/libgpopt/src/operators/CLogicalInsert.cpp index 15e948216263..9d145e7218f3 100644 --- a/src/backend/gporca/libgpopt/src/operators/CLogicalInsert.cpp +++ b/src/backend/gporca/libgpopt/src/operators/CLogicalInsert.cpp @@ -30,7 +30,8 @@ using namespace gpopt; // //--------------------------------------------------------------------------- CLogicalInsert::CLogicalInsert(CMemoryPool *mp) - : CLogical(mp), m_ptabdesc(NULL), m_pdrgpcrSource(NULL) + : CLogicalReturning(mp), m_pdrgpcrSource(NULL) + { m_fPattern = true; } @@ -45,13 +46,28 @@ CLogicalInsert::CLogicalInsert(CMemoryPool *mp) //--------------------------------------------------------------------------- CLogicalInsert::CLogicalInsert(CMemoryPool *mp, CTableDescriptor *ptabdesc, CColRefArray *pdrgpcrSource) - : CLogical(mp), m_ptabdesc(ptabdesc), m_pdrgpcrSource(pdrgpcrSource) + : CLogicalReturning(mp, ptabdesc), m_pdrgpcrSource(pdrgpcrSource) { - GPOS_ASSERT(NULL != ptabdesc); - GPOS_ASSERT(NULL != pdrgpcrSource); + InitUsedColumns(); +} - m_pcrsLocalUsed->Include(m_pdrgpcrSource); +//--------------------------------------------------------------------------- +// @function: +// CLogicalInsert::CLogicalInsert +// +// @doc: +// Ctor +// +//--------------------------------------------------------------------------- +CLogicalInsert::CLogicalInsert(CMemoryPool *mp, CTableDescriptor *ptabdesc, + CColRefArray *pdrgpcrSource, + CColRefArray *pdrgpcrOutput) + : CLogicalReturning(mp, ptabdesc, pdrgpcrOutput), + m_pdrgpcrSource(pdrgpcrSource) + +{ + InitUsedColumns(); } //--------------------------------------------------------------------------- @@ -64,10 +80,25 @@ CLogicalInsert::CLogicalInsert(CMemoryPool *mp, CTableDescriptor *ptabdesc, //--------------------------------------------------------------------------- CLogicalInsert::~CLogicalInsert() { - CRefCount::SafeRelease(m_ptabdesc); CRefCount::SafeRelease(m_pdrgpcrSource); } +//--------------------------------------------------------------------------- +// @function: +// CLogicalInsert::InitUsedColumns +// +// @doc: +// Initialize locally used columns +// +//--------------------------------------------------------------------------- +void +CLogicalInsert::InitUsedColumns() +{ + GPOS_ASSERT(NULL != m_pdrgpcrSource); + + m_pcrsLocalUsed->Include(m_pdrgpcrSource); +} + //--------------------------------------------------------------------------- // @function: // CLogicalInsert::Matches @@ -86,7 +117,7 @@ CLogicalInsert::Matches(COperator *pop) const CLogicalInsert *popInsert = CLogicalInsert::PopConvert(pop); - return m_ptabdesc->MDId()->Equals(popInsert->Ptabdesc()->MDId()) && + return CLogicalReturning::MatchesReturning(popInsert) && m_pdrgpcrSource->Equals(popInsert->PdrgpcrSource()); } @@ -101,8 +132,8 @@ CLogicalInsert::Matches(COperator *pop) const ULONG CLogicalInsert::HashValue() const { - ULONG ulHash = gpos::CombineHashes(COperator::HashValue(), - m_ptabdesc->MDId()->HashValue()); + ULONG ulHash = CLogicalReturning::HashValue(); + ulHash = gpos::CombineHashes(ulHash, CUtils::UlHashColArray(m_pdrgpcrSource)); @@ -124,9 +155,15 @@ CLogicalInsert::PopCopyWithRemappedColumns(CMemoryPool *mp, { CColRefArray *colref_array = CUtils::PdrgpcrRemap(mp, m_pdrgpcrSource, colref_mapping, must_exist); + + CColRefArray *pdrgpcrOutput = + CLogicalReturning::CopyRemappedColumns(mp, colref_mapping, must_exist); m_ptabdesc->AddRef(); - return GPOS_NEW(mp) CLogicalInsert(mp, m_ptabdesc, colref_array); + CLogicalInsert *result = GPOS_NEW(mp) + CLogicalInsert(mp, m_ptabdesc, colref_array, pdrgpcrOutput); + + return result; } //--------------------------------------------------------------------------- @@ -144,22 +181,9 @@ CLogicalInsert::DeriveOutputColumns(CMemoryPool *mp, { CColRefSet *pcrsOutput = GPOS_NEW(mp) CColRefSet(mp); pcrsOutput->Include(m_pdrgpcrSource); - return pcrsOutput; -} + pcrsOutput->Include(m_pdrgpcrOutput); -//--------------------------------------------------------------------------- -// @function: -// CLogicalInsert::PkcDeriveKeys -// -// @doc: -// Derive key collection -// -//--------------------------------------------------------------------------- -CKeyCollection * -CLogicalInsert::DeriveKeyCollection(CMemoryPool *, // mp - CExpressionHandle &exprhdl) const -{ - return PkcDeriveKeysPassThru(exprhdl, 0 /* ulChild */); + return pcrsOutput; } //--------------------------------------------------------------------------- @@ -231,9 +255,9 @@ CLogicalInsert::OsPrint(IOstream &os) const m_ptabdesc->Name().OsPrint(os); os << "), Source Columns: ["; CUtils::OsPrintDrgPcr(os, m_pdrgpcrSource); - os << "]"; + os << "], "; - return os; + return CLogicalReturning::OsPrint(os); } // EOF diff --git a/src/backend/gporca/libgpopt/src/operators/CLogicalReturning.cpp b/src/backend/gporca/libgpopt/src/operators/CLogicalReturning.cpp new file mode 100644 index 000000000000..847e62a3a3f1 --- /dev/null +++ b/src/backend/gporca/libgpopt/src/operators/CLogicalReturning.cpp @@ -0,0 +1,203 @@ +//--------------------------------------------------------------------------- +// Copyright (c) 2025 Greengage Community +// +// @filename: +// CLogicalReturning.cpp +// +// @doc: +//--------------------------------------------------------------------------- + +#include "gpopt/operators/CLogicalReturning.h" + +#include "gpos/base.h" + +#include "gpopt/xforms/CXformUtils.h" +#include "naucrates/statistics/CProjectStatsProcessor.h" + +using namespace gpopt; + +CLogicalReturning::CLogicalReturning(CMemoryPool *mp) + : CLogical(mp), m_ptabdesc(NULL), m_pdrgpcrOutput(NULL) +{ +} + +//--------------------------------------------------------------------------- +// @function: +// CLogicalReturning::CLogicalReturning +// +// @doc: +// Ctor +// +//--------------------------------------------------------------------------- +CLogicalReturning::CLogicalReturning(CMemoryPool *mp, + CTableDescriptor *ptabdesc) + : CLogical(mp), m_ptabdesc(ptabdesc) +{ + GPOS_ASSERT(NULL != ptabdesc); + + m_pdrgpcrOutput = + PdrgpcrCreateMapping(mp, ptabdesc->Pdrgpcoldesc(), UlOpId()); + + m_pcrsLocalUsed->Include(m_pdrgpcrOutput); +} + +//--------------------------------------------------------------------------- +// @function: +// CCLogicalReturning::CLogicalReturning +// +// @doc: +// Ctor +// +//--------------------------------------------------------------------------- +CLogicalReturning::CLogicalReturning(CMemoryPool *mp, + CTableDescriptor *ptabdesc, + CColRefArray *pdrgpcrOutput) + : CLogical(mp), m_ptabdesc(ptabdesc), m_pdrgpcrOutput(pdrgpcrOutput) +{ + GPOS_ASSERT(NULL != ptabdesc); + GPOS_ASSERT(NULL != pdrgpcrOutput); + + m_pcrsLocalUsed->Include(m_pdrgpcrOutput); +} + +//--------------------------------------------------------------------------- +// @function: +// CLogicalReturning::~CLogicalReturning +// +// @doc: +// Dtor +// +//--------------------------------------------------------------------------- +CLogicalReturning::~CLogicalReturning() +{ + CRefCount::SafeRelease(m_ptabdesc); + CRefCount::SafeRelease(m_pdrgpcrOutput); +} + +//--------------------------------------------------------------------------- +// @function: +// CLogicalReturning::Matches +// +// @doc: +// Match function +// +//--------------------------------------------------------------------------- +BOOL +CLogicalReturning::MatchesReturning(CLogicalReturning *popReturning) const +{ + return m_ptabdesc->MDId()->Equals(popReturning->Ptabdesc()->MDId()) && + m_pdrgpcrOutput->Equals(popReturning->PdrgpcrOutput()); +} + +//--------------------------------------------------------------------------- +// @function: +// CLogicalReturning::HashValue +// +// @doc: +// Hash function +// +//--------------------------------------------------------------------------- +ULONG +CLogicalReturning::HashValue() const +{ + ULONG ulHash = gpos::CombineHashes(COperator::HashValue(), + m_ptabdesc->MDId()->HashValue()); + + ulHash = + gpos::CombineHashes(ulHash, CUtils::UlHashColArray(m_pdrgpcrOutput)); + + return ulHash; +} + +//--------------------------------------------------------------------------- +// @function: +// CLogicalReturning::CopyRemappedColumns +// +// @doc: +// return a copy of output columns +// +//--------------------------------------------------------------------------- +CColRefArray * +CLogicalReturning::CopyRemappedColumns(CMemoryPool *mp, + UlongToColRefMap *colref_mapping, + BOOL must_exist) +{ + CColRefArray *pdrgpcrOutput = NULL; + if (must_exist) + { + pdrgpcrOutput = + CUtils::PdrgpcrRemapAndCreate(mp, m_pdrgpcrOutput, colref_mapping); + } + else + { + pdrgpcrOutput = CUtils::PdrgpcrRemap(mp, m_pdrgpcrOutput, + colref_mapping, must_exist); + } + + return pdrgpcrOutput; +} + +//--------------------------------------------------------------------------- +// @function: +// CLogicalReturning::DeriveKeyCollection +// +// @doc: +// Derive key collection +// +//--------------------------------------------------------------------------- +CKeyCollection * +CLogicalReturning::DeriveKeyCollection(CMemoryPool *mp, + CExpressionHandle & // exprhdl +) const +{ + const CBitSetArray *pdrgpbs = m_ptabdesc->PdrgpbsKeys(); + + return CLogical::PkcKeysBaseTable(mp, pdrgpbs, m_pdrgpcrOutput); +} + +//--------------------------------------------------------------------------- +// @function: +// CLogicalReturning::OsPrint +// +// @doc: +// debug print +// +//--------------------------------------------------------------------------- +IOstream & +CLogicalReturning::OsPrint(IOstream &os) const +{ + os << "Output Columns: ["; + CUtils::OsPrintDrgPcr(os, m_pdrgpcrOutput); + os << "] Key sets: {"; + + const ULONG ulColumns = m_pdrgpcrOutput->Size(); + const CBitSetArray *pdrgpbsKeys = m_ptabdesc->PdrgpbsKeys(); + for (ULONG ul = 0; ul < pdrgpbsKeys->Size(); ul++) + { + CBitSet *pbs = (*pdrgpbsKeys)[ul]; + if (0 < ul) + { + os << ", "; + } + os << "["; + ULONG ulPrintedKeys = 0; + for (ULONG ulKey = 0; ulKey < ulColumns; ulKey++) + { + if (pbs->Get(ulKey)) + { + if (0 < ulPrintedKeys) + { + os << ","; + } + os << ulKey; + ulPrintedKeys++; + } + } + os << "]"; + } + os << "}"; + + return os; +} + +// EOF diff --git a/src/backend/gporca/libgpopt/src/operators/CLogicalUpdate.cpp b/src/backend/gporca/libgpopt/src/operators/CLogicalUpdate.cpp index 9fb4cbbb57bb..9bb877c94557 100644 --- a/src/backend/gporca/libgpopt/src/operators/CLogicalUpdate.cpp +++ b/src/backend/gporca/libgpopt/src/operators/CLogicalUpdate.cpp @@ -30,8 +30,7 @@ using namespace gpopt; // //--------------------------------------------------------------------------- CLogicalUpdate::CLogicalUpdate(CMemoryPool *mp) - : CLogical(mp), - m_ptabdesc(NULL), + : CLogicalReturning(mp), m_pdrgpcrDelete(NULL), m_pdrgpcrInsert(NULL), m_pcrCtid(NULL), @@ -55,8 +54,7 @@ CLogicalUpdate::CLogicalUpdate(CMemoryPool *mp, CTableDescriptor *ptabdesc, CColRefArray *pdrgpcrInsert, CColRef *pcrCtid, CColRef *pcrSegmentId, CColRef *pcrTupleOid, CColRef *pcrTableOid) - : CLogical(mp), - m_ptabdesc(ptabdesc), + : CLogicalReturning(mp, ptabdesc), m_pdrgpcrDelete(pdrgpcrDelete), m_pdrgpcrInsert(pdrgpcrInsert), m_pcrCtid(pcrCtid), @@ -64,27 +62,36 @@ CLogicalUpdate::CLogicalUpdate(CMemoryPool *mp, CTableDescriptor *ptabdesc, m_pcrTupleOid(pcrTupleOid), m_pcrTableOid(pcrTableOid) { - GPOS_ASSERT(NULL != ptabdesc); - GPOS_ASSERT(NULL != pdrgpcrDelete); - GPOS_ASSERT(NULL != pdrgpcrInsert); GPOS_ASSERT(pdrgpcrDelete->Size() == pdrgpcrInsert->Size()); - GPOS_ASSERT(NULL != pcrCtid); - GPOS_ASSERT(NULL != pcrSegmentId); - m_pcrsLocalUsed->Include(m_pdrgpcrDelete); - m_pcrsLocalUsed->Include(m_pdrgpcrInsert); - m_pcrsLocalUsed->Include(m_pcrCtid); - m_pcrsLocalUsed->Include(m_pcrSegmentId); + InitUsedColumns(); +} - if (NULL != m_pcrTupleOid) - { - m_pcrsLocalUsed->Include(m_pcrTupleOid); - } +//--------------------------------------------------------------------------- +// @function: +// CLogicalUpdate::CLogicalUpdate +// +// @doc: +// Ctor +// +//--------------------------------------------------------------------------- +CLogicalUpdate::CLogicalUpdate(CMemoryPool *mp, CTableDescriptor *ptabdesc, + CColRefArray *pdrgpcrDelete, + CColRefArray *pdrgpcrInsert, CColRef *pcrCtid, + CColRef *pcrSegmentId, CColRef *pcrTupleOid, + CColRef *pcrTableOid, + CColRefArray *pdrgpcrOutput) + : CLogicalReturning(mp, ptabdesc, pdrgpcrOutput), + m_pdrgpcrDelete(pdrgpcrDelete), + m_pdrgpcrInsert(pdrgpcrInsert), + m_pcrCtid(pcrCtid), + m_pcrSegmentId(pcrSegmentId), + m_pcrTupleOid(pcrTupleOid), + m_pcrTableOid(pcrTableOid) +{ + GPOS_ASSERT(pdrgpcrDelete->Size() == pdrgpcrInsert->Size()); - if (NULL != m_pcrTableOid) - { - m_pcrsLocalUsed->Include(m_pcrTableOid); - } + InitUsedColumns(); } //--------------------------------------------------------------------------- @@ -97,11 +104,42 @@ CLogicalUpdate::CLogicalUpdate(CMemoryPool *mp, CTableDescriptor *ptabdesc, //--------------------------------------------------------------------------- CLogicalUpdate::~CLogicalUpdate() { - CRefCount::SafeRelease(m_ptabdesc); CRefCount::SafeRelease(m_pdrgpcrDelete); CRefCount::SafeRelease(m_pdrgpcrInsert); } +//--------------------------------------------------------------------------- +// @function: +// CLogicalUpdate::InitUsedColumns +// +// @doc: +// Initialize locally used columns +// +//--------------------------------------------------------------------------- +void +CLogicalUpdate::InitUsedColumns() +{ + GPOS_ASSERT(NULL != m_pdrgpcrDelete); + GPOS_ASSERT(NULL != m_pdrgpcrInsert); + GPOS_ASSERT(NULL != m_pcrCtid); + GPOS_ASSERT(NULL != m_pcrSegmentId); + + m_pcrsLocalUsed->Include(m_pdrgpcrDelete); + m_pcrsLocalUsed->Include(m_pdrgpcrInsert); + m_pcrsLocalUsed->Include(m_pcrCtid); + m_pcrsLocalUsed->Include(m_pcrSegmentId); + + if (NULL != m_pcrTupleOid) + { + m_pcrsLocalUsed->Include(m_pcrTupleOid); + } + + if (NULL != m_pcrTableOid) + { + m_pcrsLocalUsed->Include(m_pcrTableOid); + } +} + //--------------------------------------------------------------------------- // @function: // CLogicalUpdate::Matches @@ -120,11 +158,11 @@ CLogicalUpdate::Matches(COperator *pop) const CLogicalUpdate *popUpdate = CLogicalUpdate::PopConvert(pop); - return m_pcrCtid == popUpdate->PcrCtid() && + return CLogicalReturning::MatchesReturning(popUpdate) && + m_pcrCtid == popUpdate->PcrCtid() && m_pcrSegmentId == popUpdate->PcrSegmentId() && m_pcrTupleOid == popUpdate->PcrTupleOid() && m_pcrTableOid == popUpdate->PcrTableOid() && - m_ptabdesc->MDId()->Equals(popUpdate->Ptabdesc()->MDId()) && m_pdrgpcrDelete->Equals(popUpdate->PdrgpcrDelete()) && m_pdrgpcrInsert->Equals(popUpdate->PdrgpcrInsert()); } @@ -140,8 +178,8 @@ CLogicalUpdate::Matches(COperator *pop) const ULONG CLogicalUpdate::HashValue() const { - ULONG ulHash = gpos::CombineHashes(COperator::HashValue(), - m_ptabdesc->MDId()->HashValue()); + ULONG ulHash = CLogicalReturning::HashValue(); + ulHash = gpos::CombineHashes(ulHash, CUtils::UlHashColArray(m_pdrgpcrDelete)); ulHash = @@ -171,6 +209,10 @@ CLogicalUpdate::PopCopyWithRemappedColumns(CMemoryPool *mp, CUtils::PdrgpcrRemap(mp, m_pdrgpcrDelete, colref_mapping, must_exist); CColRefArray *pdrgpcrInsert = CUtils::PdrgpcrRemap(mp, m_pdrgpcrInsert, colref_mapping, must_exist); + + CColRefArray *pdrgpcrOutput = + CLogicalReturning::CopyRemappedColumns(mp, colref_mapping, must_exist); + CColRef *pcrCtid = CUtils::PcrRemap(m_pcrCtid, colref_mapping, must_exist); CColRef *pcrSegmentId = CUtils::PcrRemap(m_pcrSegmentId, colref_mapping, must_exist); @@ -189,9 +231,11 @@ CLogicalUpdate::PopCopyWithRemappedColumns(CMemoryPool *mp, pcrTableOid = CUtils::PcrRemap(m_pcrTableOid, colref_mapping, must_exist); } - return GPOS_NEW(mp) + CLogicalUpdate *result = GPOS_NEW(mp) CLogicalUpdate(mp, m_ptabdesc, pdrgpcrDelete, pdrgpcrInsert, pcrCtid, - pcrSegmentId, pcrTupleOid, pcrTableOid); + pcrSegmentId, pcrTupleOid, pcrTableOid, pdrgpcrOutput); + + return result; } //--------------------------------------------------------------------------- @@ -209,6 +253,7 @@ CLogicalUpdate::DeriveOutputColumns(CMemoryPool *mp, { CColRefSet *pcrsOutput = GPOS_NEW(mp) CColRefSet(mp); pcrsOutput->Include(m_pdrgpcrInsert); + pcrsOutput->Include(m_pdrgpcrOutput); pcrsOutput->Include(m_pcrCtid); pcrsOutput->Include(m_pcrSegmentId); @@ -223,21 +268,6 @@ CLogicalUpdate::DeriveOutputColumns(CMemoryPool *mp, return pcrsOutput; } -//--------------------------------------------------------------------------- -// @function: -// CLogicalUpdate::PkcDeriveKeys -// -// @doc: -// Derive key collection -// -//--------------------------------------------------------------------------- -CKeyCollection * -CLogicalUpdate::DeriveKeyCollection(CMemoryPool *, // mp - CExpressionHandle &exprhdl) const -{ - return PkcDeriveKeysPassThru(exprhdl, 0 /* ulChild */); -} - //--------------------------------------------------------------------------- // @function: // CLogicalUpdate::DeriveMaxCard @@ -320,7 +350,7 @@ CLogicalUpdate::OsPrint(IOstream &os) const os << ", "; } - return os; + return CLogicalReturning::OsPrint(os); } // EOF diff --git a/src/backend/gporca/libgpopt/src/operators/CNormalizer.cpp b/src/backend/gporca/libgpopt/src/operators/CNormalizer.cpp index 41bc99a79898..385fe3b2afa2 100644 --- a/src/backend/gporca/libgpopt/src/operators/CNormalizer.cpp +++ b/src/backend/gporca/libgpopt/src/operators/CNormalizer.cpp @@ -1499,7 +1499,8 @@ CNormalizer::FLocalColsSubsetOfInputCols(CMemoryPool *mp, CExpression *pexpr) BOOL fValid = true; if (pexpr->Pop()->FLogical()) { - if (0 == exprhdl.UlNonScalarChildren()) + if (0 == exprhdl.UlNonScalarChildren() || + CUtils::FLogicalDML(pexpr->Pop())) { return true; } diff --git a/src/backend/gporca/libgpopt/src/operators/CPhysicalDML.cpp b/src/backend/gporca/libgpopt/src/operators/CPhysicalDML.cpp index 2694618be87a..ea5bd2617a5e 100644 --- a/src/backend/gporca/libgpopt/src/operators/CPhysicalDML.cpp +++ b/src/backend/gporca/libgpopt/src/operators/CPhysicalDML.cpp @@ -37,7 +37,8 @@ using namespace gpopt; //--------------------------------------------------------------------------- CPhysicalDML::CPhysicalDML(CMemoryPool *mp, CLogicalDML::EDMLOperator edmlop, CTableDescriptor *ptabdesc, - CColRefArray *pdrgpcrSource, CBitSet *pbsModified, + CColRefArray *pdrgpcrSource, + CColRefArray *pdrgpcrOutput, CBitSet *pbsModified, CColRef *pcrAction, CColRef *pcrCtid, CColRef *pcrSegmentId, CColRef *pcrTupleOid, CColRef *pcrTableOid) @@ -45,6 +46,7 @@ CPhysicalDML::CPhysicalDML(CMemoryPool *mp, CLogicalDML::EDMLOperator edmlop, m_edmlop(edmlop), m_ptabdesc(ptabdesc), m_pdrgpcrSource(pdrgpcrSource), + m_pdrgpcrOutput(pdrgpcrOutput), m_pbsModified(pbsModified), m_pcrAction(pcrAction), m_pcrTableOid(pcrTableOid), @@ -53,11 +55,14 @@ CPhysicalDML::CPhysicalDML(CMemoryPool *mp, CLogicalDML::EDMLOperator edmlop, m_pcrTupleOid(pcrTupleOid), m_pds(NULL), m_pos(NULL), - m_pcrsRequiredLocal(NULL) + m_pcrsRequiredLocal(NULL), + m_isCTAS(IMDId::EmdidGPDBCtas == ptabdesc->MDId()->MdidType()), + m_hasTriggers(CXformUtils::FTriggersExist(edmlop, ptabdesc)) { GPOS_ASSERT(CLogicalDML::EdmlSentinel != edmlop); GPOS_ASSERT(NULL != ptabdesc); GPOS_ASSERT(NULL != pdrgpcrSource); + GPOS_ASSERT(NULL != pdrgpcrOutput); GPOS_ASSERT(NULL != pbsModified); GPOS_ASSERT(NULL != pcrAction); GPOS_ASSERT_IMP( @@ -67,6 +72,17 @@ CPhysicalDML::CPhysicalDML(CMemoryPool *mp, CLogicalDML::EDMLOperator edmlop, m_pds = CPhysical::PdsCompute(m_mp, m_ptabdesc, pdrgpcrSource, pcrSegmentId); + if (ptabdesc->ConvertHashToRandom()) + { + // Treating a hash distributed table as random during planning + m_pdsOutput = GPOS_NEW(m_mp) CDistributionSpecRandom(); + } + else + { + m_pdsOutput = CPhysical::PdsCompute(m_mp, ptabdesc, pdrgpcrOutput, + NULL /* gp_segment_id */); + } + if (CDistributionSpec::EdtHashed == m_pds->Edt() && ptabdesc->ConvertHashToRandom()) { @@ -150,9 +166,11 @@ CPhysicalDML::~CPhysicalDML() { m_ptabdesc->Release(); m_pdrgpcrSource->Release(); + m_pdrgpcrOutput->Release(); m_pbsModified->Release(); m_pds->Release(); m_pos->Release(); + m_pdsOutput->Release(); m_pcrsRequiredLocal->Release(); } @@ -251,7 +269,11 @@ CPhysicalDML::PcrsRequired(CMemoryPool *mp, "Required properties can only be computed on the relational child"); CColRefSet *pcrs = GPOS_NEW(mp) CColRefSet(mp, *m_pcrsRequiredLocal); - pcrs->Union(pcrsRequired); + + if (m_isCTAS || m_hasTriggers) + { + pcrs->Union(pcrsRequired); + } return pcrs; } @@ -379,7 +401,25 @@ CPhysicalDML::FProvidesReqdCols(CExpressionHandle &exprhdl, ULONG // ulOptReq ) const { - return FUnaryProvidesReqdCols(exprhdl, pcrsRequired); + GPOS_ASSERT(NULL != pcrsRequired); + + if (m_isCTAS) + { + return FUnaryProvidesReqdCols(exprhdl, pcrsRequired); + } + + CColRefSet *pcrs = GPOS_NEW(m_mp) CColRefSet(m_mp); + pcrs->Include(m_pdrgpcrOutput); + + if (m_hasTriggers) + { + pcrs->Include(m_pdrgpcrSource); + } + + BOOL result = pcrs->ContainsAll(pcrsRequired); + pcrs->Release(); + + return result; } //--------------------------------------------------------------------------- @@ -394,7 +434,13 @@ CDistributionSpec * CPhysicalDML::PdsDerive(CMemoryPool *, //mp, CExpressionHandle &exprhdl) const { - return PdsDerivePassThruOuter(exprhdl); + if (m_isCTAS) + { + return PdsDerivePassThruOuter(exprhdl); + } + + m_pdsOutput->AddRef(); + return m_pdsOutput; } //--------------------------------------------------------------------------- @@ -428,6 +474,8 @@ CPhysicalDML::HashValue() const ulHash = gpos::CombineHashes(ulHash, gpos::HashPtr(m_pcrTableOid)); ulHash = gpos::CombineHashes(ulHash, CUtils::UlHashColArray(m_pdrgpcrSource)); + ulHash = + gpos::CombineHashes(ulHash, CUtils::UlHashColArray(m_pdrgpcrOutput)); if (CLogicalDML::EdmlDelete == m_edmlop || CLogicalDML::EdmlUpdate == m_edmlop) @@ -461,7 +509,8 @@ CPhysicalDML::Matches(COperator *pop) const m_pcrSegmentId == popDML->PcrSegmentId() && m_pcrTupleOid == popDML->PcrTupleOid() && m_ptabdesc->MDId()->Equals(popDML->Ptabdesc()->MDId()) && - m_pdrgpcrSource->Equals(popDML->PdrgpcrSource()); + m_pdrgpcrSource->Equals(popDML->PdrgpcrSource()) && + m_pdrgpcrOutput->Equals(popDML->PdrgpcrOutput()); } return false; @@ -623,6 +672,37 @@ CPhysicalDML::OsPrint(IOstream &os) const m_pcrSegmentId->OsPrint(os); } + os << ", Output Columns: ["; + CUtils::OsPrintDrgPcr(os, m_pdrgpcrOutput); + os << "] Key sets: {"; + + const ULONG ulColumns = m_pdrgpcrOutput->Size(); + const CBitSetArray *pdrgpbsKeys = m_ptabdesc->PdrgpbsKeys(); + for (ULONG ul = 0; ul < pdrgpbsKeys->Size(); ul++) + { + CBitSet *pbs = (*pdrgpbsKeys)[ul]; + if (0 < ul) + { + os << ", "; + } + os << "["; + ULONG ulPrintedKeys = 0; + for (ULONG ulKey = 0; ulKey < ulColumns; ulKey++) + { + if (pbs->Get(ulKey)) + { + if (0 < ulPrintedKeys) + { + os << ","; + } + os << ulKey; + ulPrintedKeys++; + } + } + os << "]"; + } + os << "}"; + return os; } diff --git a/src/backend/gporca/libgpopt/src/operators/Makefile b/src/backend/gporca/libgpopt/src/operators/Makefile index 12c645bc7ccf..123945392c36 100644 --- a/src/backend/gporca/libgpopt/src/operators/Makefile +++ b/src/backend/gporca/libgpopt/src/operators/Makefile @@ -66,6 +66,7 @@ OBJS = CExpression.o \ CLogicalNAryJoin.o \ CLogicalPartitionSelector.o \ CLogicalProject.o \ + CLogicalReturning.o \ CLogicalRightOuterJoin.o \ CLogicalRowTrigger.o \ CLogicalSelect.o \ diff --git a/src/backend/gporca/libgpopt/src/translate/CTranslatorDXLToExpr.cpp b/src/backend/gporca/libgpopt/src/translate/CTranslatorDXLToExpr.cpp index e7cf86eab727..42a3e32d4b4e 100644 --- a/src/backend/gporca/libgpopt/src/translate/CTranslatorDXLToExpr.cpp +++ b/src/backend/gporca/libgpopt/src/translate/CTranslatorDXLToExpr.cpp @@ -272,9 +272,6 @@ CTranslatorDXLToExpr::Pexpr(const CDXLNode *dxlnode, m_pdrgpulOutputColRefs = GPOS_NEW(m_mp) ULongPtrArray(m_mp); m_pdrgpmdname = GPOS_NEW(m_mp) CMDNameArray(m_mp); - BOOL fGenerateRequiredColumns = - COperator::EopLogicalUpdate != pexpr->Pop()->Eopid(); - const ULONG length = query_output_dxlnode_array->Size(); for (ULONG ul = 0; ul < length; ul++) { @@ -292,18 +289,15 @@ CTranslatorDXLToExpr::Pexpr(const CDXLNode *dxlnode, // get its column reference from the hash map const CColRef *colref = LookupColRef(m_phmulcr, colid); - if (fGenerateRequiredColumns) - { - const ULONG ulColRefId = colref->Id(); - ULONG *pulCopy = GPOS_NEW(m_mp) ULONG(ulColRefId); - // add to the array of output column reference ids - m_pdrgpulOutputColRefs->Append(pulCopy); - - // get the column names and add it to the array of output column names - CMDName *mdname = - GPOS_NEW(m_mp) CMDName(m_mp, dxl_colref->MdName()->GetMDName()); - m_pdrgpmdname->Append(mdname); - } + const ULONG ulColRefId = colref->Id(); + ULONG *pulCopy = GPOS_NEW(m_mp) ULONG(ulColRefId); + // add to the array of output column reference ids + m_pdrgpulOutputColRefs->Append(pulCopy); + + // get the column names and add it to the array of output column names + CMDName *mdname = + GPOS_NEW(m_mp) CMDName(m_mp, dxl_colref->MdName()->GetMDName()); + m_pdrgpmdname->Append(mdname); } return pexpr; @@ -1398,9 +1392,15 @@ CTranslatorDXLToExpr::PexprLogicalInsert(const CDXLNode *dxlnode) CColRefArray *colref_array = CTranslatorDXLToExprUtils::Pdrgpcr(m_mp, m_phmulcr, pdrgpulSourceCols); - return GPOS_NEW(m_mp) CExpression( - m_mp, GPOS_NEW(m_mp) CLogicalInsert(m_mp, ptabdesc, colref_array), - pexprChild); + CLogicalInsert *pexprLogInsert = + GPOS_NEW(m_mp) CLogicalInsert(m_mp, ptabdesc, colref_array); + + // add mapping between the DXL ColId and CColRef to m_phmulcr + ConstructDXLColId2ColRefMapping( + pdxlopInsert->GetDXLTableDescr()->GetColumnDescr(), + pexprLogInsert->PdrgpcrOutput()); + + return GPOS_NEW(m_mp) CExpression(m_mp, pexprLogInsert, pexprChild); } //--------------------------------------------------------------------------- @@ -1447,11 +1447,15 @@ CTranslatorDXLToExpr::PexprLogicalDelete(const CDXLNode *dxlnode) CColRefArray *colref_array = CTranslatorDXLToExprUtils::Pdrgpcr(m_mp, m_phmulcr, pdrgpulCols); - return GPOS_NEW(m_mp) CExpression( - m_mp, - GPOS_NEW(m_mp) CLogicalDelete(m_mp, ptabdesc, colref_array, pcrCtid, - pcrSegmentId, pcrTableOid), - pexprChild); + CLogicalDelete *pexprLogDelete = GPOS_NEW(m_mp) CLogicalDelete( + m_mp, ptabdesc, colref_array, pcrCtid, pcrSegmentId, pcrTableOid); + + // add mapping between the DXL ColId and CColRef to m_phmulcr + ConstructDXLColId2ColRefMapping( + pdxlopDelete->GetDXLTableDescr()->GetColumnDescr(), + pexprLogDelete->PdrgpcrOutput()); + + return GPOS_NEW(m_mp) CExpression(m_mp, pexprLogDelete, pexprChild); } //--------------------------------------------------------------------------- @@ -1509,12 +1513,16 @@ CTranslatorDXLToExpr::PexprLogicalUpdate(const CDXLNode *dxlnode) pcrTupleOid = LookupColRef(m_phmulcr, tuple_oid); } - return GPOS_NEW(m_mp) CExpression( - m_mp, - GPOS_NEW(m_mp) - CLogicalUpdate(m_mp, ptabdesc, pdrgpcrDelete, pdrgpcrInsert, - pcrCtid, pcrSegmentId, pcrTupleOid, pcrTableOid), - pexprChild); + CLogicalUpdate *pexprLogUpdate = GPOS_NEW(m_mp) + CLogicalUpdate(m_mp, ptabdesc, pdrgpcrDelete, pdrgpcrInsert, pcrCtid, + pcrSegmentId, pcrTupleOid, pcrTableOid); + + // add mapping between the DXL ColId and CColRef to m_phmulcr + ConstructDXLColId2ColRefMapping( + pdxlopUpdate->GetDXLTableDescr()->GetColumnDescr(), + pexprLogUpdate->PdrgpcrOutput()); + + return GPOS_NEW(m_mp) CExpression(m_mp, pexprLogUpdate, pexprChild); } //--------------------------------------------------------------------------- diff --git a/src/backend/gporca/libgpopt/src/translate/CTranslatorExprToDXL.cpp b/src/backend/gporca/libgpopt/src/translate/CTranslatorExprToDXL.cpp index dc70eef99da8..79bd59ec6f1a 100644 --- a/src/backend/gporca/libgpopt/src/translate/CTranslatorExprToDXL.cpp +++ b/src/backend/gporca/libgpopt/src/translate/CTranslatorExprToDXL.cpp @@ -361,47 +361,43 @@ CTranslatorExprToDXL::PdxlnTranslate(CExpression *pexpr, &ulNonGatherMotions, &fDML, true /*fRemap*/, true /*fRoot*/); - if (fDML) - { - pdrgpdsBaseTables->Release(); - return dxlnode; - } - - CDXLNode *pdxlnPrL = (*dxlnode)[0]; - GPOS_ASSERT(EdxlopScalarProjectList == - pdxlnPrL->GetOperator()->GetDXLOperator()); - const ULONG length = pdrgpmdname->Size(); - GPOS_ASSERT(length == colref_array->Size()); - GPOS_ASSERT(length == pdxlnPrL->Arity()); - for (ULONG ul = 0; ul < length; ul++) - { - // desired output column name - CMDName *mdname = - GPOS_NEW(m_mp) CMDName(m_mp, (*pdrgpmdname)[ul]->GetMDName()); - // get the old project element for the ColId - CDXLNode *pdxlnPrElOld = (*pdxlnPrL)[ul]; - CDXLScalarProjElem *pdxlopPrElOld = - CDXLScalarProjElem::Cast(pdxlnPrElOld->GetOperator()); - GPOS_ASSERT(1 == pdxlnPrElOld->Arity()); - CDXLNode *child_dxlnode = (*pdxlnPrElOld)[0]; - const ULONG colid = pdxlopPrElOld->Id(); - - // create a new project element node with the col id and new column name - // and add the scalar child - CDXLNode *pdxlnPrElNew = GPOS_NEW(m_mp) CDXLNode( - m_mp, GPOS_NEW(m_mp) CDXLScalarProjElem(m_mp, colid, mdname)); - child_dxlnode->AddRef(); - pdxlnPrElNew->AddChild(child_dxlnode); + if (length > 0) + { + CDXLNode *pdxlnPrL = (*dxlnode)[0]; + GPOS_ASSERT(EdxlopScalarProjectList == + pdxlnPrL->GetOperator()->GetDXLOperator()); - // replace the project element - pdxlnPrL->ReplaceChild(ul, pdxlnPrElNew); + GPOS_ASSERT(length == colref_array->Size()); + GPOS_ASSERT(length == pdxlnPrL->Arity()); + for (ULONG ul = 0; ul < length; ul++) + { + // desired output column name + CMDName *mdname = + GPOS_NEW(m_mp) CMDName(m_mp, (*pdrgpmdname)[ul]->GetMDName()); + + // get the old project element for the ColId + CDXLNode *pdxlnPrElOld = (*pdxlnPrL)[ul]; + CDXLScalarProjElem *pdxlopPrElOld = + CDXLScalarProjElem::Cast(pdxlnPrElOld->GetOperator()); + GPOS_ASSERT(1 == pdxlnPrElOld->Arity()); + CDXLNode *child_dxlnode = (*pdxlnPrElOld)[0]; + const ULONG colid = pdxlopPrElOld->Id(); + + // create a new project element node with the col id and new column name + // and add the scalar child + CDXLNode *pdxlnPrElNew = GPOS_NEW(m_mp) CDXLNode( + m_mp, GPOS_NEW(m_mp) CDXLScalarProjElem(m_mp, colid, mdname)); + child_dxlnode->AddRef(); + pdxlnPrElNew->AddChild(child_dxlnode); + + // replace the project element + pdxlnPrL->ReplaceChild(ul, pdxlnPrElNew); + } } - - - if (0 == ulNonGatherMotions) + if (!fDML && 0 == ulNonGatherMotions) { CTranslatorExprToDXLUtils::SetDirectDispatchInfo( m_mp, m_pmda, dxlnode, pexpr, pdrgpdsBaseTables); @@ -4558,6 +4554,44 @@ CTranslatorExprToDXL::PdxlnMotion(CExpression *pexprMotion, CTranslatorExprToDXLUtils::PdxlnProjListFromChildProjList( m_mp, m_pcf, m_phmcrdxln, pdxlnProjListChild); + // load unhandled subplans required by child to motion them too + CDXLNode *pdxlnPrLstSplan = NULL; + CColRefSetIter reqColRefIter(*pexprMotion->Prpp()->PcrsRequired()); + while (reqColRefIter.Advance()) + { + CColRef *colref = reqColRefIter.Pcr(); + CDXLNode *dxlnode = m_phmcrdxln->Find(colref); + + if (NULL != dxlnode && + EdxlopScalarSubPlan == dxlnode->GetOperator()->GetDXLOperator()) + { + if (pdxlnPrLstSplan == NULL) + { + // make a copy of motion's proj list + pdxlnPrLstSplan = + CTranslatorExprToDXLUtils::PdxlnProjListFromChildProjList( + m_mp, m_pcf, m_phmcrdxln, pdxlnProjListChild); + } + + CDXLNode *pdxlnPrElSplan = CTranslatorExprToDXLUtils::PdxlnProjElem( + m_mp, m_phmcrdxln, colref); + pdxlnPrLstSplan->AddChild(pdxlnPrElSplan); + + // also add new proj elem referencing created subplan to motion's proj list + CDXLNode *pdxlnPrEl = CTranslatorExprToDXLUtils::PdxlnProjElem( + m_mp, m_phmcrdxln, colref); + proj_list_dxlnode->AddChild(pdxlnPrEl); + } + } + + // if there are any leftover subplans wrap them in extra Result node + if (pdxlnPrLstSplan != NULL) + { + child_dxlnode = PdxlnResult( + CTranslatorExprToDXLUtils::PdxlpropCopy(m_mp, child_dxlnode), + pdxlnPrLstSplan, child_dxlnode); + } + // set input and output segment information motion->SetSegmentInfo(GetInputSegIdsArray(pexprMotion), GetOutputSegIdsArray(pexprMotion)); @@ -5795,6 +5829,7 @@ CTranslatorExprToDXL::PdxlnDML(CExpression *pexpr, CExpression *pexprChild = (*pexpr)[0]; CTableDescriptor *ptabdesc = popDML->Ptabdesc(); CColRefArray *pdrgpcrSource = popDML->PdrgpcrSource(); + CColRefArray *pdrgpcrOutput = popDML->PdrgpcrOutput(); CColRef *pcrAction = popDML->PcrAction(); GPOS_ASSERT(NULL != pcrAction); @@ -5828,8 +5863,8 @@ CTranslatorExprToDXL::PdxlnDML(CExpression *pexpr, pexprChild, pdrgpcrSource, pdrgpdsBaseTables, pulNonGatherMotions, pfDML, false /*fRemap*/, false /*fRoot*/); - CDXLTableDescr *table_descr = MakeDXLTableDescr( - ptabdesc, NULL /*pdrgpcrOutput*/, NULL /*requiredProperties*/); + CDXLTableDescr *table_descr = + MakeDXLTableDescr(ptabdesc, pdrgpcrOutput, pexpr->Prpp()); ULongPtrArray *pdrgpul = CUtils::Pdrgpul(m_mp, pdrgpcrSource); CDXLDirectDispatchInfo *dxl_direct_dispatch_info = @@ -5839,14 +5874,48 @@ CTranslatorExprToDXL::PdxlnDML(CExpression *pexpr, ctid_colid, segid_colid, preserve_oids, tuple_oid, tableoid_colid, dxl_direct_dispatch_info); + // if dml node has triggers we should also output source columns + if (popDML->HasTriggers()) + { + pdrgpcrOutput->AppendArray(pdrgpcrSource); + } + // project list - CColRefSet *pcrsOutput = pexpr->Prpp()->PcrsRequired(); - CDXLNode *pdxlnPrL = PdxlnProjList(pcrsOutput, pdrgpcrSource); + CDXLNode *pdxlnPrL = PdxlnProjList(NULL, pdrgpcrSource); + + // configure output project list for used columns + CDXLNode *pdxlnPrLOutput; + ULongPtrArray *usedColsIndexes = GPOS_NEW(m_mp) ULongPtrArray(m_mp); + + for (ULONG outputColIndex = 0; outputColIndex < pdrgpcrOutput->Size(); + outputColIndex++) + { + CColRef *colref = (*pdrgpcrOutput)[outputColIndex]; + if (colref->GetUsage(true, true) == CColRef::EUsed) + { + usedColsIndexes->Append(GPOS_NEW(m_mp) ULONG(outputColIndex)); + } + } + + if (pdrgpcrOutput->Size() == usedColsIndexes->Size()) + { + pdxlnPrLOutput = PdxlnProjList(NULL, pdrgpcrOutput); + } + else + { + CColRefArray *reducedOutput = + pdrgpcrOutput->CreateReducedArray(usedColsIndexes); + pdxlnPrLOutput = PdxlnProjList(NULL, reducedOutput); + reducedOutput->Release(); + } + + usedColsIndexes->Release(); CDXLNode *pdxlnDML = GPOS_NEW(m_mp) CDXLNode(m_mp, pdxlopDML); CDXLPhysicalProperties *dxl_properties = GetProperties(pexpr); pdxlnDML->SetProperties(dxl_properties); + pdxlnDML->AddChild(pdxlnPrLOutput); pdxlnDML->AddChild(pdxlnPrL); pdxlnDML->AddChild(child_dxlnode); @@ -7747,7 +7816,7 @@ CDXLNode * CTranslatorExprToDXL::PdxlnProjList(const CColRefSet *pcrsOutput, CColRefArray *colref_array) { - GPOS_ASSERT(NULL != pcrsOutput); + GPOS_ASSERT_IMP(NULL == pcrsOutput, colref_array != NULL); CDXLScalarProjList *pdxlopPrL = GPOS_NEW(m_mp) CDXLScalarProjList(m_mp); CDXLNode *pdxlnPrL = GPOS_NEW(m_mp) CDXLNode(m_mp, pdxlopPrL); @@ -7766,18 +7835,22 @@ CTranslatorExprToDXL::PdxlnProjList(const CColRefSet *pcrsOutput, pcrs->Include(colref); } - // add the remaining required columns - CColRefSetIter crsi(*pcrsOutput); - while (crsi.Advance()) + if (pcrsOutput != NULL) { - CColRef *colref = crsi.Pcr(); - - if (!pcrs->FMember(colref)) + // add the remaining required columns + CColRefSetIter crsi(*pcrsOutput); + while (crsi.Advance()) { - CDXLNode *pdxlnPrEl = CTranslatorExprToDXLUtils::PdxlnProjElem( - m_mp, m_phmcrdxln, colref); - pdxlnPrL->AddChild(pdxlnPrEl); - pcrs->Include(colref); + CColRef *colref = crsi.Pcr(); + + if (!pcrs->FMember(colref)) + { + CDXLNode *pdxlnPrEl = + CTranslatorExprToDXLUtils::PdxlnProjElem( + m_mp, m_phmcrdxln, colref); + pdxlnPrL->AddChild(pdxlnPrEl); + pcrs->Include(colref); + } } } pcrs->Release(); diff --git a/src/backend/gporca/libgpopt/src/xforms/CXformDelete2DML.cpp b/src/backend/gporca/libgpopt/src/xforms/CXformDelete2DML.cpp index 9c764b6f79e2..d3bb8c8e24d2 100644 --- a/src/backend/gporca/libgpopt/src/xforms/CXformDelete2DML.cpp +++ b/src/backend/gporca/libgpopt/src/xforms/CXformDelete2DML.cpp @@ -80,6 +80,9 @@ CXformDelete2DML::Transform(CXformContext *pxfctxt, CXformResult *pxfres, CColRefArray *colref_array = popDelete->Pdrgpcr(); colref_array->AddRef(); + CColRefArray *pdrgpcrOutput = popDelete->PdrgpcrOutput(); + pdrgpcrOutput->AddRef(); + CColRef *pcrCtid = popDelete->PcrCtid(); CColRef *pcrSegmentId = popDelete->PcrSegmentId(); @@ -93,7 +96,7 @@ CXformDelete2DML::Transform(CXformContext *pxfctxt, CXformResult *pxfres, // create logical DML CExpression *pexprAlt = CXformUtils::PexprLogicalDMLOverProject( mp, pexprChild, CLogicalDML::EdmlDelete, ptabdesc, colref_array, - pcrCtid, pcrSegmentId, pcrTableOid); + pdrgpcrOutput, pcrCtid, pcrSegmentId, pcrTableOid); // add alternative to transformation result pxfres->Add(pexprAlt); diff --git a/src/backend/gporca/libgpopt/src/xforms/CXformImplementDML.cpp b/src/backend/gporca/libgpopt/src/xforms/CXformImplementDML.cpp index beaaa976e40c..4c2d0e88c4c0 100644 --- a/src/backend/gporca/libgpopt/src/xforms/CXformImplementDML.cpp +++ b/src/backend/gporca/libgpopt/src/xforms/CXformImplementDML.cpp @@ -82,6 +82,8 @@ CXformImplementDML::Transform(CXformContext *pxfctxt, CXformResult *pxfres, CColRefArray *pdrgpcrSource = popDML->PdrgpcrSource(); pdrgpcrSource->AddRef(); + CColRefArray *pdrgpcrOutput = popDML->PdrgpcrOutput(); + pdrgpcrOutput->AddRef(); CBitSet *pbsModified = popDML->PbsModified(); pbsModified->AddRef(); @@ -98,9 +100,9 @@ CXformImplementDML::Transform(CXformContext *pxfctxt, CXformResult *pxfres, // create physical DML CExpression *pexprAlt = GPOS_NEW(mp) CExpression( mp, - GPOS_NEW(mp) CPhysicalDML(mp, edmlop, ptabdesc, pdrgpcrSource, - pbsModified, pcrAction, pcrCtid, pcrSegmentId, - pcrTupleOid, pcrTableOid), + GPOS_NEW(mp) CPhysicalDML( + mp, edmlop, ptabdesc, pdrgpcrSource, pdrgpcrOutput, pbsModified, + pcrAction, pcrCtid, pcrSegmentId, pcrTupleOid, pcrTableOid), pexprChild); // add alternative to transformation result pxfres->Add(pexprAlt); diff --git a/src/backend/gporca/libgpopt/src/xforms/CXformInsert2DML.cpp b/src/backend/gporca/libgpopt/src/xforms/CXformInsert2DML.cpp index fa6d733fb66f..bedd7d6cac37 100644 --- a/src/backend/gporca/libgpopt/src/xforms/CXformInsert2DML.cpp +++ b/src/backend/gporca/libgpopt/src/xforms/CXformInsert2DML.cpp @@ -80,6 +80,9 @@ CXformInsert2DML::Transform(CXformContext *pxfctxt, CXformResult *pxfres, CColRefArray *pdrgpcrSource = popInsert->PdrgpcrSource(); pdrgpcrSource->AddRef(); + CColRefArray *pdrgpcrOutput = popInsert->PdrgpcrOutput(); + pdrgpcrOutput->AddRef(); + // child of insert operator CExpression *pexprChild = (*pexpr)[0]; pexprChild->AddRef(); @@ -87,6 +90,7 @@ CXformInsert2DML::Transform(CXformContext *pxfctxt, CXformResult *pxfres, // create logical DML CExpression *pexprAlt = CXformUtils::PexprLogicalDMLOverProject( mp, pexprChild, CLogicalDML::EdmlInsert, ptabdesc, pdrgpcrSource, + pdrgpcrOutput, NULL, //pcrCtid NULL, //pcrSegmentId NULL //pcrTable diff --git a/src/backend/gporca/libgpopt/src/xforms/CXformUpdate2DML.cpp b/src/backend/gporca/libgpopt/src/xforms/CXformUpdate2DML.cpp index 0145cb9e9cc0..3f0d4731da7a 100644 --- a/src/backend/gporca/libgpopt/src/xforms/CXformUpdate2DML.cpp +++ b/src/backend/gporca/libgpopt/src/xforms/CXformUpdate2DML.cpp @@ -84,6 +84,7 @@ CXformUpdate2DML::Transform(CXformContext *pxfctxt, CXformResult *pxfres, CTableDescriptor *ptabdesc = popUpdate->Ptabdesc(); CColRefArray *pdrgpcrDelete = popUpdate->PdrgpcrDelete(); CColRefArray *pdrgpcrInsert = popUpdate->PdrgpcrInsert(); + CColRefArray *pdrgpcrOutput = popUpdate->PdrgpcrOutput(); CColRef *pcrCtid = popUpdate->PcrCtid(); CColRef *pcrSegmentId = popUpdate->PcrSegmentId(); CColRef *pcrTupleOid = popUpdate->PcrTupleOid(); @@ -160,11 +161,13 @@ CXformUpdate2DML::Transform(CXformContext *pxfctxt, CXformResult *pxfres, // create logical DML ptabdesc->AddRef(); pdrgpcrDelete->AddRef(); + pdrgpcrOutput->AddRef(); CExpression *pexprDML = GPOS_NEW(mp) CExpression( mp, - GPOS_NEW(mp) CLogicalDML(mp, CLogicalDML::EdmlUpdate, ptabdesc, - pdrgpcrDelete, pbsModified, pcrAction, pcrCtid, - pcrSegmentId, pcrTupleOid, pcrTableOid), + GPOS_NEW(mp) + CLogicalDML(mp, CLogicalDML::EdmlUpdate, ptabdesc, pdrgpcrDelete, + pdrgpcrOutput, pbsModified, pcrAction, pcrCtid, + pcrSegmentId, pcrTupleOid, pcrTableOid), pexprAssertConstraints); // TODO: - Oct 30, 2012; detect and handle AFTER triggers on update diff --git a/src/backend/gporca/libgpopt/src/xforms/CXformUtils.cpp b/src/backend/gporca/libgpopt/src/xforms/CXformUtils.cpp index 8304bdd9e984..1c45d5d43687 100644 --- a/src/backend/gporca/libgpopt/src/xforms/CXformUtils.cpp +++ b/src/backend/gporca/libgpopt/src/xforms/CXformUtils.cpp @@ -1312,8 +1312,9 @@ CXformUtils::PexprLogicalPartitionSelector(CMemoryPool *mp, CExpression * CXformUtils::PexprLogicalDMLOverProject( CMemoryPool *mp, CExpression *pexprChild, CLogicalDML::EDMLOperator edmlop, - CTableDescriptor *ptabdesc, CColRefArray *colref_array, CColRef *pcrCtid, - CColRef *pcrSegmentId, CColRef *pcrTableOid) + CTableDescriptor *ptabdesc, CColRefArray *colref_array, + CColRefArray *pdrgpcrOutput, CColRef *pcrCtid, CColRef *pcrSegmentId, + CColRef *pcrTableOid) { GPOS_ASSERT(CLogicalDML::EdmlInsert == edmlop || CLogicalDML::EdmlDelete == edmlop); @@ -1353,10 +1354,10 @@ CXformUtils::PexprLogicalDMLOverProject( CExpression *pexprDML = GPOS_NEW(mp) CExpression( mp, - GPOS_NEW(mp) CLogicalDML(mp, edmlop, ptabdesc, colref_array, - GPOS_NEW(mp) CBitSet(mp) /*pbsModified*/, - pcrAction, pcrCtid, pcrSegmentId, - NULL /*pcrTupleOid*/, pcrTableOid), + GPOS_NEW(mp) CLogicalDML( + mp, edmlop, ptabdesc, colref_array, pdrgpcrOutput, + GPOS_NEW(mp) CBitSet(mp) /*pbsModified*/, pcrAction, pcrCtid, + pcrSegmentId, NULL /*pcrTupleOid*/, pcrTableOid), pexprProject); CExpression *pexprOutput = pexprDML; @@ -1384,27 +1385,23 @@ BOOL CXformUtils::FTriggersExist(CLogicalDML::EDMLOperator edmlop, CTableDescriptor *ptabdesc, BOOL fBefore) { - CMDAccessor *md_accessor = COptCtxt::PoctxtFromTLS()->Pmda(); - const IMDRelation *pmdrel = md_accessor->RetrieveRel(ptabdesc->MDId()); - const ULONG ulTriggers = pmdrel->TriggerCount(); - - for (ULONG ul = 0; ul < ulTriggers; ul++) - { - const IMDTrigger *pmdtrigger = - md_accessor->RetrieveTrigger(pmdrel->TriggerMDidAt(ul)); - if (!pmdtrigger->IsEnabled() || !pmdtrigger->ExecutesOnRowLevel() || - !FTriggerApplies(edmlop, pmdtrigger)) - { - continue; - } - - if (pmdtrigger->IsBefore() == fBefore) - { - return true; - } - } + return FTriggersExistInner(edmlop, ptabdesc, true, fBefore); +} - return false; +//--------------------------------------------------------------------------- +// @function: +// CXformUtils::FTriggersExist +// +// @doc: +// Check whether there are any row-level triggers on +// the given table that match the given DML operation +// +//--------------------------------------------------------------------------- +BOOL +CXformUtils::FTriggersExist(CLogicalDML::EDMLOperator edmlop, + CTableDescriptor *ptabdesc) +{ + return FTriggersExistInner(edmlop, ptabdesc, false, false); } //--------------------------------------------------------------------------- @@ -4487,6 +4484,44 @@ CXformUtils::ICmpPrjElemsArr(const void *pvFst, const void *pvSnd) } +//--------------------------------------------------------------------------- +// @function: +// CXformUtils::FTriggersExistInner +// +// @doc: +// Private warehouse for checking triggers on the +// given table that match the given DML operation +// +//--------------------------------------------------------------------------- +BOOL +CXformUtils::FTriggersExistInner(CLogicalDML::EDMLOperator edmlop, + CTableDescriptor *ptabdesc, BOOL shouldCheck, + BOOL fBefore) +{ + CMDAccessor *md_accessor = COptCtxt::PoctxtFromTLS()->Pmda(); + const IMDRelation *pmdrel = md_accessor->RetrieveRel(ptabdesc->MDId()); + const ULONG ulTriggers = pmdrel->TriggerCount(); + + for (ULONG ul = 0; ul < ulTriggers; ul++) + { + const IMDTrigger *pmdtrigger = + md_accessor->RetrieveTrigger(pmdrel->TriggerMDidAt(ul)); + if (!pmdtrigger->IsEnabled() || !pmdtrigger->ExecutesOnRowLevel() || + !FTriggerApplies(edmlop, pmdtrigger)) + { + continue; + } + + if (!shouldCheck || pmdtrigger->IsBefore() == fBefore) + { + return true; + } + } + + return false; +} + + //--------------------------------------------------------------------------- // @function: // CXformUtils::PdrgpdrgpexprSortedPrjElemsArray diff --git a/src/backend/gporca/libnaucrates/include/naucrates/dxl/operators/CDXLTableDescr.h b/src/backend/gporca/libnaucrates/include/naucrates/dxl/operators/CDXLTableDescr.h index f1eb8e3a9a62..c619275f2b1e 100644 --- a/src/backend/gporca/libnaucrates/include/naucrates/dxl/operators/CDXLTableDescr.h +++ b/src/backend/gporca/libnaucrates/include/naucrates/dxl/operators/CDXLTableDescr.h @@ -87,6 +87,13 @@ class CDXLTableDescr : public CRefCount // user id ULONG GetExecuteAsUserId() const; + // get the column descriptor array + const CDXLColDescrArray * + GetColumnDescr() const + { + return m_dxl_column_descr_array; + } + // get the column descriptor at the given position const CDXLColDescr *GetColumnDescrAt(ULONG idx) const; diff --git a/src/backend/gporca/libnaucrates/src/operators/CDXLPhysicalDML.cpp b/src/backend/gporca/libnaucrates/src/operators/CDXLPhysicalDML.cpp index 5190b429eada..8edc89fcd00d 100644 --- a/src/backend/gporca/libnaucrates/src/operators/CDXLPhysicalDML.cpp +++ b/src/backend/gporca/libnaucrates/src/operators/CDXLPhysicalDML.cpp @@ -169,14 +169,17 @@ CDXLPhysicalDML::SerializeToDXL(CXMLSerializer *xml_serializer, CDXLTokens::GetDXLTokenStr(EdxltokenDirectDispatchInfo)); } - // serialize project list + // serialize project list for returning list (*node)[0]->SerializeToDXL(xml_serializer); + // serialize project list + (*node)[1]->SerializeToDXL(xml_serializer); + // serialize table descriptor m_dxl_table_descr->SerializeToDXL(xml_serializer); // serialize physical child - (*node)[1]->SerializeToDXL(xml_serializer); + (*node)[2]->SerializeToDXL(xml_serializer); xml_serializer->CloseElement( CDXLTokens::GetDXLTokenStr(EdxltokenNamespacePrefix), element_name); @@ -194,8 +197,8 @@ CDXLPhysicalDML::SerializeToDXL(CXMLSerializer *xml_serializer, void CDXLPhysicalDML::AssertValid(const CDXLNode *node, BOOL validate_children) const { - GPOS_ASSERT(2 == node->Arity()); - CDXLNode *child_dxlnode = (*node)[1]; + GPOS_ASSERT(3 == node->Arity()); + CDXLNode *child_dxlnode = (*node)[2]; GPOS_ASSERT(EdxloptypePhysical == child_dxlnode->GetOperator()->GetDXLOperatorType()); diff --git a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerPhysicalDML.cpp b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerPhysicalDML.cpp index 65f88d2a09e6..9229627763bf 100644 --- a/src/backend/gporca/libnaucrates/src/parser/CParseHandlerPhysicalDML.cpp +++ b/src/backend/gporca/libnaucrates/src/parser/CParseHandlerPhysicalDML.cpp @@ -152,6 +152,13 @@ CParseHandlerPhysicalDML::StartElement(const XMLCh *const, // element_uri, m_parse_handler_mgr, this); m_parse_handler_mgr->ActivateParseHandler(proj_list_parse_handler); + // parse handler for the returning proj list + CParseHandlerBase *proj_list_output_parse_handler = + CParseHandlerFactory::GetParseHandler( + m_mp, CDXLTokens::XmlstrToken(EdxltokenScalarProjList), + m_parse_handler_mgr, this); + m_parse_handler_mgr->ActivateParseHandler(proj_list_output_parse_handler); + //parse handler for the direct dispatch info CParseHandlerBase *direct_dispatch_parse_handler = CParseHandlerFactory::GetParseHandler( @@ -169,6 +176,7 @@ CParseHandlerPhysicalDML::StartElement(const XMLCh *const, // element_uri, // store child parse handlers in array this->Append(prop_parse_handler); this->Append(direct_dispatch_parse_handler); + this->Append(proj_list_output_parse_handler); this->Append(proj_list_parse_handler); this->Append(table_descr_parse_handler); this->Append(child_parse_handler); @@ -199,7 +207,7 @@ CParseHandlerPhysicalDML::EndElement(const XMLCh *const, // element_uri, str->GetBuffer()); } - GPOS_ASSERT(5 == this->Length()); + GPOS_ASSERT(6 == this->Length()); CParseHandlerProperties *prop_parse_handler = dynamic_cast((*this)[0]); @@ -212,20 +220,25 @@ CParseHandlerPhysicalDML::EndElement(const XMLCh *const, // element_uri, GPOS_ASSERT(NULL != direct_dispatch_parse_handler->GetDXLDirectDispatchInfo()); - CParseHandlerProjList *proj_list_parse_handler = + CParseHandlerProjList *proj_list_output_parse_handler = dynamic_cast((*this)[2]); + GPOS_ASSERT(NULL != proj_list_output_parse_handler); + GPOS_ASSERT(NULL != proj_list_output_parse_handler->CreateDXLNode()); + + CParseHandlerProjList *proj_list_parse_handler = + dynamic_cast((*this)[3]); GPOS_ASSERT(NULL != proj_list_parse_handler); GPOS_ASSERT(NULL != proj_list_parse_handler->CreateDXLNode()); CParseHandlerTableDescr *table_descr_parse_handler = - dynamic_cast((*this)[3]); + dynamic_cast((*this)[4]); GPOS_ASSERT(NULL != table_descr_parse_handler); GPOS_ASSERT(NULL != table_descr_parse_handler->GetDXLTableDescr()); CDXLTableDescr *table_descr = table_descr_parse_handler->GetDXLTableDescr(); table_descr->AddRef(); CParseHandlerPhysicalOp *child_parse_handler = - dynamic_cast((*this)[4]); + dynamic_cast((*this)[5]); GPOS_ASSERT(NULL != child_parse_handler); GPOS_ASSERT(NULL != child_parse_handler->CreateDXLNode()); @@ -241,6 +254,7 @@ CParseHandlerPhysicalDML::EndElement(const XMLCh *const, // element_uri, // set statistics and physical properties CParseHandlerUtils::SetProperties(m_dxl_node, prop_parse_handler); + AddChildFromParseHandler(proj_list_output_parse_handler); AddChildFromParseHandler(proj_list_parse_handler); AddChildFromParseHandler(child_parse_handler); diff --git a/src/backend/gporca/server/src/unittest/gpopt/minidump/CDMLTest.cpp b/src/backend/gporca/server/src/unittest/gpopt/minidump/CDMLTest.cpp index 55015c96ae38..a1abc64a814e 100644 --- a/src/backend/gporca/server/src/unittest/gpopt/minidump/CDMLTest.cpp +++ b/src/backend/gporca/server/src/unittest/gpopt/minidump/CDMLTest.cpp @@ -28,6 +28,15 @@ ULONG CDMLTest::m_ulDMLTestCounter = 0; // start from first test // minidump files const CHAR *rgszDMLFileNames[] = { "../data/dxl/minidump/Insert.mdp", + "../data/dxl/minidump/InsertIntoReturning.mdp", + "../data/dxl/minidump/DeleteReturning.mdp", + "../data/dxl/minidump/UpdateReturning.mdp", + "../data/dxl/minidump/InsertIntoReturningProjection.mdp", + "../data/dxl/minidump/DeleteReturningProjection.mdp", + "../data/dxl/minidump/UpdateReturningProjection.mdp", + "../data/dxl/minidump/InsertInCTE.mdp", + "../data/dxl/minidump/DeleteInCTE.mdp", + "../data/dxl/minidump/UpdateInCTE.mdp", "../data/dxl/minidump/MultipleUpdateWithJoinOnDistCol.mdp", "../data/dxl/minidump/UpdatingNonDistributionColumnFunc.mdp", "../data/dxl/minidump/UpdatingMultipleColumn.mdp", diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index d14dd2013d82..3e1db5a14bf1 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -1339,6 +1339,7 @@ _copyDML(const DML *from) COPY_SCALAR_FIELD(tupleoidColIdx); COPY_SCALAR_FIELD(tableoidColIdx); COPY_SCALAR_FIELD(canSetTag); + COPY_NODE_FIELD(targetListProj); return newnode; } diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 3674f6bfdfb3..bdb52de16146 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -1219,6 +1219,7 @@ _outDML(StringInfo str, const DML *node) WRITE_INT_FIELD(tupleoidColIdx); WRITE_INT_FIELD(tableoidColIdx); WRITE_BOOL_FIELD(canSetTag); + WRITE_NODE_FIELD(targetListProj); _outPlanInfo(str, (Plan *) node); } diff --git a/src/backend/nodes/readfast.c b/src/backend/nodes/readfast.c index 8dcb032b8694..c89f2efb7e47 100644 --- a/src/backend/nodes/readfast.c +++ b/src/backend/nodes/readfast.c @@ -2240,6 +2240,7 @@ _readDML(void) READ_INT_FIELD(tupleoidColIdx); READ_INT_FIELD(tableoidColIdx); READ_BOOL_FIELD(canSetTag); + READ_NODE_FIELD(targetListProj); readPlanInfo((Plan *)local_node); diff --git a/src/include/executor/nodeDML.h b/src/include/executor/nodeDML.h index 5ca551e3061d..828e6a9bbc7e 100644 --- a/src/include/executor/nodeDML.h +++ b/src/include/executor/nodeDML.h @@ -17,9 +17,11 @@ #define NODEDML_H extern void ExecDMLExplainEnd(PlanState *planstate, struct StringInfoData *buf); +extern void RemapProjection(ProjectionInfo *projInfo, AttrMap *map); extern TupleTableSlot* ExecDML(DMLState *node); extern DMLState* ExecInitDML(DML *node, EState *estate, int eflags); extern void ExecEndDML(DMLState *node); +extern void ExecSquelchDML(DMLState *node); #endif /* NODEDML_H */ diff --git a/src/include/gpopt/translate/CTranslatorDXLToPlStmt.h b/src/include/gpopt/translate/CTranslatorDXLToPlStmt.h index 778c81d42e86..8db17485fff2 100644 --- a/src/include/gpopt/translate/CTranslatorDXLToPlStmt.h +++ b/src/include/gpopt/translate/CTranslatorDXLToPlStmt.h @@ -137,6 +137,12 @@ class CTranslatorDXLToPlStmt // command type CmdType m_cmd_type; + // does dml operation require returning + BOOL m_has_returning; + + // has dml operation with returning on replicated table + BOOL m_returning_dml_on_replicated; + // is target table distributed, false when in non DML statements BOOL m_is_tgt_tbl_distributed; diff --git a/src/include/gpopt/translate/CTranslatorQueryToDXL.h b/src/include/gpopt/translate/CTranslatorQueryToDXL.h index 10887c772d04..3355d510c674 100644 --- a/src/include/gpopt/translate/CTranslatorQueryToDXL.h +++ b/src/include/gpopt/translate/CTranslatorQueryToDXL.h @@ -423,6 +423,10 @@ class CTranslatorQueryToDXL // returns the corresponding ColId for the given system attribute numbber ULONG GetSystemColId(INT attribute_number); + // Wrap dxl node in logical project with return columns from returningList + CDXLNode *ProcessReturningList(CDXLNode *dml_dxlnode, + CDXLTableDescr *table_descr); + public: // dtor ~CTranslatorQueryToDXL(); diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h index bad260346c0d..ceb625d333e7 100644 --- a/src/include/nodes/execnodes.h +++ b/src/include/nodes/execnodes.h @@ -2843,6 +2843,7 @@ typedef struct DMLState TupleTableSlot *cleanedUpSlot; /* holds 'final' tuple which matches the target relation schema */ AttrNumber segid_attno; /* attribute number of "gp_segment_id" */ bool canSetTag; /* calculate processed tuples */ + TupleTableSlot *resultTupleSlot;/* slot for temporary storing projection result tuples */ } DMLState; /* diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h index 21e8e8c88b1b..933720126a33 100644 --- a/src/include/nodes/plannodes.h +++ b/src/include/nodes/plannodes.h @@ -1330,6 +1330,7 @@ typedef struct DML AttrNumber tupleoidColIdx; /* index of tuple oid column into the target list */ AttrNumber tableoidColIdx; /* index of table oid column into the target list */ bool canSetTag; /* calculate processed tuples */ + List *targetListProj; /* projection of target list of child node */ } DML; /* diff --git a/src/test/regress/expected/bfv_dml_optimizer.out b/src/test/regress/expected/bfv_dml_optimizer.out index 5b53b1bce19f..1c5552282076 100644 --- a/src/test/regress/expected/bfv_dml_optimizer.out +++ b/src/test/regress/expected/bfv_dml_optimizer.out @@ -393,18 +393,19 @@ analyze bar; explain delete from foo using bar where foo.a = bar.a returning foo.*; QUERY PLAN ----------------------------------------------------------------------------------------------------------- - Gather Motion 3:1 (slice3; segments: 3) (cost=3.23..6.66 rows=10 width=16) - -> Delete on foo (cost=3.23..6.66 rows=4 width=16) - -> Explicit Redistribute Motion 3:3 (slice2; segments: 3) (cost=3.23..6.66 rows=4 width=16) - -> Hash Join (cost=3.23..6.66 rows=4 width=16) - Hash Cond: (foo.a = bar.a) - -> Redistribute Motion 3:3 (slice1; segments: 3) (cost=0.00..3.30 rows=4 width=14) - Hash Key: foo.a - -> Seq Scan on foo (cost=0.00..3.10 rows=4 width=14) - -> Hash (cost=3.10..3.10 rows=4 width=10) - -> Seq Scan on bar (cost=0.00..3.10 rows=4 width=10) - Optimizer: Postgres query optimizer -(11 rows) + Gather Motion 3:1 (slice3; segments: 3) (cost=0.00..862.18 rows=10 width=8) + -> Delete (cost=0.00..862.18 rows=4 width=8) + -> Result (cost=0.00..862.00 rows=4 width=14) + -> Explicit Redistribute Motion 3:3 (slice2; segments: 3) (cost=0.00..862.00 rows=4 width=10) + -> Hash Join (cost=0.00..862.00 rows=4 width=10) + Hash Cond: (foo.a = bar.a) + -> Redistribute Motion 3:3 (slice1; segments: 3) (cost=0.00..431.00 rows=4 width=14) + Hash Key: foo.a + -> Seq Scan on foo (cost=0.00..431.00 rows=4 width=14) + -> Hash (cost=431.00..431.00 rows=4 width=4) + -> Seq Scan on bar (cost=0.00..431.00 rows=4 width=4) + Optimizer: Pivotal Optimizer (GPORCA) +(12 rows) delete from foo using bar where foo.a = bar.a returning foo.*; a | b diff --git a/src/test/regress/expected/explain_analyze_optimizer.out b/src/test/regress/expected/explain_analyze_optimizer.out index 1f99de7109d8..adcd65939847 100644 --- a/src/test/regress/expected/explain_analyze_optimizer.out +++ b/src/test/regress/expected/explain_analyze_optimizer.out @@ -108,19 +108,18 @@ WITH cte AS ( ) SELECT * FROM cte; QUERY PLAN ------------------------------------------------------------------------------------ - Gather Motion 3:1 (slice2; segments: 3) (actual rows=5 loops=1) - -> Insert on with_dml (actual rows=3 loops=1) - -> Redistribute Motion 1:3 (slice1; segments: 1) (actual rows=3 loops=1) - Hash Key: i.i - -> Function Scan on generate_series i (actual rows=5 loops=1) + Gather Motion 3:1 (slice1; segments: 3) (actual rows=5 loops=1) + -> Insert (actual rows=3 loops=1) + -> Result (actual rows=3 loops=1) + -> Result (actual rows=3 loops=1) + -> Function Scan on generate_series (actual rows=5 loops=1) Planning time: 20.649 ms (slice0) Executor memory: 59K bytes. - (slice1) Executor memory: 58K bytes (seg1). Work_mem: 17K bytes max. - (slice2) Executor memory: 42K bytes avg x 3 workers, 42K bytes max (seg0). + (slice1) Executor memory: 90K bytes avg x 3 workers, 90K bytes max (seg0). Work_mem: 17K bytes max. Memory used: 128000kB - Optimizer: Postgres query optimizer + Optimizer: Pivotal Optimizer (GPORCA) Execution time: 21.314 ms -(12 rows) +(11 rows) EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF) WITH cte AS ( @@ -130,15 +129,17 @@ WITH cte AS ( QUERY PLAN --------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=5 loops=1) - -> Update on with_dml (actual rows=3 loops=1) - -> Seq Scan on with_dml (actual rows=3 loops=1) + -> Update (actual rows=3 loops=1) + -> Split (actual rows=6 loops=1) + -> Result (actual rows=3 loops=1) + -> Seq Scan on with_dml (actual rows=3 loops=1) Planning time: 13.933 ms (slice0) Executor memory: 59K bytes. (slice1) Executor memory: 58K bytes avg x 3 workers, 58K bytes max (seg0). Memory used: 128000kB - Optimizer: Postgres query optimizer + Optimizer: Pivotal Optimizer (GPORCA) Execution time: 4.397 ms -(9 rows) +(11 rows) EXPLAIN (ANALYZE, TIMING OFF, COSTS OFF) WITH cte AS ( @@ -148,16 +149,17 @@ WITH cte AS ( QUERY PLAN --------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (actual rows=5 loops=1) - -> Delete on with_dml (actual rows=3 loops=1) - -> Seq Scan on with_dml (actual rows=3 loops=1) - Filter: (i > 0) + -> Delete (actual rows=3 loops=1) + -> Result (actual rows=3 loops=1) + -> Seq Scan on with_dml (actual rows=3 loops=1) + Filter: (i > 0) Planning time: 8.322 ms (slice0) Executor memory: 59K bytes. (slice1) Executor memory: 57K bytes avg x 3 workers, 57K bytes max (seg0). Memory used: 128000kB - Optimizer: Postgres query optimizer + Optimizer: Pivotal Optimizer (GPORCA) Execution time: 9.462 ms -(10 rows) +(11 rows) DROP TABLE with_dml; -- diff --git a/src/test/regress/expected/partition_pruning_optimizer.out b/src/test/regress/expected/partition_pruning_optimizer.out index 76b541fca91a..6d9e2099f1f1 100644 --- a/src/test/regress/expected/partition_pruning_optimizer.out +++ b/src/test/regress/expected/partition_pruning_optimizer.out @@ -3492,7 +3492,6 @@ explain (verbose, costs off) delete from test_1_prt_extra where j = 2; QUERY PLAN --------------------------------------------------------------------------------------------------------------------- Delete - Output: "outer".ColRef_0009, test_1_prt_extra.ctid -> Result Output: test_1_prt_extra.ctid, test_1_prt_extra.gp_segment_id, 0 -> Seq Scan on partition_pruning.test_1_prt_extra @@ -3500,7 +3499,7 @@ explain (verbose, costs off) delete from test_1_prt_extra where j = 2; Filter: (test_1_prt_extra.j = 2) Optimizer: Pivotal Optimizer (GPORCA) Settings: enable_bitmapscan=on, enable_hashjoin=off, enable_indexscan=on, enable_mergejoin=on, enable_seqscan=off -(9 rows) +(8 rows) delete from test_1_prt_extra where j = 2; -- Check that deletion performed correctly @@ -3558,7 +3557,6 @@ HINT: For non-partitioned tables, run analyze (). For QUERY PLAN ------------------------------------------------------------------------------------------------------------------- Delete - Output: "outer".ColRef_0025, test.ctid, test.tableoid -> Result Output: test.ctid, test.tableoid, test.gp_segment_id, 0 -> Hash Anti Join @@ -3578,7 +3576,7 @@ HINT: For non-partitioned tables, run analyze (). For Output: test_in_predicate.i, test_in_predicate.j Optimizer: Pivotal Optimizer (GPORCA) Settings: enable_bitmapscan=on, enable_hashjoin=off, enable_indexscan=on, enable_mergejoin=on, enable_seqscan=off -(21 rows) +(20 rows) delete from test where not exists( select 1 from test_in_predicate where test.i = test_in_predicate.i and test.j = test_in_predicate.j @@ -3635,7 +3633,6 @@ explain (verbose, costs off) update test_1_prt_extra set k = 10 where j = 2; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------------- Update - Output: test_1_prt_extra.i, test_1_prt_extra.j, test_1_prt_extra.k, (DMLAction), test_1_prt_extra.ctid -> Split Output: test_1_prt_extra.i, test_1_prt_extra.j, test_1_prt_extra.k, test_1_prt_extra.ctid, test_1_prt_extra.gp_segment_id, DMLAction -> Result @@ -3645,7 +3642,7 @@ explain (verbose, costs off) update test_1_prt_extra set k = 10 where j = 2; Filter: (test_1_prt_extra.j = 2) Optimizer: Pivotal Optimizer (GPORCA) Settings: enable_bitmapscan=on, enable_hashjoin=off, enable_indexscan=on, enable_mergejoin=on, enable_seqscan=off -(11 rows) +(10 rows) update test_1_prt_extra set k = 10 where j = 2; -- Check that update performed correctly and tuple moved to correct partition @@ -3704,7 +3701,6 @@ HINT: For non-partitioned tables, run analyze (). For QUERY PLAN ------------------------------------------------------------------------------------------------------------------- Update - Output: test.i, test.j, test.k, (DMLAction), test.ctid, test.tableoid -> Split Output: test.i, test.j, test.k, test.ctid, test.tableoid, test.gp_segment_id, DMLAction -> Result @@ -3726,7 +3722,7 @@ HINT: For non-partitioned tables, run analyze (). For Output: test_in_predicate.i, test_in_predicate.j Optimizer: Pivotal Optimizer (GPORCA) Settings: enable_bitmapscan=on, enable_hashjoin=off, enable_indexscan=on, enable_mergejoin=on, enable_seqscan=off -(23 rows) +(22 rows) update test set k = 10 where not exists( select 1 from test_in_predicate where test.i = test_in_predicate.i and test.j = test_in_predicate.j diff --git a/src/test/regress/expected/qp_dropped_cols_optimizer.out b/src/test/regress/expected/qp_dropped_cols_optimizer.out index 198ca1294e8b..61d15f7325ef 100644 --- a/src/test/regress/expected/qp_dropped_cols_optimizer.out +++ b/src/test/regress/expected/qp_dropped_cols_optimizer.out @@ -16290,7 +16290,6 @@ EXPLAIN (COSTS OFF, VERBOSE) INSERT INTO t_part_dropped VALUES (1, 2, 4); QUERY PLAN -------------------------------------------------- Insert - Output: c1, NULL::integer, c3, c4, ColRef_0004 -> Result Output: c1, c3, c4, 1 -> Result @@ -16300,14 +16299,13 @@ EXPLAIN (COSTS OFF, VERBOSE) INSERT INTO t_part_dropped VALUES (1, 2, 4); -> Result Output: true Optimizer: Pivotal Optimizer (GPORCA) -(11 rows) +(10 rows) INSERT INTO t_part_dropped VALUES (1, 2, 4); EXPLAIN (COSTS OFF, VERBOSE) INSERT INTO t_part_dropped_1_prt_p2 VALUES (1, 2, 4); QUERY PLAN ---------------------------------------- Insert - Output: c1, c3, c4, ColRef_0004 -> Result Output: c1, c3, c4, 1 -> Result @@ -16317,7 +16315,7 @@ EXPLAIN (COSTS OFF, VERBOSE) INSERT INTO t_part_dropped_1_prt_p2 VALUES (1, 2, 4 -> Result Output: true Optimizer: Pivotal Optimizer (GPORCA) -(11 rows) +(10 rows) INSERT INTO t_part_dropped_1_prt_p2 VALUES (1, 2, 4); INSERT INTO t_part_dropped_1_prt_p2 VALUES (1, 2, 0); @@ -16327,7 +16325,6 @@ EXPLAIN (COSTS OFF, VERBOSE) UPDATE t_part_dropped_1_prt_p2 SET c1 = 2; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Update - Output: t_part_dropped_1_prt_p2.c1, t_part_dropped_1_prt_p2.c3, t_part_dropped_1_prt_p2.c4, (DMLAction), t_part_dropped_1_prt_p2.ctid -> Redistribute Motion 3:3 (slice1; segments: 3) Output: t_part_dropped_1_prt_p2.c1, t_part_dropped_1_prt_p2.c3, t_part_dropped_1_prt_p2.c4, t_part_dropped_1_prt_p2.ctid, t_part_dropped_1_prt_p2.gp_segment_id, (DMLAction) Hash Key: t_part_dropped_1_prt_p2.c1 @@ -16338,14 +16335,13 @@ EXPLAIN (COSTS OFF, VERBOSE) UPDATE t_part_dropped_1_prt_p2 SET c1 = 2; -> Seq Scan on public.t_part_dropped_1_prt_p2 Output: t_part_dropped_1_prt_p2.c1, t_part_dropped_1_prt_p2.c3, t_part_dropped_1_prt_p2.c4, t_part_dropped_1_prt_p2.ctid, t_part_dropped_1_prt_p2.gp_segment_id Optimizer: Pivotal Optimizer (GPORCA) -(12 rows) +(11 rows) UPDATE t_part_dropped_1_prt_p2 SET c1 = 2; EXPLAIN (COSTS OFF, VERBOSE) UPDATE t_part_dropped SET c1 = 3; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Update - Output: t_part_dropped.c1, NULL::integer, t_part_dropped.c3, t_part_dropped.c4, (DMLAction), t_part_dropped.ctid, t_part_dropped.tableoid -> Redistribute Motion 3:3 (slice1; segments: 3) Output: t_part_dropped.c1, t_part_dropped.c3, t_part_dropped.c4, t_part_dropped.ctid, t_part_dropped.tableoid, t_part_dropped.gp_segment_id, (DMLAction) Hash Key: t_part_dropped.c1 @@ -16360,7 +16356,7 @@ EXPLAIN (COSTS OFF, VERBOSE) UPDATE t_part_dropped SET c1 = 3; -> Dynamic Seq Scan on public.t_part_dropped (dynamic scan id: 1) Output: t_part_dropped.c1, t_part_dropped.c3, t_part_dropped.c4, t_part_dropped.ctid, t_part_dropped.tableoid, t_part_dropped.gp_segment_id Optimizer: Pivotal Optimizer (GPORCA) -(16 rows) +(15 rows) UPDATE t_part_dropped SET c1 = 3; -- Ensure that split update on leaf partition does not throw constraint error @@ -16380,7 +16376,6 @@ EXPLAIN (COSTS OFF, VERBOSE) UPDATE t_part_dropped SET c1 = 3 WHERE c4 = 0; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Update - Output: t_part_dropped.c1, NULL::integer, t_part_dropped.c3, t_part_dropped.c4, (DMLAction), t_part_dropped.ctid, t_part_dropped.tableoid -> Redistribute Motion 3:3 (slice1; segments: 3) Output: t_part_dropped.c1, t_part_dropped.c3, t_part_dropped.c4, t_part_dropped.ctid, t_part_dropped.tableoid, t_part_dropped.gp_segment_id, (DMLAction) Hash Key: t_part_dropped.c1 @@ -16396,7 +16391,7 @@ EXPLAIN (COSTS OFF, VERBOSE) UPDATE t_part_dropped SET c1 = 3 WHERE c4 = 0; Output: t_part_dropped.c1, t_part_dropped.c3, t_part_dropped.c4, t_part_dropped.ctid, t_part_dropped.tableoid, t_part_dropped.gp_segment_id Filter: (t_part_dropped.c4 = 0) Optimizer: Pivotal Optimizer (GPORCA) -(17 rows) +(16 rows) UPDATE t_part_dropped SET c1 = 3 WHERE c4 = 0; SELECT count(*) FROM t_part_dropped_1_prt_p2; @@ -16415,13 +16410,12 @@ EXPLAIN (COSTS OFF, VERBOSE) DELETE FROM t_part_dropped_1_prt_p2; QUERY PLAN ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- Delete - Output: "outer".ColRef_0010, t_part_dropped_1_prt_p2.ctid -> Result Output: t_part_dropped_1_prt_p2.ctid, t_part_dropped_1_prt_p2.gp_segment_id, 0 -> Seq Scan on public.t_part_dropped_1_prt_p2 Output: t_part_dropped_1_prt_p2.ctid, t_part_dropped_1_prt_p2.gp_segment_id Optimizer: Pivotal Optimizer (GPORCA) -(7 rows) +(6 rows) DELETE FROM t_part_dropped_1_prt_p2; DROP TABLE t_part_dropped; @@ -16442,7 +16436,6 @@ EXPLAIN (COSTS OFF, VERBOSE) INSERT INTO t_part VALUES (1, 5, 2, 5); QUERY PLAN ---------------------------------------- Insert - Output: c1, c2, c3, c4, ColRef_0005 -> Result Output: c1, c2, c3, c4, 1 -> Result @@ -16452,14 +16445,13 @@ EXPLAIN (COSTS OFF, VERBOSE) INSERT INTO t_part VALUES (1, 5, 2, 5); -> Result Output: true Optimizer: Pivotal Optimizer (GPORCA) -(11 rows) +(10 rows) INSERT INTO t_part VALUES (1, 5, 2, 5); EXPLAIN (COSTS OFF, VERBOSE) INSERT INTO t_part_1_prt_p2 VALUES (1, 5, 2, 5); QUERY PLAN ------------------------------------------------------ Insert - Output: c1, NULL::integer, c2, c3, c4, ColRef_0005 -> Result Output: c1, c2, c3, c4, 1 -> Result @@ -16469,7 +16461,7 @@ EXPLAIN (COSTS OFF, VERBOSE) INSERT INTO t_part_1_prt_p2 VALUES (1, 5, 2, 5); -> Result Output: true Optimizer: Pivotal Optimizer (GPORCA) -(11 rows) +(10 rows) INSERT INTO t_part_1_prt_p2 VALUES (1, 5, 2, 5); -- Ensure that split update on leaf and root partitions does not @@ -16478,7 +16470,6 @@ EXPLAIN (COSTS OFF, VERBOSE) UPDATE t_part_1_prt_p2 SET c1 = 2; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- Update - Output: t_part_1_prt_p2.c1, NULL::integer, t_part_1_prt_p2.c2, t_part_1_prt_p2.c3, t_part_1_prt_p2.c4, (DMLAction), t_part_1_prt_p2.ctid -> Redistribute Motion 3:3 (slice1; segments: 3) Output: t_part_1_prt_p2.c1, t_part_1_prt_p2.c2, t_part_1_prt_p2.c3, t_part_1_prt_p2.c4, t_part_1_prt_p2.ctid, t_part_1_prt_p2.gp_segment_id, (DMLAction) Hash Key: t_part_1_prt_p2.c1 @@ -16489,14 +16480,13 @@ EXPLAIN (COSTS OFF, VERBOSE) UPDATE t_part_1_prt_p2 SET c1 = 2; -> Seq Scan on public.t_part_1_prt_p2 Output: t_part_1_prt_p2.c1, t_part_1_prt_p2.c2, t_part_1_prt_p2.c3, t_part_1_prt_p2.c4, t_part_1_prt_p2.ctid, t_part_1_prt_p2.gp_segment_id Optimizer: Pivotal Optimizer (GPORCA) -(12 rows) +(11 rows) UPDATE t_part_1_prt_p2 SET c1 = 2; EXPLAIN (COSTS OFF, VERBOSE) UPDATE t_part SET c1 = 3; QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------- Update - Output: t_part.c1, t_part.c2, t_part.c3, t_part.c4, (DMLAction), t_part.ctid, t_part.tableoid -> Redistribute Motion 3:3 (slice1; segments: 3) Output: t_part.c1, t_part.c2, t_part.c3, t_part.c4, t_part.ctid, t_part.tableoid, t_part.gp_segment_id, (DMLAction) Hash Key: t_part.c1 @@ -16511,7 +16501,7 @@ EXPLAIN (COSTS OFF, VERBOSE) UPDATE t_part SET c1 = 3; -> Dynamic Seq Scan on public.t_part (dynamic scan id: 1) Output: t_part.c1, t_part.c2, t_part.c3, t_part.c4, t_part.ctid, t_part.tableoid, t_part.gp_segment_id Optimizer: Pivotal Optimizer (GPORCA) -(16 rows) +(15 rows) UPDATE t_part SET c1 = 3; -- Ensure that split update on leaf partition does not throw constraint error @@ -16529,13 +16519,12 @@ EXPLAIN (COSTS OFF, VERBOSE) DELETE FROM t_part_1_prt_p2; QUERY PLAN ----------------------------------------------------------------------------------------------------------------------------------------------------------- Delete - Output: "outer".ColRef_0011, t_part_1_prt_p2.ctid -> Result Output: t_part_1_prt_p2.ctid, t_part_1_prt_p2.gp_segment_id, 0 -> Seq Scan on public.t_part_1_prt_p2 Output: t_part_1_prt_p2.ctid, t_part_1_prt_p2.gp_segment_id Optimizer: Pivotal Optimizer (GPORCA) -(7 rows) +(6 rows) DELETE FROM t_part_1_prt_p2; DROP TABLE t_part; @@ -16572,7 +16561,6 @@ EXPLAIN (COSTS OFF, VERBOSE) UPDATE t_part SET c1 = 3; QUERY PLAN ---------------------------------------------------------------------------------------------------------------------------------------- Update - Output: t_part.c1, t_part.c2, t_part.c3, t_part.c4, (DMLAction), t_part.ctid, t_part.tableoid -> Redistribute Motion 3:3 (slice1; segments: 3) Output: t_part.c1, t_part.c2, t_part.c3, t_part.c4, t_part.ctid, t_part.tableoid, t_part.gp_segment_id, (DMLAction) Hash Key: t_part.c1 @@ -16587,7 +16575,7 @@ EXPLAIN (COSTS OFF, VERBOSE) UPDATE t_part SET c1 = 3; -> Dynamic Seq Scan on public.t_part (dynamic scan id: 1) Output: t_part.c1, t_part.c2, t_part.c3, t_part.c4, t_part.ctid, t_part.tableoid, t_part.gp_segment_id Optimizer: Pivotal Optimizer (GPORCA) -(16 rows) +(15 rows) UPDATE t_part SET c1 = 3; SELECT * FROM t_part_1_prt_p2; diff --git a/src/test/regress/expected/returning_gp.out b/src/test/regress/expected/returning_gp.out index e10c1b6998e1..2359a67c02c1 100644 --- a/src/test/regress/expected/returning_gp.out +++ b/src/test/regress/expected/returning_gp.out @@ -1,6 +1,7 @@ -- -- Extra GPDB tests on INSERT/UPDATE/DELETE RETURNING -- +SET optimizer_trace_fallback=ON; CREATE TABLE returning_parttab (distkey int4, partkey int4, i int, t text) DISTRIBUTED BY (distkey) PARTITION BY RANGE (partkey) (START (1) END (10)); @@ -64,7 +65,7 @@ update returning_parttab set partkey = 19 where partkey = 13 returning *; 2 | 19 | multi2 3 (1 row) --- update that moves the tuple across partitions (not supported) +-- update that moves the tuple across partitions update returning_parttab set partkey = 18 where partkey = 4 returning *; ERROR: moving tuple from partition "returning_parttab_1_prt_1" to partition "returning_parttab_1_prt_newpart" not supported (seg0 slice1 127.0.0.1:40000 pid=5753) -- delete @@ -91,6 +92,37 @@ select * from returning_parttab; 2 | 19 | multi2 3 (11 rows) +-- Test DML on partitioned table with RETURNING subquery with cte +explain (costs off) +with cte as ( + select distkey from returning_parttab order by distkey limit 1 +) insert into returning_parttab values (1, 5, 'test') returning (select * from cte); + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + -> Insert on returning_parttab + InitPlan 1 (returns $0) (slice3) + -> Limit + -> Gather Motion 3:1 (slice1; segments: 3) + Merge Key: returning_parttab_1_prt_1.distkey + -> Limit + -> Sort + Sort Key: returning_parttab_1_prt_1.distkey + -> Append + -> Seq Scan on returning_parttab_1_prt_1 + -> Seq Scan on returning_parttab_1_prt_newpart + -> Result + Optimizer: Postgres query optimizer +(14 rows) + +with cte as ( + select distkey from returning_parttab order by distkey limit 1 +) insert into returning_parttab values (1, 5, 'test') returning (select * from cte); + distkey +--------- + 1 +(1 row) + -- -- Test UPDATE RETURNING with a split update, i.e. an update of the distribution -- key. diff --git a/src/test/regress/expected/returning_gp_optimizer.out b/src/test/regress/expected/returning_gp_optimizer.out new file mode 100644 index 000000000000..c9f8d5680d0a --- /dev/null +++ b/src/test/regress/expected/returning_gp_optimizer.out @@ -0,0 +1,231 @@ +-- +-- Extra GPDB tests on INSERT/UPDATE/DELETE RETURNING +-- +SET optimizer_trace_fallback=ON; +CREATE TABLE returning_parttab (distkey int4, partkey int4, i int, t text) +DISTRIBUTED BY (distkey) +PARTITION BY RANGE (partkey) (START (1) END (10)); +NOTICE: CREATE TABLE will create partition "returning_parttab_1_prt_1" for table "returning_parttab" +-- +-- Test INSERT RETURNING with partitioning +-- +insert into returning_parttab values (1, 1, 1, 'single insert') returning *; + distkey | partkey | i | t +---------+---------+---+--------------- + 1 | 1 | 1 | single insert +(1 row) + +insert into returning_parttab +select 1, g, g, 'multi ' || g from generate_series(1, 5) g +returning distkey, partkey, i, t; + distkey | partkey | i | t +---------+---------+---+--------- + 1 | 1 | 1 | multi 1 + 1 | 2 | 2 | multi 2 + 1 | 3 | 3 | multi 3 + 1 | 4 | 4 | multi 4 + 1 | 5 | 5 | multi 5 +(5 rows) + +-- Drop a column, and create a new partition. The new partition will not have +-- the dropped column, while in the old partition, it's still physically there, +-- just marked as dropped. Make sure the executor maps the columns correctly. +ALTER TABLE returning_parttab DROP COLUMN i; +alter table returning_parttab add partition newpart start (10) end (20); +NOTICE: CREATE TABLE will create partition "returning_parttab_1_prt_newpart" for table "returning_parttab" +insert into returning_parttab values (1, 10, 'single2 insert') returning *; + distkey | partkey | t +---------+---------+---------------- + 1 | 10 | single2 insert +(1 row) + +insert into returning_parttab select 2, g + 10, 'multi2 ' || g from generate_series(1, 5) g +returning distkey, partkey, t; + distkey | partkey | t +---------+---------+---------- + 2 | 11 | multi2 1 + 2 | 12 | multi2 2 + 2 | 13 | multi2 3 + 2 | 14 | multi2 4 + 2 | 15 | multi2 5 +(5 rows) + +-- +-- Test UPDATE/DELETE RETURNING with partitioning +-- +update returning_parttab set partkey = 9 where partkey = 3 returning *; + distkey | partkey | t +---------+---------+--------- + 1 | 9 | multi 3 +(1 row) + +update returning_parttab set partkey = 19 where partkey = 13 returning *; + distkey | partkey | t +---------+---------+---------- + 2 | 19 | multi2 3 +(1 row) + +-- update that moves the tuple across partitions +update returning_parttab set partkey = 18 where partkey = 4 returning *; + distkey | partkey | t +---------+---------+--------- + 1 | 18 | multi 4 +(1 row) + +-- delete +delete from returning_parttab where partkey = 14 returning *; + distkey | partkey | t +---------+---------+---------- + 2 | 14 | multi2 4 +(1 row) + +-- Check table contents, to be sure that all the commands did what they claimed. +select * from returning_parttab; + distkey | partkey | t +---------+---------+---------------- + 1 | 1 | single insert + 1 | 1 | multi 1 + 1 | 2 | multi 2 + 1 | 5 | multi 5 + 1 | 9 | multi 3 + 1 | 10 | single2 insert + 1 | 18 | multi 4 + 2 | 11 | multi2 1 + 2 | 12 | multi2 2 + 2 | 15 | multi2 5 + 2 | 19 | multi2 3 +(11 rows) + +-- Test DML on partitioned table with RETURNING subquery with cte +explain (costs off) +with cte as ( + select distkey from returning_parttab order by distkey limit 1 +) insert into returning_parttab values (1, 5, 'test') returning (select * from cte); +INFO: GPORCA failed to produce a plan, falling back to planner +DETAIL: No plan has been computed for required properties + QUERY PLAN +----------------------------------------------------------------------------------- + Gather Motion 3:1 (slice2; segments: 3) + -> Insert on returning_parttab + InitPlan 1 (returns $0) (slice3) + -> Limit + -> Gather Motion 3:1 (slice1; segments: 3) + Merge Key: returning_parttab_1_prt_1.distkey + -> Limit + -> Sort + Sort Key: returning_parttab_1_prt_1.distkey + -> Append + -> Seq Scan on returning_parttab_1_prt_1 + -> Seq Scan on returning_parttab_1_prt_newpart + -> Result + Optimizer: Postgres query optimizer +(14 rows) + +with cte as ( + select distkey from returning_parttab order by distkey limit 1 +) insert into returning_parttab values (1, 5, 'test') returning (select * from cte); +INFO: GPORCA failed to produce a plan, falling back to planner +DETAIL: No plan has been computed for required properties + distkey +--------- + 1 +(1 row) + +-- +-- Test UPDATE RETURNING with a split update, i.e. an update of the distribution +-- key. +-- +CREATE TEMP TABLE returning_disttest (id int4) DISTRIBUTED BY (id); +INSERT INTO returning_disttest VALUES (1), (2); +-- Disable QUIET mode, so that we get some testing of the command tag as well. +-- (At one point, each split update incorrectly counted as two updated rows.) +\set QUIET off +UPDATE returning_disttest SET id = id + 1; +UPDATE 2 +SELECT * FROM returning_disttest; + id +---- + 3 + 2 +(2 rows) + +-- +-- Test returning ctid with trigger +-- +CREATE TABLE returning_ctid (f1 serial, f2 text) DISTRIBUTED BY (f1); +CREATE TABLE +-- Create function used by trigger +CREATE FUNCTION trig_row_before_insupdate() RETURNS TRIGGER AS $$ + BEGIN + NEW.f2 := NEW.f2 || ' triggered !'; + RETURN NEW; + END +$$ language plpgsql; +CREATE FUNCTION +-- Create trigger for each row insert or update +CREATE TRIGGER trig_row_before BEFORE INSERT OR UPDATE ON returning_ctid +FOR EACH ROW EXECUTE PROCEDURE trig_row_before_insupdate(); +CREATE TRIGGER +-- Check returning sys attribute on insert +INSERT INTO returning_ctid(f2) VALUES ('test') RETURNING ctid; +INFO: GPORCA failed to produce a plan, falling back to planner +DETAIL: Feature not supported: INSERT with triggers + ctid +------- + (0,1) +(1 row) + +INSERT 0 1 +SELECT *, ctid FROM returning_ctid; + f1 | f2 | ctid +----+------------------+------- + 1 | test triggered ! | (0,1) +(1 row) + +-- Clean up +DROP TRIGGER trig_row_before ON returning_ctid; +DROP TRIGGER +DROP FUNCTION trig_row_before_insupdate() CASCADE; +DROP FUNCTION +DROP TABLE returning_ctid; +DROP TABLE +-- +-- Test returning ctid with trigger for AOCO table +-- +CREATE TABLE returning_ctid_aoco (f1 serial, f2 text) WITH (appendonly=true, orientation=column) DISTRIBUTED BY (f1); +CREATE TABLE +-- Create function used by trigger +CREATE FUNCTION trig_row_before_insupdate() RETURNS TRIGGER AS $$ + BEGIN + NEW.f2 := NEW.f2 || ' triggered !'; + RETURN NEW; + END +$$ language plpgsql; +CREATE FUNCTION +-- Create trigger for each row insert +CREATE TRIGGER trig_row_before BEFORE INSERT ON returning_ctid_aoco +FOR EACH ROW EXECUTE PROCEDURE trig_row_before_insupdate(); +CREATE TRIGGER +-- Check returning sys attribute on insert +INSERT INTO returning_ctid_aoco(f2) VALUES ('test') RETURNING ctid; +INFO: GPORCA failed to produce a plan, falling back to planner +DETAIL: Feature not supported: INSERT with triggers + ctid +-------------- + (33554432,2) +(1 row) + +INSERT 0 1 +SELECT *, ctid FROM returning_ctid_aoco; + f1 | f2 | ctid +----+------------------+-------------- + 1 | test triggered ! | (33554432,2) +(1 row) + +-- Clean up +DROP TRIGGER trig_row_before ON returning_ctid_aoco; +DROP TRIGGER +DROP FUNCTION trig_row_before_insupdate() CASCADE; +DROP FUNCTION +DROP TABLE returning_ctid_aoco; +DROP TABLE diff --git a/src/test/regress/expected/subselect_gp_optimizer.out b/src/test/regress/expected/subselect_gp_optimizer.out index 3a37f5a98a9c..22fa78ed143a 100644 --- a/src/test/regress/expected/subselect_gp_optimizer.out +++ b/src/test/regress/expected/subselect_gp_optimizer.out @@ -3669,25 +3669,26 @@ select i from t2 where exists (select i from cte); QUERY PLAN -------------------------------------------------------------------------------------------------- - Gather Motion 3:1 (slice4; segments: 3) - -> Result - One-Time Filter: $1 - InitPlan 1 (returns $1) (slice5) - -> Limit - -> Gather Motion 3:1 (slice3; segments: 3) - -> Limit - -> Delete on t1 - -> Explicit Redistribute Motion 3:3 (slice2; segments: 3) - -> Hash Join - Hash Cond: (t1.i = t2_1.i) - -> Redistribute Motion 3:3 (slice1; segments: 3) - Hash Key: t1.i - -> Seq Scan on t1 - -> Hash - -> Seq Scan on t2 t2_1 - -> Seq Scan on t2 - Optimizer: Postgres query optimizer -(18 rows) + Gather Motion 1:1 (slice4; segments: 1) + -> Nested Loop + Join Filter: true + -> Result + Filter: ((count()) > '0'::bigint) + -> Aggregate + -> Gather Motion 3:1 (slice3; segments: 3) + -> Delete + -> Result + -> Hash Join + Hash Cond: (t1.i = t2_1.i) + -> Seq Scan on t1 + -> Hash + -> Broadcast Motion 3:3 (slice2; segments: 3) + -> Seq Scan on t2 t2_1 + -> Materialize + -> Gather Motion 3:1 (slice1; segments: 3) + -> Seq Scan on t2 + Optimizer: Pivotal Optimizer (GPORCA) +(19 rows) with cte as (delete from t1 @@ -3730,19 +3731,26 @@ select i from t2 where 0 = (select i from cte); QUERY PLAN -------------------------------------------------------------------------- - Gather Motion 3:1 (slice3; segments: 3) - -> Result - One-Time Filter: (0 = $1) - InitPlan 1 (returns $1) (slice4) - -> Gather Motion 3:1 (slice2; segments: 3) - -> Update on t1 - -> Redistribute Motion 3:3 (slice1; segments: 3) - Hash Key: "outer".i - -> Split - -> Seq Scan on t1 - -> Seq Scan on t2 - Optimizer: Postgres query optimizer -(12 rows) + Gather Motion 1:1 (slice4; segments: 1) + -> Nested Loop + Join Filter: true + -> Result + Filter: (0 = i) + -> Assert + Assert Cond: ((row_number() OVER (?)) = 1) + -> WindowAgg + -> Gather Motion 3:1 (slice3; segments: 3) + -> Update + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: t1.i + -> Split + -> Result + -> Seq Scan on t1 + -> Materialize + -> Gather Motion 3:1 (slice1; segments: 3) + -> Seq Scan on t2 + Optimizer: Pivotal Optimizer (GPORCA) +(19 rows) with cte as (update t1 set i = 0 @@ -3784,25 +3792,23 @@ where t2.i in (select i from cte where t2.i = cte.i and cte.i > 0) order by i; QUERY PLAN -------------------------------------------------------------------------------------------------- - Gather Motion 3:1 (slice3; segments: 3) + Gather Motion 3:1 (slice1; segments: 3) Merge Key: t2.i -> Sort Sort Key: t2.i - -> Seq Scan on t2 - Filter: (SubPlan 1) - SubPlan 1 (slice3; segments: 3) - -> Result - Filter: (t2.i = cte.i) - -> Materialize - -> Broadcast Motion 3:3 (slice2; segments: 3) - -> Subquery Scan on cte - Filter: (cte.i > 0) - -> Insert on t1 - -> Redistribute Motion 1:3 (slice1; segments: 1) - Hash Key: i.i - -> Function Scan on generate_series i - Optimizer: Postgres query optimizer -(18 rows) + -> Hash Semi Join + Hash Cond: ((t2.i = i) AND (t2.i = i)) + -> Seq Scan on t2 + Filter: (i > 0) + -> Hash + -> Result + Filter: (i > 0) + -> Insert + -> Result + -> Result + -> Function Scan on generate_series + Optimizer: Pivotal Optimizer (GPORCA) +(16 rows) with cte as (insert into t1 @@ -3839,23 +3845,23 @@ where t_repl.i in (select i from cte where t_repl.i = cte.i and cte.i > 0) order by i; QUERY PLAN -------------------------------------------------------------------------------------------------- - Gather Motion 1:1 (slice3; segments: 1) + Gather Motion 3:1 (slice1; segments: 3) + Merge Key: t_repl.i -> Sort Sort Key: t_repl.i - -> Seq Scan on t_repl - Filter: (SubPlan 1) - SubPlan 1 (slice3; segments: 1) - -> Result - Filter: (t_repl.i = cte.i) - -> Materialize - -> Gather Motion 3:1 (slice2; segments: 3) - -> Subquery Scan on cte - Filter: (cte.i > 0) - -> Insert on t1 - -> Redistribute Motion 1:3 (slice1; segments: 1) - Hash Key: i.i - -> Function Scan on generate_series i - Optimizer: Postgres query optimizer + -> Hash Semi Join + Hash Cond: ((t_repl.i = i) AND (t_repl.i = i)) + -> Result + -> Seq Scan on t_repl + Filter: (i > 0) + -> Hash + -> Result + Filter: (i > 0) + -> Insert + -> Result + -> Result + -> Function Scan on generate_series + Optimizer: Pivotal Optimizer (GPORCA) (17 rows) with cte as @@ -3893,30 +3899,26 @@ where t2.i in (select i from cte join t3 using (i) where t3.j = t2.i) order by i; QUERY PLAN -------------------------------------------------------------------------------------------------- - Gather Motion 3:1 (slice4; segments: 3) + Gather Motion 3:1 (slice2; segments: 3) Merge Key: t2.i -> Sort Sort Key: t2.i - -> Seq Scan on t2 - Filter: (SubPlan 1) - SubPlan 1 (slice4; segments: 3) - -> Hash Join - Hash Cond: (t1.i = t3.i) - -> Result - -> Materialize - -> Broadcast Motion 3:3 (slice2; segments: 3) - -> Insert on t1 - -> Redistribute Motion 1:3 (slice1; segments: 1) - Hash Key: i.i - -> Function Scan on generate_series i - -> Hash - -> Result - Filter: (t3.j = t2.i) - -> Materialize - -> Broadcast Motion 3:3 (slice3; segments: 3) - -> Seq Scan on t3 - Optimizer: Postgres query optimizer -(23 rows) + -> Hash Semi Join + Hash Cond: ((t2.i = t3.j) AND (t2.i = i)) + -> Seq Scan on t2 + Filter: (NOT (i IS NULL)) + -> Hash + -> Hash Join + Hash Cond: (i = t3.i) + -> Insert + -> Result + -> Result + -> Function Scan on generate_series + -> Hash + -> Broadcast Motion 3:3 (slice1; segments: 3) + -> Seq Scan on t3 + Optimizer: Pivotal Optimizer (GPORCA) +(19 rows) with cte as (insert into t1 @@ -3947,29 +3949,27 @@ where t_repl.i in (select i from cte join t3 using (i) where t3.j = t_repl.i) order by i; QUERY PLAN -------------------------------------------------------------------------------------------------- - Gather Motion 1:1 (slice4; segments: 1) + Gather Motion 3:1 (slice2; segments: 3) + Merge Key: t_repl.i -> Sort Sort Key: t_repl.i - -> Seq Scan on t_repl - Filter: (SubPlan 1) - SubPlan 1 (slice4; segments: 3) - -> Hash Join - Hash Cond: (t1.i = t3.i) - -> Result - -> Materialize - -> Gather Motion 3:1 (slice2; segments: 3) - -> Insert on t1 - -> Redistribute Motion 1:3 (slice1; segments: 1) - Hash Key: i.i - -> Function Scan on generate_series i - -> Hash - -> Result - Filter: (t3.j = t_repl.i) - -> Materialize - -> Gather Motion 3:1 (slice3; segments: 3) - -> Seq Scan on t3 - Optimizer: Postgres query optimizer -(22 rows) + -> Hash Semi Join + Hash Cond: ((t_repl.i = t3.j) AND (t_repl.i = i)) + -> Result + -> Seq Scan on t_repl + Filter: (NOT (i IS NULL)) + -> Hash + -> Hash Join + Hash Cond: (i = t3.i) + -> Insert + -> Result + -> Result + -> Function Scan on generate_series + -> Hash + -> Broadcast Motion 3:3 (slice1; segments: 3) + -> Seq Scan on t3 + Optimizer: Pivotal Optimizer (GPORCA) +(20 rows) with cte as (insert into t1 diff --git a/src/test/regress/expected/updatable_views_optimizer.out b/src/test/regress/expected/updatable_views_optimizer.out index 8e10f46593d8..00274e305895 100644 --- a/src/test/regress/expected/updatable_views_optimizer.out +++ b/src/test/regress/expected/updatable_views_optimizer.out @@ -913,12 +913,14 @@ UPDATE rw_view1 v SET bb='Updated row 2' WHERE rw_view1_aa(v)=2 RETURNING rw_view1_aa(v), v.bb; QUERY PLAN ------------------------------------------ - Gather Motion 1:1 (slice1; segments: 1) - -> Update on base_tbl - -> Seq Scan on base_tbl - Filter: (a = 2) - Optimizer: Postgres query optimizer -(5 rows) + Gather Motion 3:1 (slice1; segments: 3) + -> Update + -> Split + -> Result + -> Index Scan using base_tbl_pkey on base_tbl + Index Cond: (a = 2) + Optimizer: Pivotal Optimizer (GPORCA) +(7 rows) DROP TABLE base_tbl CASCADE; NOTICE: drop cascades to 2 other objects @@ -1112,14 +1114,17 @@ EXPLAIN (verbose, costs off) UPDATE rw_view1 SET b = b + 1 RETURNING *; QUERY PLAN ------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) - Output: base_tbl.a, base_tbl.b - -> Update on public.base_tbl - Output: base_tbl.a, base_tbl.b - -> Seq Scan on public.base_tbl - Output: base_tbl.a, (base_tbl.b + 1), base_tbl.ctid, base_tbl.gp_segment_id - Optimizer: Postgres query optimizer - Settings: optimizer=off -(8 rows) + Output: a, b + -> Update + Output: a, b + -> Split + Output: base_tbl.a, base_tbl.b, base_tbl.ctid, base_tbl.gp_segment_id, DMLAction + -> Result + Output: base_tbl.a, base_tbl.b, (base_tbl.b + 1), base_tbl.ctid, base_tbl.gp_segment_id + -> Seq Scan on public.base_tbl + Output: base_tbl.a, base_tbl.b, base_tbl.ctid, base_tbl.gp_segment_id + Optimizer: Pivotal Optimizer (GPORCA) +(12 rows) UPDATE rw_view1 SET b = b + 1 RETURNING *; a | b @@ -2054,11 +2059,11 @@ EXPLAIN (costs off) INSERT INTO rw_view1 VALUES (2, 'New row 2'); Hash Key: "outer".id -> Result -> GroupAggregate - Group Key: "outer"., "outer".ColRef_0015 + Group Key: "outer"., "outer".ColRef_0025 -> Sort - Sort Key: "outer"., "outer".ColRef_0015 + Sort Key: "outer"., "outer".ColRef_0025 -> Result - Filter: (CASE WHEN (NOT ("outer".ColRef_0015 IS NULL)) THEN true ELSE false END IS NOT TRUE) + Filter: (CASE WHEN (NOT ("outer".ColRef_0025 IS NULL)) THEN true ELSE false END IS NOT TRUE) -> Nested Loop Left Join Join Filter: true -> Result diff --git a/src/test/regress/expected/update_gp_optimizer.out b/src/test/regress/expected/update_gp_optimizer.out index bee8de0f2b42..90fab208e190 100644 --- a/src/test/regress/expected/update_gp_optimizer.out +++ b/src/test/regress/expected/update_gp_optimizer.out @@ -258,19 +258,20 @@ WITH CTE AS (DELETE FROM t1 RETURNING *) SELECT count(*) AS a FROM t_strewn JOIN cte USING (i); QUERY PLAN --------------------------------------------------------------------------- - Aggregate - -> Gather Motion 3:1 (slice2; segments: 3) - -> Aggregate + Gather Motion 1:1 (slice3; segments: 1) + -> Aggregate + -> Gather Motion 3:1 (slice2; segments: 3) -> Hash Join - Hash Cond: (t_strewn.i = cte.i) - -> Seq Scan on t_strewn + Hash Cond: (t_strewn.i = i) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: t_strewn.i + -> Seq Scan on t_strewn -> Hash - -> Broadcast Motion 3:3 (slice1; segments: 3) - -> Subquery Scan on cte - -> Delete on t1 - -> Seq Scan on t1 - Optimizer: Postgres query optimizer -(12 rows) + -> Delete + -> Result + -> Seq Scan on t1 + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) WITH CTE AS (DELETE FROM t1 RETURNING *) SELECT count(*) AS a FROM t_strewn JOIN cte USING (i); diff --git a/src/test/regress/expected/with_clause.out b/src/test/regress/expected/with_clause.out index ffbf1a8a8935..b75531a79e78 100644 --- a/src/test/regress/expected/with_clause.out +++ b/src/test/regress/expected/with_clause.out @@ -2417,6 +2417,39 @@ create table t_new as (with cte as (delete from with_dml where i > 0 returning *) select * from cte); ERROR: cannot create plan with several writing gangs +-- Test usage of system columns returned from DML operations +explain (costs off) +with cte as ( + insert into with_dml select i, i * 100 from generate_series(1,5) i + returning i, gp_segment_id +) select i from cte order by gp_segment_id; + QUERY PLAN +------------------------------------------------------------------------ + Gather Motion 3:1 (slice2; segments: 3) + Merge Key: cte.gp_segment_id + -> Sort + Sort Key: cte.gp_segment_id + -> Subquery Scan on cte + -> Insert on with_dml + -> Redistribute Motion 1:3 (slice1; segments: 1) + Hash Key: i.i + -> Function Scan on generate_series i + Optimizer: Postgres query optimizer +(10 rows) + +with cte as ( + insert into with_dml select i, i * 100 from generate_series(1,5) i + returning i, gp_segment_id +) select i from cte order by gp_segment_id; + i +--- + 3 + 4 + 2 + 1 + 5 +(5 rows) + drop table with_dml; -- Test various SELECT statements from CTE with -- modifying DML operations over replicated tables diff --git a/src/test/regress/expected/with_clause_optimizer.out b/src/test/regress/expected/with_clause_optimizer.out index 591f7a127b75..c278ab93d77c 100644 --- a/src/test/regress/expected/with_clause_optimizer.out +++ b/src/test/regress/expected/with_clause_optimizer.out @@ -2304,17 +2304,18 @@ with cte as ( ) select count(*) from cte where i > 2; QUERY PLAN ------------------------------------------------------------------------------ - Aggregate - -> Gather Motion 3:1 (slice2; segments: 3) - -> Aggregate - -> Subquery Scan on cte - Filter: (cte.i > 2) - -> Insert on with_dml - -> Redistribute Motion 1:3 (slice1; segments: 1) - Hash Key: i.i - -> Function Scan on generate_series i - Optimizer: Postgres query optimizer -(10 rows) + Gather Motion 1:1 (slice2; segments: 1) + -> Aggregate + -> Gather Motion 3:1 (slice1; segments: 3) + -> Aggregate + -> Result + Filter: (i > 2) + -> Insert + -> Result + -> Result + -> Function Scan on generate_series + Optimizer: Pivotal Optimizer (GPORCA) +(11 rows) with cte as ( insert into with_dml select i, i * 100 from generate_series(1,5) i @@ -2338,19 +2339,90 @@ with cte as ( insert into with_dml select i, i * 100 from generate_series(1,5) i returning i ) select count(*) from cte where i < (select avg(i) from cte); -ERROR: Too much references to non-SELECT CTE (allpaths.c:2043) + QUERY PLAN +------------------------------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice5; segments: 3) + -> Sequence + -> Shared Scan (share slice:id 5:0) + -> Materialize + -> Insert + -> Result + -> Result + -> Function Scan on generate_series + -> Redistribute Motion 1:3 (slice4; segments: 1) + -> Aggregate + -> Gather Motion 3:1 (slice3; segments: 3) + -> Aggregate + -> Nested Loop + Join Filter: ((share0_ref2.i)::numeric < (pg_catalog.avg((avg(share0_ref3.i))))) + -> Broadcast Motion 1:3 (slice2; segments: 1) + -> Aggregate + -> Gather Motion 3:1 (slice1; segments: 3) + -> Aggregate + -> Shared Scan (share slice:id 1:0) + -> Materialize + -> Shared Scan (share slice:id 3:0) + Optimizer: Pivotal Optimizer (GPORCA) +(22 rows) + explain (costs off) with cte as ( update with_dml set j = j + 1 returning i ) select count(*) from cte where i < (select avg(i) from cte); -ERROR: Too much references to non-SELECT CTE (allpaths.c:2043) + QUERY PLAN +------------------------------------------------------------------------------ + Gather Motion 3:1 (slice4; segments: 3) + -> Sequence + -> Shared Scan (share slice:id 4:0) + -> Materialize + -> Update + -> Split + -> Result + -> Seq Scan on with_dml + -> Redistribute Motion 1:3 (slice3; segments: 1) + -> Aggregate + -> Nested Loop + Join Filter: ((share0_ref2.i)::numeric < (pg_catalog.avg((avg(share0_ref3.i))))) + -> Aggregate + -> Gather Motion 3:1 (slice2; segments: 3) + -> Aggregate + -> Shared Scan (share slice:id 2:0) + -> Materialize + -> Gather Motion 3:1 (slice1; segments: 3) + -> Shared Scan (share slice:id 1:0) + Optimizer: Pivotal Optimizer (GPORCA) +(20 rows) + explain (costs off) with cte as ( delete from with_dml where i > 0 returning i ) select count(*) from cte where i < (select avg(i) from cte); -ERROR: Too much references to non-SELECT CTE (allpaths.c:2043) + QUERY PLAN +------------------------------------------------------------------------------ + Gather Motion 3:1 (slice4; segments: 3) + -> Sequence + -> Shared Scan (share slice:id 4:0) + -> Materialize + -> Delete + -> Result + -> Seq Scan on with_dml + Filter: (i > 0) + -> Redistribute Motion 1:3 (slice3; segments: 1) + -> Aggregate + -> Nested Loop + Join Filter: ((share0_ref2.i)::numeric < (pg_catalog.avg((avg(share0_ref3.i))))) + -> Aggregate + -> Gather Motion 3:1 (slice2; segments: 3) + -> Aggregate + -> Shared Scan (share slice:id 2:0) + -> Materialize + -> Gather Motion 3:1 (slice1; segments: 3) + -> Shared Scan (share slice:id 1:0) + Optimizer: Pivotal Optimizer (GPORCA) +(20 rows) + explain (costs off) with cte as ( insert into with_dml @@ -2359,7 +2431,24 @@ with cte as ( cte2 as ( select * from cte) select * from cte2 a join cte2 b using (i); -ERROR: Too much references to non-SELECT CTE (allpaths.c:2043) + QUERY PLAN +---------------------------------------------------------------------------- + Gather Motion 3:1 (slice1; segments: 3) + -> Sequence + -> Shared Scan (share slice:id 1:1) + -> Materialize + -> Insert + -> Result + -> Result + -> Function Scan on generate_series + -> Hash Join + Hash Cond: (share1_ref3.i = share1_ref2.i) + -> Shared Scan (share slice:id 1:1) + -> Hash + -> Shared Scan (share slice:id 1:1) + Optimizer: Pivotal Optimizer (GPORCA) +(14 rows) + create table with_dml_repl (i int, j int) distributed replicated; explain (costs off) with cte as ( @@ -2369,7 +2458,29 @@ with cte as ( cte2 as ( select * from cte) select * from cte2 a join cte2 b using (i); -ERROR: Too much references to non-SELECT CTE (allpaths.c:2043) + QUERY PLAN +---------------------------------------------------------------------------------- + Gather Motion 3:1 (slice3; segments: 3) + -> Sequence + -> Shared Scan (share slice:id 3:1) + -> Materialize + -> Result + One-Time Filter: (gp_execution_segment() = 0) + -> Insert + -> Result + -> Function Scan on generate_series + -> Hash Join + Hash Cond: (share1_ref3.i = share1_ref2.i) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: share1_ref3.i + -> Shared Scan (share slice:id 1:1) + -> Hash + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: share1_ref2.i + -> Shared Scan (share slice:id 2:1) + Optimizer: Pivotal Optimizer (GPORCA) +(19 rows) + explain (costs off) with recursive cte as ( select 1 as i from with_dml where with_dml.j = 2 @@ -2420,6 +2531,39 @@ create table t_new as (with cte as (delete from with_dml where i > 0 returning *) select * from cte); ERROR: cannot create plan with several writing gangs +-- Test usage of system columns returned from DML operations +explain (costs off) +with cte as ( + insert into with_dml select i, i * 100 from generate_series(1,5) i + returning i, gp_segment_id +) select i from cte order by gp_segment_id; + QUERY PLAN +---------------------------------------------------------------------- + Result + -> Gather Motion 3:1 (slice1; segments: 3) + Merge Key: gp_segment_id + -> Sort + Sort Key: gp_segment_id + -> Insert + -> Result + -> Result + -> Function Scan on generate_series + Optimizer: Pivotal Optimizer (GPORCA) +(10 rows) + +with cte as ( + insert into with_dml select i, i * 100 from generate_series(1,5) i + returning i, gp_segment_id +) select i from cte order by gp_segment_id; + i +--- + 3 + 4 + 2 + 1 + 5 +(5 rows) + drop table with_dml; -- Test various SELECT statements from CTE with -- modifying DML operations over replicated tables @@ -2440,10 +2584,11 @@ with cte as ( ------------------------------------------------------ Explicit Gather Motion 3:1 (slice1; segments: 3) -> Aggregate - -> Insert on with_dml_dr - -> Function Scan on generate_series i - Optimizer: Postgres query optimizer -(5 rows) + -> Insert + -> Result + -> Function Scan on generate_series + Optimizer: Pivotal Optimizer (GPORCA) +(6 rows) with cte as ( insert into with_dml_dr @@ -2513,16 +2658,17 @@ with cte as ( select i, i * 100 from generate_series(1,5) i returning i ) select * from cte order by i; - QUERY PLAN ------------------------------------------------------- + QUERY PLAN +---------------------------------------------------------- Explicit Gather Motion 3:1 (slice1; segments: 3) - Merge Key: with_dml_dr.i + Merge Key: i -> Sort - Sort Key: with_dml_dr.i - -> Insert on with_dml_dr - -> Function Scan on generate_series i - Optimizer: Postgres query optimizer -(7 rows) + Sort Key: i + -> Insert + -> Result + -> Function Scan on generate_series + Optimizer: Pivotal Optimizer (GPORCA) +(8 rows) with cte as ( insert into with_dml_dr @@ -2559,13 +2705,14 @@ with cte as ( Explicit Gather Motion 3:1 (slice1; segments: 3) -> Aggregate -> Hash Join - Hash Cond: (with_dml_dr.i = t_repl.i) - -> Insert on with_dml_dr - -> Function Scan on generate_series i + Hash Cond: (i = t_repl.i) + -> Insert + -> Result + -> Function Scan on generate_series -> Hash -> Seq Scan on t_repl - Optimizer: Postgres query optimizer -(9 rows) + Optimizer: Pivotal Optimizer (GPORCA) +(10 rows) with cte as ( insert into with_dml_dr @@ -2733,16 +2880,21 @@ select count(*) from cte join (select a from generate_series(1,5) a) x on cte.i = x.a; QUERY PLAN ------------------------------------------------------------ - Explicit Gather Motion 3:1 (slice1; segments: 3) + Gather Motion 1:1 (slice2; segments: 1) -> Aggregate - -> Hash Join - Hash Cond: (with_dml_dr.i = a.a) - -> Insert on with_dml_dr - -> Function Scan on generate_series i - -> Hash - -> Function Scan on generate_series a - Optimizer: Postgres query optimizer -(9 rows) + -> Gather Motion 3:1 (slice1; segments: 3) + -> Aggregate + -> Hash Join + Hash Cond: (i = generate_series_1.generate_series) + -> Result + -> Insert + -> Result + -> Function Scan on generate_series + -> Hash + -> Result + -> Function Scan on generate_series generate_series_1 + Optimizer: Pivotal Optimizer (GPORCA) +(14 rows) with cte as ( insert into with_dml_dr @@ -2775,17 +2927,20 @@ with cte as ( ) select count(*) from cte join t_hashed on cte.i = t_hashed.i; QUERY PLAN ------------------------------------------------------------------ - Aggregate - -> Gather Motion 3:1 (slice1; segments: 3) - -> Aggregate - -> Hash Join - Hash Cond: (with_dml_dr.i = t_hashed.i) - -> Insert on with_dml_dr - -> Function Scan on generate_series i - -> Hash - -> Seq Scan on t_hashed - Optimizer: Postgres query optimizer -(10 rows) + Gather Motion 1:1 (slice2; segments: 1) + -> Aggregate + -> Gather Motion 3:1 (slice1; segments: 3) + -> Aggregate + -> Hash Join + Hash Cond: (i = t_hashed.i) + -> Result + -> Insert + -> Result + -> Function Scan on generate_series + -> Hash + -> Seq Scan on t_hashed + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) with cte as ( insert into with_dml_dr @@ -2805,17 +2960,20 @@ with cte as ( ) select count(*) from cte left join t_hashed on cte.i = t_hashed.i; QUERY PLAN --------------------------------------------------------------- - Aggregate - -> Hash Left Join - Hash Cond: (with_dml_dr.i = t_hashed.i) - -> Explicit Gather Motion 3:1 (slice1; segments: 3) - -> Insert on with_dml_dr - -> Function Scan on generate_series i - -> Hash - -> Gather Motion 3:1 (slice2; segments: 3) - -> Seq Scan on t_hashed - Optimizer: Postgres query optimizer -(10 rows) + Gather Motion 1:1 (slice2; segments: 1) + -> Aggregate + -> Gather Motion 3:1 (slice1; segments: 3) + -> Aggregate + -> Hash Left Join + Hash Cond: (i = t_hashed.i) + -> Result + -> Insert + -> Result + -> Function Scan on generate_series + -> Hash + -> Seq Scan on t_hashed + Optimizer: Pivotal Optimizer (GPORCA) +(13 rows) with cte as ( insert into with_dml_dr @@ -2835,17 +2993,22 @@ with cte as ( ) select count(*) from cte join t_strewn on cte.i = t_strewn.i; QUERY PLAN ------------------------------------------------------------------ - Aggregate - -> Gather Motion 3:1 (slice1; segments: 3) - -> Aggregate - -> Hash Join - Hash Cond: (with_dml_dr.i = t_strewn.i) - -> Insert on with_dml_dr - -> Function Scan on generate_series i - -> Hash - -> Seq Scan on t_strewn - Optimizer: Postgres query optimizer -(10 rows) + Gather Motion 1:1 (slice3; segments: 1) + -> Aggregate + -> Gather Motion 3:1 (slice2; segments: 3) + -> Aggregate + -> Hash Join + Hash Cond: (i = t_strewn.i) + -> Result + -> Insert + -> Result + -> Function Scan on generate_series + -> Hash + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: t_strewn.i + -> Seq Scan on t_strewn + Optimizer: Pivotal Optimizer (GPORCA) +(15 rows) with cte as ( insert into with_dml_dr @@ -2865,17 +3028,22 @@ with cte as ( ) select count(*) from cte left join t_strewn on cte.i = t_strewn.i; QUERY PLAN --------------------------------------------------------------- - Aggregate - -> Hash Left Join - Hash Cond: (with_dml_dr.i = t_strewn.i) - -> Explicit Gather Motion 3:1 (slice1; segments: 3) - -> Insert on with_dml_dr - -> Function Scan on generate_series i - -> Hash - -> Gather Motion 3:1 (slice2; segments: 3) - -> Seq Scan on t_strewn - Optimizer: Postgres query optimizer -(10 rows) + Gather Motion 1:1 (slice3; segments: 1) + -> Aggregate + -> Gather Motion 3:1 (slice2; segments: 3) + -> Aggregate + -> Hash Left Join + Hash Cond: (i = t_strewn.i) + -> Result + -> Insert + -> Result + -> Function Scan on generate_series + -> Hash + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: t_strewn.i + -> Seq Scan on t_strewn + Optimizer: Pivotal Optimizer (GPORCA) +(15 rows) with cte as ( insert into with_dml_dr @@ -2981,18 +3149,30 @@ with cte as ( ) select count(*) from cte a join cte b using (i); QUERY PLAN ------------------------------------------------------------------------------ - Explicit Gather Motion 3:1 (slice1; segments: 3) - -> Aggregate - -> Hash Join - Hash Cond: (share0_ref2.i = share0_ref1.i) - -> Shared Scan (share slice:id 1:0) - -> Hash - -> Shared Scan (share slice:id 1:0) - -> Materialize - -> Insert on with_dml_dr - -> Function Scan on generate_series i - Optimizer: Postgres query optimizer -(11 rows) + Gather Motion 3:1 (slice5; segments: 3) + -> Sequence + -> Shared Scan (share slice:id 5:0) + -> Materialize + -> Result + One-Time Filter: (gp_execution_segment() = 1) + -> Insert + -> Result + -> Function Scan on generate_series + -> Redistribute Motion 1:3 (slice4; segments: 1) + -> Aggregate + -> Gather Motion 3:1 (slice3; segments: 3) + -> Aggregate + -> Hash Join + Hash Cond: (share0_ref3.i = share0_ref2.i) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: share0_ref3.i + -> Shared Scan (share slice:id 1:0) + -> Hash + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: share0_ref2.i + -> Shared Scan (share slice:id 2:0) + Optimizer: Pivotal Optimizer (GPORCA) +(23 rows) with cte as ( insert into with_dml_dr @@ -3089,19 +3269,27 @@ where t1.i in (select i from cte) order by 1; QUERY PLAN -------------------------------------------------------------------------- - Gather Motion 3:1 (slice1; segments: 3) + Gather Motion 3:1 (slice2; segments: 3) Merge Key: t1.i -> Sort Sort Key: t1.i - -> Seq Scan on t1 - Filter: (hashed SubPlan 1) - SubPlan 1 (slice1; segments: 3) - -> Materialize - -> Subquery Scan on cte - -> Insert on with_dml_dr - -> Function Scan on generate_series i - Optimizer: Postgres query optimizer -(12 rows) + -> Hash Join + Hash Cond: (i = t1.i) + -> HashAggregate + Group Key: i + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: i + -> HashAggregate + Group Key: i + -> Result + One-Time Filter: (gp_execution_segment() = 1) + -> Insert + -> Result + -> Function Scan on generate_series + -> Hash + -> Seq Scan on t1 + Optimizer: Pivotal Optimizer (GPORCA) +(20 rows) with cte as ( insert into with_dml_dr @@ -3130,21 +3318,30 @@ where t1.i in (select i from cte where cte.i = t1.j) order by 1; QUERY PLAN -------------------------------------------------------------------------------- - Gather Motion 3:1 (slice1; segments: 3) + Gather Motion 3:1 (slice3; segments: 3) Merge Key: t1.i -> Sort Sort Key: t1.i - -> Seq Scan on t1 - Filter: (SubPlan 1) - SubPlan 1 (slice1; segments: 3) - -> Result - Filter: (cte.i = t1.j) - -> Materialize - -> Subquery Scan on cte - -> Insert on with_dml_dr - -> Function Scan on generate_series i - Optimizer: Postgres query optimizer -(14 rows) + -> Hash Join + Hash Cond: ((t1.j = i) AND (t1.i = i)) + -> Redistribute Motion 3:3 (slice1; segments: 3) + Hash Key: t1.j + -> Seq Scan on t1 + Filter: (NOT (j IS NULL)) + -> Hash + -> HashAggregate + Group Key: i + -> Redistribute Motion 3:3 (slice2; segments: 3) + Hash Key: i + -> HashAggregate + Group Key: i + -> Result + One-Time Filter: (gp_execution_segment() = 0) + -> Insert + -> Result + -> Function Scan on generate_series + Optimizer: Pivotal Optimizer (GPORCA) +(23 rows) with cte as ( insert into with_dml_dr @@ -3233,15 +3430,16 @@ order by 1; QUERY PLAN --------------------------------------------------- Explicit Gather Motion 3:1 (slice1; segments: 3) - Merge Key: with_dml_dr.i + Merge Key: i -> Sort - Sort Key: with_dml_dr.i + Sort Key: i -> Append - -> Insert on with_dml_dr + -> Insert -> Result + -> Result -> Seq Scan on t_repl - Optimizer: Postgres query optimizer -(9 rows) + Optimizer: Pivotal Optimizer (GPORCA) +(10 rows) with cte as ( insert into with_dml_dr diff --git a/src/test/regress/expected/with_optimizer.out b/src/test/regress/expected/with_optimizer.out new file mode 100644 index 000000000000..c23f56106073 --- /dev/null +++ b/src/test/regress/expected/with_optimizer.out @@ -0,0 +1,2411 @@ +-- +-- Tests for common table expressions (WITH query, ... SELECT ...) +-- +-- Basic WITH +WITH q1(x,y) AS (SELECT 1,2) +SELECT * FROM q1, q1 AS q2; + x | y | x | y +---+---+---+--- + 1 | 2 | 1 | 2 +(1 row) + +-- Multiple uses are evaluated only once +SELECT count(*) FROM ( + WITH q1(x) AS (SELECT random() FROM generate_series(1, 5)) + SELECT * FROM q1 + UNION + SELECT * FROM q1 +) ss; + count +------- + 5 +(1 row) + +-- WITH RECURSIVE +-- sum of 1..100 +WITH RECURSIVE t(n) AS ( + VALUES (1) +UNION ALL + SELECT n+1 FROM t WHERE n < 100 +) +SELECT sum(n) FROM t; + sum +------ + 5050 +(1 row) + +WITH RECURSIVE t(n) AS ( + SELECT (VALUES(1)) +UNION ALL + SELECT n+1 FROM t WHERE n < 5 +) +SELECT * FROM t; + n +--- + 1 + 2 + 3 + 4 + 5 +(5 rows) + +-- recursive view +CREATE RECURSIVE VIEW nums (n) AS + VALUES (1) +UNION ALL + SELECT n+1 FROM nums WHERE n < 5; +SELECT * FROM nums; + n +--- + 1 + 2 + 3 + 4 + 5 +(5 rows) + +CREATE OR REPLACE RECURSIVE VIEW nums (n) AS + VALUES (1) +UNION ALL + SELECT n+1 FROM nums WHERE n < 6; +SELECT * FROM nums; + n +--- + 1 + 2 + 3 + 4 + 5 + 6 +(6 rows) + +-- This is an infinite loop with UNION ALL, but not with UNION +WITH RECURSIVE t(n) AS ( + SELECT 1 +UNION + SELECT 10-n FROM t) +SELECT * FROM t; + n +--- + 1 + 9 +(2 rows) + +-- This'd be an infinite loop, but outside query reads only as much as needed +WITH RECURSIVE t(n) AS ( + VALUES (1) +UNION ALL + SELECT n+1 FROM t) +SELECT * FROM t LIMIT 10; + n +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 +(10 rows) + +-- UNION case should have same property +WITH RECURSIVE t(n) AS ( + SELECT 1 +UNION + SELECT n+1 FROM t) +SELECT * FROM t LIMIT 10; + n +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 +(10 rows) + +-- Test behavior with an unknown-type literal in the WITH +WITH q AS (SELECT 'foo' AS x) +SELECT x, x IS OF (unknown) as is_unknown FROM q; + x | is_unknown +-----+------------ + foo | t +(1 row) + +WITH RECURSIVE t(n) AS ( + SELECT 'foo' +UNION ALL + SELECT n || ' bar' FROM t WHERE length(n) < 20 +) +SELECT n, n IS OF (text) as is_text FROM t; + n | is_text +-------------------------+--------- + foo | t + foo bar | t + foo bar bar | t + foo bar bar bar | t + foo bar bar bar bar | t + foo bar bar bar bar bar | t +(6 rows) + +-- In a perfect world, this would work and resolve the literal as int ... +-- but for now, we have to be content with resolving to text too soon. +WITH RECURSIVE t(n) AS ( + SELECT '7' +UNION ALL + SELECT n+1 FROM t WHERE n < 10 +) +SELECT n, n IS OF (int) AS is_int FROM t; +ERROR: operator does not exist: text + integer +LINE 4: SELECT n+1 FROM t WHERE n < 10 + ^ +HINT: No operator matches the given name and argument type(s). You might need to add explicit type casts. +-- +-- Some examples with a tree +-- +-- department structure represented here is as follows: +-- +-- ROOT-+->A-+->B-+->C +-- | | +-- | +->D-+->F +-- +->E-+->G +CREATE TEMP TABLE department ( + id INTEGER PRIMARY KEY, -- department ID + parent_department INTEGER REFERENCES department, -- upper department ID + name TEXT -- department name +); +INSERT INTO department VALUES (0, NULL, 'ROOT'); +INSERT INTO department VALUES (1, 0, 'A'); +INSERT INTO department VALUES (2, 1, 'B'); +INSERT INTO department VALUES (3, 2, 'C'); +INSERT INTO department VALUES (4, 2, 'D'); +INSERT INTO department VALUES (5, 0, 'E'); +INSERT INTO department VALUES (6, 4, 'F'); +INSERT INTO department VALUES (7, 5, 'G'); +-- GPDB: Some of the queries below will return non-deterministic results +-- because of moving rows across segments. This table is the same, except that +-- all the rows reside on a single segment, so that you get consistent results. +CREATE TEMP TABLE department_oneseg AS SELECT 1 AS distkey, * FROM department DISTRIBUTED BY (distkey); +-- extract all departments under 'A'. Result should be A, B, C, D and F +WITH RECURSIVE subdepartment AS +( + -- non recursive term + SELECT name as root_name, * FROM department WHERE name = 'A' + UNION ALL + -- recursive term + SELECT sd.root_name, d.* FROM department AS d, subdepartment AS sd + WHERE d.parent_department = sd.id +) +SELECT * FROM subdepartment ORDER BY name; + root_name | id | parent_department | name +-----------+----+-------------------+------ + A | 1 | 0 | A + A | 2 | 1 | B + A | 3 | 2 | C + A | 4 | 2 | D + A | 6 | 4 | F +(5 rows) + +-- extract all departments under 'A' with "level" number +WITH RECURSIVE subdepartment(level, id, parent_department, name) AS +( + -- non recursive term + SELECT 1, * FROM department WHERE name = 'A' + UNION ALL + -- recursive term + SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd + WHERE d.parent_department = sd.id +) +SELECT * FROM subdepartment ORDER BY name; + level | id | parent_department | name +-------+----+-------------------+------ + 1 | 1 | 0 | A + 2 | 2 | 1 | B + 3 | 3 | 2 | C + 3 | 4 | 2 | D + 4 | 6 | 4 | F +(5 rows) + +-- extract all departments under 'A' with "level" number. +-- Only shows level 2 or more +WITH RECURSIVE subdepartment(level, id, parent_department, name) AS +( + -- non recursive term + SELECT 1, * FROM department WHERE name = 'A' + UNION ALL + -- recursive term + SELECT sd.level + 1, d.* FROM department AS d, subdepartment AS sd + WHERE d.parent_department = sd.id +) +SELECT * FROM subdepartment WHERE level >= 2 ORDER BY name; + level | id | parent_department | name +-------+----+-------------------+------ + 2 | 2 | 1 | B + 3 | 3 | 2 | C + 3 | 4 | 2 | D + 4 | 6 | 4 | F +(4 rows) + +-- "RECURSIVE" is ignored if the query has no self-reference +WITH RECURSIVE subdepartment AS +( + -- note lack of recursive UNION structure + SELECT * FROM department WHERE name = 'A' +) +SELECT * FROM subdepartment ORDER BY name; + id | parent_department | name +----+-------------------+------ + 1 | 0 | A +(1 row) + +-- inside subqueries +SELECT count(*) FROM ( + WITH RECURSIVE t(n) AS ( + SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 500 + ) + SELECT * FROM t) AS t WHERE n < ( + SELECT count(*) FROM ( + WITH RECURSIVE t(n) AS ( + SELECT 1 UNION ALL SELECT n + 1 FROM t WHERE n < 100 + ) + SELECT * FROM t WHERE n < 50000 + ) AS t WHERE n < 100); + count +------- + 98 +(1 row) + +-- use same CTE twice at different subquery levels +WITH q1(x,y) AS ( + SELECT hundred, sum(ten) FROM tenk1 GROUP BY hundred + ) +SELECT count(*) FROM q1 WHERE y > (SELECT sum(y)/100 FROM q1 qsub); + count +------- + 50 +(1 row) + +-- via a VIEW +CREATE TEMPORARY VIEW vsubdepartment AS + WITH RECURSIVE subdepartment AS + ( + -- non recursive term + SELECT * FROM department WHERE name = 'A' + UNION ALL + -- recursive term + SELECT d.* FROM department AS d, subdepartment AS sd + WHERE d.parent_department = sd.id + ) + SELECT * FROM subdepartment; +SELECT * FROM vsubdepartment ORDER BY name; + id | parent_department | name +----+-------------------+------ + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 6 | 4 | F +(5 rows) + +-- Check reverse listing +SELECT pg_get_viewdef('vsubdepartment'::regclass); + pg_get_viewdef +----------------------------------------------- + WITH RECURSIVE subdepartment AS ( + + SELECT department.id, + + department.parent_department, + + department.name + + FROM department + + WHERE (department.name = 'A'::text)+ + UNION ALL + + SELECT d.id, + + d.parent_department, + + d.name + + FROM department d, + + subdepartment sd + + WHERE (d.parent_department = sd.id)+ + ) + + SELECT subdepartment.id, + + subdepartment.parent_department, + + subdepartment.name + + FROM subdepartment; +(1 row) + +SELECT pg_get_viewdef('vsubdepartment'::regclass, true); + pg_get_viewdef +--------------------------------------------- + WITH RECURSIVE subdepartment AS ( + + SELECT department.id, + + department.parent_department, + + department.name + + FROM department + + WHERE department.name = 'A'::text+ + UNION ALL + + SELECT d.id, + + d.parent_department, + + d.name + + FROM department d, + + subdepartment sd + + WHERE d.parent_department = sd.id+ + ) + + SELECT subdepartment.id, + + subdepartment.parent_department, + + subdepartment.name + + FROM subdepartment; +(1 row) + +-- Another reverse-listing example +CREATE VIEW sums_1_100 AS +WITH RECURSIVE t(n) AS ( + VALUES (1) +UNION ALL + SELECT n+1 FROM t WHERE n < 100 +) +SELECT sum(n) FROM t; +\d+ sums_1_100 + View "public.sums_1_100" + Column | Type | Modifiers | Storage | Description +--------+--------+-----------+---------+------------- + sum | bigint | | plain | +View definition: + WITH RECURSIVE t(n) AS ( + VALUES (1) + UNION ALL + SELECT t_1.n + 1 + FROM t t_1 + WHERE t_1.n < 100 + ) + SELECT sum(t.n) AS sum + FROM t; + +-- corner case in which sub-WITH gets initialized first +with recursive q as ( + select * from department_oneseg + union all + (with x as (select * from q) + select * from x) + ) +select id, parent_department, name from q limit 24; + id | parent_department | name +----+-------------------+------ + 0 | | ROOT + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 5 | 0 | E + 6 | 4 | F + 7 | 5 | G + 0 | | ROOT + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 5 | 0 | E + 6 | 4 | F + 7 | 5 | G + 0 | | ROOT + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 5 | 0 | E + 6 | 4 | F + 7 | 5 | G +(24 rows) + +with recursive q as ( + select * from department_oneseg + union all + (with recursive x as ( + select * from department_oneseg + union all + (select * from q union all select * from x) + ) + select * from x) + ) +select id, parent_department, name from q limit 32; + id | parent_department | name +----+-------------------+------ + 0 | | ROOT + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 5 | 0 | E + 6 | 4 | F + 7 | 5 | G + 0 | | ROOT + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 5 | 0 | E + 6 | 4 | F + 7 | 5 | G + 0 | | ROOT + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 5 | 0 | E + 6 | 4 | F + 7 | 5 | G + 0 | | ROOT + 1 | 0 | A + 2 | 1 | B + 3 | 2 | C + 4 | 2 | D + 5 | 0 | E + 6 | 4 | F + 7 | 5 | G +(32 rows) + +-- recursive term has sub-UNION +WITH RECURSIVE t(i,j) AS ( + VALUES (1,2) + UNION ALL + SELECT t2.i, t.j+1 FROM + (SELECT 2 AS i UNION ALL SELECT 3 AS i) AS t2 + JOIN t ON (t2.i = t.i+1)) + SELECT * FROM t; + i | j +---+--- + 1 | 2 + 2 | 3 + 3 | 4 +(3 rows) + +-- +-- different tree example +-- +CREATE TEMPORARY TABLE tree( + id INTEGER PRIMARY KEY, + parent_id INTEGER REFERENCES tree(id) +); +INSERT INTO tree +VALUES (1, NULL), (2, 1), (3,1), (4,2), (5,2), (6,2), (7,3), (8,3), + (9,4), (10,4), (11,7), (12,7), (13,7), (14, 9), (15,11), (16,11); +-- +-- get all paths from "second level" nodes to leaf nodes +-- +WITH RECURSIVE t(id, path) AS ( + VALUES(1,ARRAY[]::integer[]) +UNION ALL + SELECT tree.id, t.path || tree.id + FROM tree JOIN t ON (tree.parent_id = t.id) +) +SELECT t1.*, t2.* FROM t AS t1 JOIN t AS t2 ON + (t1.path[1] = t2.path[1] AND + array_upper(t1.path,1) = 1 AND + array_upper(t2.path,1) > 1) + ORDER BY t1.id, t2.id; + id | path | id | path +----+------+----+------------- + 2 | {2} | 4 | {2,4} + 2 | {2} | 5 | {2,5} + 2 | {2} | 6 | {2,6} + 2 | {2} | 9 | {2,4,9} + 2 | {2} | 10 | {2,4,10} + 2 | {2} | 14 | {2,4,9,14} + 3 | {3} | 7 | {3,7} + 3 | {3} | 8 | {3,8} + 3 | {3} | 11 | {3,7,11} + 3 | {3} | 12 | {3,7,12} + 3 | {3} | 13 | {3,7,13} + 3 | {3} | 15 | {3,7,11,15} + 3 | {3} | 16 | {3,7,11,16} +(13 rows) + +-- just count 'em +WITH RECURSIVE t(id, path) AS ( + VALUES(1,ARRAY[]::integer[]) +UNION ALL + SELECT tree.id, t.path || tree.id + FROM tree JOIN t ON (tree.parent_id = t.id) +) +SELECT t1.id, count(t2.*) FROM t AS t1 JOIN t AS t2 ON + (t1.path[1] = t2.path[1] AND + array_upper(t1.path,1) = 1 AND + array_upper(t2.path,1) > 1) + GROUP BY t1.id + ORDER BY t1.id; + id | count +----+------- + 2 | 6 + 3 | 7 +(2 rows) + +-- this variant tickled a whole-row-variable bug in 8.4devel +WITH RECURSIVE t(id, path) AS ( + VALUES(1,ARRAY[]::integer[]) +UNION ALL + SELECT tree.id, t.path || tree.id + FROM tree JOIN t ON (tree.parent_id = t.id) +) +SELECT t1.id, t2.path, t2 FROM t AS t1 JOIN t AS t2 ON +(t1.id=t2.id); + id | path | t2 +----+-------------+-------------------- + 1 | {} | (1,{}) + 2 | {2} | (2,{2}) + 3 | {3} | (3,{3}) + 4 | {2,4} | (4,"{2,4}") + 5 | {2,5} | (5,"{2,5}") + 6 | {2,6} | (6,"{2,6}") + 7 | {3,7} | (7,"{3,7}") + 8 | {3,8} | (8,"{3,8}") + 9 | {2,4,9} | (9,"{2,4,9}") + 10 | {2,4,10} | (10,"{2,4,10}") + 11 | {3,7,11} | (11,"{3,7,11}") + 12 | {3,7,12} | (12,"{3,7,12}") + 13 | {3,7,13} | (13,"{3,7,13}") + 14 | {2,4,9,14} | (14,"{2,4,9,14}") + 15 | {3,7,11,15} | (15,"{3,7,11,15}") + 16 | {3,7,11,16} | (16,"{3,7,11,16}") +(16 rows) + +-- +-- test cycle detection +-- +create temp table graph( f int, t int, label text ); +insert into graph values + (1, 2, 'arc 1 -> 2'), + (1, 3, 'arc 1 -> 3'), + (2, 3, 'arc 2 -> 3'), + (1, 4, 'arc 1 -> 4'), + (4, 5, 'arc 4 -> 5'), + (5, 1, 'arc 5 -> 1'); +with recursive search_graph(f, t, label, path, cycle) as ( + select *, array[row(g.f, g.t)], false from graph g + union all + select g.*, path || row(g.f, g.t), row(g.f, g.t) = any(path) + from graph g, search_graph sg + where g.f = sg.t and not cycle +) +select * from search_graph; + f | t | label | path | cycle +---+---+------------+-------------------------------------------+------- + 1 | 2 | arc 1 -> 2 | {"(1,2)"} | f + 1 | 3 | arc 1 -> 3 | {"(1,3)"} | f + 2 | 3 | arc 2 -> 3 | {"(2,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(1,4)"} | f + 4 | 5 | arc 4 -> 5 | {"(4,5)"} | f + 5 | 1 | arc 5 -> 1 | {"(5,1)"} | f + 1 | 2 | arc 1 -> 2 | {"(5,1)","(1,2)"} | f + 1 | 3 | arc 1 -> 3 | {"(5,1)","(1,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(5,1)","(1,4)"} | f + 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"} | f + 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"} | f + 5 | 1 | arc 5 -> 1 | {"(4,5)","(5,1)"} | f + 1 | 2 | arc 1 -> 2 | {"(4,5)","(5,1)","(1,2)"} | f + 1 | 3 | arc 1 -> 3 | {"(4,5)","(5,1)","(1,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(4,5)","(5,1)","(1,4)"} | f + 2 | 3 | arc 2 -> 3 | {"(5,1)","(1,2)","(2,3)"} | f + 4 | 5 | arc 4 -> 5 | {"(5,1)","(1,4)","(4,5)"} | f + 5 | 1 | arc 5 -> 1 | {"(1,4)","(4,5)","(5,1)"} | f + 1 | 2 | arc 1 -> 2 | {"(1,4)","(4,5)","(5,1)","(1,2)"} | f + 1 | 3 | arc 1 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(1,4)","(4,5)","(5,1)","(1,4)"} | t + 2 | 3 | arc 2 -> 3 | {"(4,5)","(5,1)","(1,2)","(2,3)"} | f + 4 | 5 | arc 4 -> 5 | {"(4,5)","(5,1)","(1,4)","(4,5)"} | t + 5 | 1 | arc 5 -> 1 | {"(5,1)","(1,4)","(4,5)","(5,1)"} | t + 2 | 3 | arc 2 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} | f +(25 rows) + +-- ordering by the path column has same effect as SEARCH DEPTH FIRST +with recursive search_graph(f, t, label, path, cycle) as ( + select *, array[row(g.f, g.t)], false from graph g + union all + select g.*, path || row(g.f, g.t), row(g.f, g.t) = any(path) + from graph g, search_graph sg + where g.f = sg.t and not cycle +) +select * from search_graph order by path; + f | t | label | path | cycle +---+---+------------+-------------------------------------------+------- + 1 | 2 | arc 1 -> 2 | {"(1,2)"} | f + 2 | 3 | arc 2 -> 3 | {"(1,2)","(2,3)"} | f + 1 | 3 | arc 1 -> 3 | {"(1,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(1,4)"} | f + 4 | 5 | arc 4 -> 5 | {"(1,4)","(4,5)"} | f + 5 | 1 | arc 5 -> 1 | {"(1,4)","(4,5)","(5,1)"} | f + 1 | 2 | arc 1 -> 2 | {"(1,4)","(4,5)","(5,1)","(1,2)"} | f + 2 | 3 | arc 2 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,2)","(2,3)"} | f + 1 | 3 | arc 1 -> 3 | {"(1,4)","(4,5)","(5,1)","(1,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(1,4)","(4,5)","(5,1)","(1,4)"} | t + 2 | 3 | arc 2 -> 3 | {"(2,3)"} | f + 4 | 5 | arc 4 -> 5 | {"(4,5)"} | f + 5 | 1 | arc 5 -> 1 | {"(4,5)","(5,1)"} | f + 1 | 2 | arc 1 -> 2 | {"(4,5)","(5,1)","(1,2)"} | f + 2 | 3 | arc 2 -> 3 | {"(4,5)","(5,1)","(1,2)","(2,3)"} | f + 1 | 3 | arc 1 -> 3 | {"(4,5)","(5,1)","(1,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(4,5)","(5,1)","(1,4)"} | f + 4 | 5 | arc 4 -> 5 | {"(4,5)","(5,1)","(1,4)","(4,5)"} | t + 5 | 1 | arc 5 -> 1 | {"(5,1)"} | f + 1 | 2 | arc 1 -> 2 | {"(5,1)","(1,2)"} | f + 2 | 3 | arc 2 -> 3 | {"(5,1)","(1,2)","(2,3)"} | f + 1 | 3 | arc 1 -> 3 | {"(5,1)","(1,3)"} | f + 1 | 4 | arc 1 -> 4 | {"(5,1)","(1,4)"} | f + 4 | 5 | arc 4 -> 5 | {"(5,1)","(1,4)","(4,5)"} | f + 5 | 1 | arc 5 -> 1 | {"(5,1)","(1,4)","(4,5)","(5,1)"} | t +(25 rows) + +-- +-- test multiple WITH queries +-- +WITH RECURSIVE + y (id) AS (VALUES (1)), + x (id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5) +SELECT * FROM x; + id +---- + 1 + 2 + 3 + 4 + 5 +(5 rows) + +-- forward reference OK +WITH RECURSIVE + x(id) AS (SELECT * FROM y UNION ALL SELECT id+1 FROM x WHERE id < 5), + y(id) AS (values (1)) + SELECT * FROM x; + id +---- + 1 + 2 + 3 + 4 + 5 +(5 rows) + +WITH RECURSIVE + x(id) AS + (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), + y(id) AS + (VALUES (1) UNION ALL SELECT id+1 FROM y WHERE id < 10) + SELECT y.*, x.* FROM y LEFT JOIN x USING (id); + id | id +----+---- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | 4 + 5 | 5 + 6 | + 7 | + 8 | + 9 | + 10 | +(10 rows) + +WITH RECURSIVE + x(id) AS + (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 5), + y(id) AS + (VALUES (1) UNION ALL SELECT id+1 FROM x WHERE id < 10) + SELECT y.*, x.* FROM y LEFT JOIN x USING (id); + id | id +----+---- + 1 | 1 + 2 | 2 + 3 | 3 + 4 | 4 + 5 | 5 + 6 | +(6 rows) + +WITH RECURSIVE + x(id) AS + (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), + y(id) AS + (SELECT * FROM x UNION ALL SELECT * FROM x), + z(id) AS + (SELECT * FROM x UNION ALL SELECT id+1 FROM z WHERE id < 10) + SELECT * FROM z; + id +---- + 1 + 2 + 3 + 2 + 3 + 4 + 3 + 4 + 5 + 4 + 5 + 6 + 5 + 6 + 7 + 6 + 7 + 8 + 7 + 8 + 9 + 8 + 9 + 10 + 9 + 10 + 10 +(27 rows) + +WITH RECURSIVE + x(id) AS + (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 3 ), + y(id) AS + (SELECT * FROM x UNION ALL SELECT * FROM x), + z(id) AS + (SELECT * FROM y UNION ALL SELECT id+1 FROM z WHERE id < 10) + SELECT * FROM z; + id +---- + 1 + 2 + 3 + 1 + 2 + 3 + 2 + 3 + 4 + 2 + 3 + 4 + 3 + 4 + 5 + 3 + 4 + 5 + 4 + 5 + 6 + 4 + 5 + 6 + 5 + 6 + 7 + 5 + 6 + 7 + 6 + 7 + 8 + 6 + 7 + 8 + 7 + 8 + 9 + 7 + 8 + 9 + 8 + 9 + 10 + 8 + 9 + 10 + 9 + 10 + 9 + 10 + 10 + 10 +(54 rows) + +-- +-- Test WITH attached to a data-modifying statement +-- +CREATE TEMPORARY TABLE y (a INTEGER) DISTRIBUTED RANDOMLY; +INSERT INTO y SELECT generate_series(1, 10); +WITH t AS ( + SELECT a FROM y +) +INSERT INTO y +SELECT a+20 FROM t RETURNING *; + a +---- + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 +(10 rows) + +SELECT * FROM y; + a +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 21 + 22 + 23 + 24 + 25 + 26 + 27 + 28 + 29 + 30 +(20 rows) + +WITH t AS ( + SELECT a FROM y +) +UPDATE y SET a = y.a-10 FROM t WHERE y.a > 20 AND t.a = y.a RETURNING y.a; + a +---- + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 +(10 rows) + +SELECT * FROM y; + a +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 +(20 rows) + +WITH RECURSIVE t(a) AS ( + SELECT 11 + UNION ALL + SELECT a+1 FROM t WHERE a < 50 +) +DELETE FROM y USING t WHERE t.a = y.a RETURNING y.a; + a +---- + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 +(10 rows) + +SELECT * FROM y; + a +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 +(10 rows) + +DROP TABLE y; +-- +-- error cases +-- +-- INTERSECT +WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x) + SELECT * FROM x; +ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term +LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT SELECT n+1 FROM x... + ^ +WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FROM x) + SELECT * FROM x; +ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term +LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 INTERSECT ALL SELECT n+1 FR... + ^ +-- EXCEPT +WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x) + SELECT * FROM x; +ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term +LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT SELECT n+1 FROM x) + ^ +WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM x) + SELECT * FROM x; +ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term +LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 EXCEPT ALL SELECT n+1 FROM ... + ^ +-- GPDB Specific Error Cases +-- Set operations within the recursive term with a self-reference. +-- Currently set operations in the recursive term involving the cte itself must +-- be prevented. The reason for this is that such a query may lead to a plan +-- where there is a motion between the RecursiveUnion node and the +-- WorkTableScan node. +CREATE TEMPORARY TABLE z(x int primary key); +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM (SELECT * FROM x UNION SELECT * FROM z)foo) + SELECT * FROM x; +ERROR: recursive reference to query "x" must not appear within a subquery +LINE 1: ...SELECT 1 UNION ALL SELECT n+1 FROM (SELECT * FROM x UNION SE... + ^ +-- Set operation in recursive term that does not have a self-reference +-- This is supported +CREATE TEMPORARY TABLE u(x int primary key); +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM (SELECT * from z UNION SELECT * FROM u)foo, x where foo.x = x.n) + SELECT * FROM x; + n +--- + 1 +(1 row) + +-- no non-recursive term +WITH RECURSIVE x(n) AS (SELECT n FROM x) + SELECT * FROM x; +ERROR: recursive query "x" does not have the form non-recursive-term UNION [ALL] recursive-term +LINE 1: WITH RECURSIVE x(n) AS (SELECT n FROM x) + ^ +-- recursive term in the left hand side (strictly speaking, should allow this) +WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1) + SELECT * FROM x; +ERROR: recursive reference to query "x" must not appear within its non-recursive term +LINE 1: WITH RECURSIVE x(n) AS (SELECT n FROM x UNION ALL SELECT 1) + ^ +-- recursive term with a self-reference within a subquery is not allowed +WITH RECURSIVE cte(level, id) as ( + SELECT 1, 2 + UNION ALL + SELECT level+1, c FROM (SELECT * FROM cte OFFSET 0) foo, bar) +SELECT * FROM cte LIMIT 10; +ERROR: recursive reference to query "cte" must not appear within a subquery +LINE 4: SELECT level+1, c FROM (SELECT * FROM cte OFFSET 0) foo, ba... + ^ +-- recursive term with a distinct operation is not allowed +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT distinct(n+1) FROM x) + SELECT * FROM x; +ERROR: DISTINCT in a recursive query is not implemented +-- recursive term with a group by operation is not allowed +CREATE TEMPORARY TABLE bar(c int); +NOTICE: Table doesn't have 'DISTRIBUTED BY' clause -- Using column named 'c' as the Greenplum Database data distribution key for this table. +HINT: The 'DISTRIBUTED BY' clause determines the distribution of data. Make sure column(s) chosen are the optimal data distribution key to minimize skew. +WITH RECURSIVE x(n) AS ( + SELECT 1,2 + UNION ALL + SELECT level+1, c FROM x, bar GROUP BY 1,2) + SELECT * FROM x LIMIT 10; +ERROR: GROUP BY in a recursive query is not implemented +LINE 4: SELECT level+1, c FROM x, bar GROUP BY 1,2) + ^ +WITH RECURSIVE x(n) AS ( + SELECT 1,2 + UNION ALL + SELECT level+1, row_number() over() FROM x, bar) + SELECT * FROM x LIMIT 10; +ERROR: window functions in the target list of a recursive query is not supported in Greenplum +LINE 4: SELECT level+1, row_number() over() FROM x, bar) + ^ +CREATE TEMPORARY TABLE y (a INTEGER) DISTRIBUTED RANDOMLY; +INSERT INTO y SELECT generate_series(1, 10); +-- LEFT JOIN +WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 + UNION ALL + SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10) +SELECT * FROM x; +ERROR: recursive reference to query "x" must not appear within an outer join +LINE 3: SELECT x.n+1 FROM y LEFT JOIN x ON x.n = y.a WHERE n < 10) + ^ +-- RIGHT JOIN +WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 + UNION ALL + SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10) +SELECT * FROM x; +ERROR: recursive reference to query "x" must not appear within an outer join +LINE 3: SELECT x.n+1 FROM x RIGHT JOIN y ON x.n = y.a WHERE n < 10) + ^ +-- FULL JOIN +WITH RECURSIVE x(n) AS (SELECT a FROM y WHERE a = 1 + UNION ALL + SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) +SELECT * FROM x; +ERROR: recursive reference to query "x" must not appear within an outer join +LINE 3: SELECT x.n+1 FROM x FULL JOIN y ON x.n = y.a WHERE n < 10) + ^ +-- subquery +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x + WHERE n IN (SELECT * FROM x)) + SELECT * FROM x; +ERROR: recursive reference to query "x" must not appear within a subquery +LINE 2: WHERE n IN (SELECT * FROM x)) + ^ +-- aggregate functions +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) FROM x) + SELECT * FROM x; +ERROR: aggregate functions are not allowed in a recursive query's recursive term +LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT count(*) F... + ^ +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FROM x) + SELECT * FROM x; +ERROR: aggregate functions are not allowed in a recursive query's recursive term +LINE 1: WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT sum(n) FRO... + ^ +-- ORDER BY +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1) + SELECT * FROM x; +ERROR: ORDER BY in a recursive query is not implemented +LINE 1: ...VE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x ORDER BY 1) + ^ +-- LIMIT/OFFSET +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1) + SELECT * FROM x; +ERROR: OFFSET in a recursive query is not implemented +LINE 1: ... AS (SELECT 1 UNION ALL SELECT n+1 FROM x LIMIT 10 OFFSET 1) + ^ +-- FOR UPDATE +WITH RECURSIVE x(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM x FOR UPDATE) + SELECT * FROM x; +ERROR: FOR UPDATE/SHARE in a recursive query is not implemented +-- target list has a recursive query name +WITH RECURSIVE x(id) AS (values (1) + UNION ALL + SELECT (SELECT * FROM x) FROM x WHERE id < 5 +) SELECT * FROM x; +ERROR: recursive reference to query "x" must not appear within a subquery +LINE 3: SELECT (SELECT * FROM x) FROM x WHERE id < 5 + ^ +-- mutual recursive query (not implemented) +WITH RECURSIVE + x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id < 5), + y (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM x WHERE id < 5) +SELECT * FROM x; +ERROR: mutual recursion between WITH items is not implemented +LINE 2: x (id) AS (SELECT 1 UNION ALL SELECT id+1 FROM y WHERE id ... + ^ +-- non-linear recursion is not allowed +WITH RECURSIVE foo(i) AS + (values (1) + UNION ALL + (SELECT i+1 FROM foo WHERE i < 10 + UNION ALL + SELECT i+1 FROM foo WHERE i < 5) +) SELECT * FROM foo; +ERROR: recursive reference to query "foo" must not appear more than once +LINE 6: SELECT i+1 FROM foo WHERE i < 5) + ^ +WITH RECURSIVE foo(i) AS + (values (1) + UNION ALL + SELECT * FROM + (SELECT i+1 FROM foo WHERE i < 10 + UNION ALL + SELECT i+1 FROM foo WHERE i < 5) AS t +) SELECT * FROM foo; +ERROR: recursive reference to query "foo" must not appear within a subquery +LINE 5: (SELECT i+1 FROM foo WHERE i < 10 + ^ +WITH RECURSIVE foo(i) AS + (values (1) + UNION ALL + (SELECT i+1 FROM foo WHERE i < 10 + EXCEPT + SELECT i+1 FROM foo WHERE i < 5) +) SELECT * FROM foo; +ERROR: recursive reference to query "foo" must not appear within EXCEPT +LINE 6: SELECT i+1 FROM foo WHERE i < 5) + ^ +WITH RECURSIVE foo(i) AS + (values (1) + UNION ALL + (SELECT i+1 FROM foo WHERE i < 10 + INTERSECT + SELECT i+1 FROM foo WHERE i < 5) +) SELECT * FROM foo; +ERROR: recursive reference to query "foo" must not appear more than once +LINE 6: SELECT i+1 FROM foo WHERE i < 5) + ^ +-- Wrong type induced from non-recursive term +WITH RECURSIVE foo(i) AS + (SELECT i FROM (VALUES(1),(2)) t(i) + UNION ALL + SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) +SELECT * FROM foo; +ERROR: recursive query "foo" column 1 has type integer in non-recursive term but type numeric overall +LINE 2: (SELECT i FROM (VALUES(1),(2)) t(i) + ^ +HINT: Cast the output of the non-recursive term to the correct type. +-- rejects different typmod, too (should we allow this?) +WITH RECURSIVE foo(i) AS + (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) + UNION ALL + SELECT (i+1)::numeric(10,0) FROM foo WHERE i < 10) +SELECT * FROM foo; +ERROR: recursive query "foo" column 1 has type numeric(3,0) in non-recursive term but type numeric overall +LINE 2: (SELECT i::numeric(3,0) FROM (VALUES(1),(2)) t(i) + ^ +HINT: Cast the output of the non-recursive term to the correct type. +-- disallow OLD/NEW reference in CTE +CREATE TEMPORARY TABLE x (n integer); +CREATE RULE r2 AS ON UPDATE TO x DO INSTEAD + WITH t AS (SELECT OLD.*) UPDATE y SET a = t.n FROM t; +ERROR: cannot refer to OLD within WITH query +-- +-- test for bug #4902 +-- +with cte(foo) as ( values(42) ) values((select foo from cte)); + column1 +--------- + 42 +(1 row) + +with cte(foo) as ( select 42 ) select * from ((select foo from cte)) q; + foo +----- + 42 +(1 row) + +-- test CTE referencing an outer-level variable (to see that changed-parameter +-- signaling still works properly after fixing this bug) +select ( with cte(foo) as ( values(f1) ) + select (select foo from cte) ) +from int4_tbl; + foo +------------- + 0 + 123456 + -123456 + 2147483647 + -2147483647 +(5 rows) + +select ( with cte(foo) as ( values(f1) ) + values((select foo from cte)) ) +from int4_tbl; + column1 +------------- + 0 + 123456 + -123456 + 2147483647 + -2147483647 +(5 rows) + +-- +-- test Nested CTE +-- +WITH outermost(x) AS ( + SELECT 1 + UNION (WITH innermost as (SELECT 2) + SELECT * FROM innermost + UNION SELECT 3) +) +SELECT * FROM outermost; + x +--- + 1 + 2 + 3 +(3 rows) + +-- +-- test for nested-recursive-WITH bug +-- +WITH RECURSIVE t(j) AS ( + WITH RECURSIVE s(i) AS ( + VALUES (1) + UNION ALL + SELECT i+1 FROM s WHERE i < 10 + ) + SELECT i FROM s + UNION ALL + SELECT j+1 FROM t WHERE j < 10 +) +SELECT * FROM t; + j +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 5 + 6 + 7 + 8 + 9 + 10 + 6 + 7 + 8 + 9 + 10 + 7 + 8 + 9 + 10 + 8 + 9 + 10 + 9 + 10 + 10 +(55 rows) + +-- +-- test WITH attached to intermediate-level set operation +-- +WITH outermost(x) AS ( + SELECT 1 + UNION (WITH innermost as (SELECT 2) + SELECT * FROM innermost + UNION SELECT 3) +) +SELECT * FROM outermost; + x +--- + 1 + 2 + 3 +(3 rows) + +WITH outermost(x) AS ( + SELECT 1 + UNION (WITH innermost as (SELECT 2) + SELECT * FROM outermost -- fail + UNION SELECT * FROM innermost) +) +SELECT * FROM outermost; +ERROR: relation "outermost" does not exist +LINE 4: SELECT * FROM outermost + ^ +DETAIL: There is a WITH item named "outermost", but it cannot be referenced from this part of the query. +HINT: Use WITH RECURSIVE, or re-order the WITH items to remove forward references. +WITH RECURSIVE outermost(x) AS ( + SELECT 1 + UNION (WITH innermost as (SELECT 2) + SELECT * FROM outermost + UNION SELECT * FROM innermost) +) +SELECT * FROM outermost; + x +--- + 1 + 2 +(2 rows) + +WITH RECURSIVE outermost(x) AS ( + WITH innermost as (SELECT 2 FROM outermost) -- fail + SELECT * FROM innermost + UNION SELECT * from outermost +) +SELECT * FROM outermost; +ERROR: recursive reference to query "outermost" must not appear within a subquery +LINE 2: WITH innermost as (SELECT 2 FROM outermost) + ^ +-- +-- This test will fail with the old implementation of PARAM_EXEC parameter +-- assignment, because the "q1" Var passed down to A's targetlist subselect +-- looks exactly like the "A.id" Var passed down to C's subselect, causing +-- the old code to give them the same runtime PARAM_EXEC slot. But the +-- lifespans of the two parameters overlap, thanks to B also reading A. +-- +with +A as ( select q2 as id, (select q1) as x from int8_tbl ), +B as ( select id, row_number() over (partition by id) as r from A ), +C as ( select A.id, array(select B.id from B where B.id = A.id) from A ) +select * from C; + id | array +-------------------+------------------------------------- + 456 | {456} + 4567890123456789 | {4567890123456789,4567890123456789} + 123 | {123} + 4567890123456789 | {4567890123456789,4567890123456789} + -4567890123456789 | {-4567890123456789} +(5 rows) + +-- +-- Test CTEs read in non-initialization orders +-- gpdb +-- Remove window funtions from Recursive CTE's test case. +-- Currently Recursive CTE's do not support the Window Functions, +-- So we remove it from the test cases. +-- +WITH RECURSIVE + tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), + iter (id_key, row_type, link) AS ( + SELECT 0, 'base', 17 + UNION ALL ( + WITH remaining(id_key, row_type, link, min) AS ( + SELECT tab.id_key, 'true'::text, iter.link, tab.id_key + FROM tab INNER JOIN iter USING (link) + WHERE tab.id_key > iter.id_key + ), + first_remaining AS ( + SELECT id_key, row_type, link + FROM remaining + WHERE id_key=min + ), + effect AS ( + SELECT tab.id_key, 'new'::text, tab.link + FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key + WHERE e.row_type = 'false' + ) + SELECT * FROM first_remaining + UNION ALL SELECT * FROM effect + ) + ) +SELECT * FROM iter; + id_key | row_type | link +--------+----------+------ + 0 | base | 17 + 5 | true | 17 + 6 | true | 17 + 4 | true | 17 + 3 | true | 17 + 2 | true | 17 + 1 | true | 17 + 6 | true | 17 + 5 | true | 17 + 6 | true | 17 + 5 | true | 17 + 6 | true | 17 + 4 | true | 17 + 5 | true | 17 + 6 | true | 17 + 4 | true | 17 + 3 | true | 17 + 5 | true | 17 + 6 | true | 17 + 4 | true | 17 + 3 | true | 17 + 2 | true | 17 + 6 | true | 17 + 6 | true | 17 + 5 | true | 17 + 6 | true | 17 + 6 | true | 17 + 5 | true | 17 + 6 | true | 17 + 5 | true | 17 + 6 | true | 17 + 4 | true | 17 + 6 | true | 17 + 5 | true | 17 + 6 | true | 17 + 5 | true | 17 + 6 | true | 17 + 4 | true | 17 + 5 | true | 17 + 6 | true | 17 + 4 | true | 17 + 3 | true | 17 + 6 | true | 17 + 6 | true | 17 + 6 | true | 17 + 5 | true | 17 + 6 | true | 17 + 6 | true | 17 + 6 | true | 17 + 5 | true | 17 + 6 | true | 17 + 6 | true | 17 + 5 | true | 17 + 6 | true | 17 + 5 | true | 17 + 6 | true | 17 + 4 | true | 17 + 6 | true | 17 + 6 | true | 17 + 6 | true | 17 + 6 | true | 17 + 5 | true | 17 + 6 | true | 17 + 6 | true | 17 +(64 rows) + +WITH RECURSIVE + tab(id_key,link) AS (VALUES (1,17), (2,17), (3,17), (4,17), (6,17), (5,17)), + iter (id_key, row_type, link) AS ( + SELECT 0, 'base', 17 + UNION ( + WITH remaining(id_key, row_type, link, min) AS ( + SELECT tab.id_key, 'true'::text, iter.link, tab.id_key + FROM tab INNER JOIN iter USING (link) + WHERE tab.id_key > iter.id_key + ), + first_remaining AS ( + SELECT id_key, row_type, link + FROM remaining + WHERE id_key=min + ), + effect AS ( + SELECT tab.id_key, 'new'::text, tab.link + FROM first_remaining e INNER JOIN tab ON e.id_key=tab.id_key + WHERE e.row_type = 'false' + ) + SELECT * FROM first_remaining + UNION ALL SELECT * FROM effect + ) + ) +SELECT * FROM iter; + id_key | row_type | link +--------+----------+------ + 0 | base | 17 + 1 | true | 17 + 2 | true | 17 + 3 | true | 17 + 4 | true | 17 + 5 | true | 17 + 6 | true | 17 +(7 rows) + +-- +-- Data-modifying statements in WITH +-- +-- INSERT ... RETURNING +WITH t AS ( + INSERT INTO y + VALUES + (11), + (12), + (13), + (14), + (15), + (16), + (17), + (18), + (19), + (20) + RETURNING * +) +SELECT * FROM t; + a +---- + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 +(10 rows) + +SELECT * FROM y; + a +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 +(20 rows) + +-- UPDATE ... RETURNING +WITH t AS ( + UPDATE y + SET a=a+1 + RETURNING * +) +SELECT * FROM t; + a +---- + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 +(20 rows) + +SELECT * FROM y; + a +---- + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 +(20 rows) + +-- DELETE ... RETURNING +WITH t AS ( + DELETE FROM y + WHERE a <= 10 + RETURNING * +) +SELECT * FROM t; + a +---- + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 +(9 rows) + +SELECT * FROM y; + a +---- + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 +(11 rows) + +-- forward reference +WITH RECURSIVE t AS ( + INSERT INTO y + SELECT a+5 FROM t2 WHERE a > 5 + RETURNING * +), t2 AS ( + UPDATE y SET a=a-11 RETURNING * +) +SELECT * FROM t +UNION ALL +SELECT * FROM t2; +ERROR: only one modifying WITH clause allowed per query +DETAIL: Greenplum Database currently only support CTEs with one writable clause. +HINT: Rewrite the query to only include one writable CTE clause. +SELECT * FROM y; + a +---- + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 +(11 rows) + +-- unconditional DO INSTEAD rule +CREATE RULE y_rule AS ON DELETE TO y DO INSTEAD + INSERT INTO y VALUES(42) RETURNING *; +WITH t AS ( + DELETE FROM y RETURNING * +) +SELECT * FROM t; + a +---- + 42 +(1 row) + +SELECT * FROM y; + a +---- + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 42 +(12 rows) + +DROP RULE y_rule ON y; +-- check merging of outer CTE with CTE in a rule action +CREATE TEMP TABLE bug6051 AS + select i from generate_series(1,3) as t(i); +SELECT * FROM bug6051; + i +--- + 1 + 2 + 3 +(3 rows) + +WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) +INSERT INTO bug6051 SELECT * FROM t1; +ERROR: writable CTE queries cannot be themselves writable +DETAIL: Greenplum Database currently only support CTEs with one writable clause, called in a non-writable context. +HINT: Rewrite the query to only include one writable clause. +SELECT * FROM bug6051; + i +--- + 1 + 2 + 3 +(3 rows) + +CREATE TEMP TABLE bug6051_2 (i int); +CREATE RULE bug6051_ins AS ON INSERT TO bug6051 DO INSTEAD + INSERT INTO bug6051_2 + SELECT NEW.i; +WITH t1 AS ( DELETE FROM bug6051 RETURNING * ) +INSERT INTO bug6051 SELECT * FROM t1; +ERROR: writable CTE queries cannot be themselves writable +DETAIL: Greenplum Database currently only support CTEs with one writable clause, called in a non-writable context. +HINT: Rewrite the query to only include one writable clause. +SELECT * FROM bug6051; + i +--- + 1 + 2 + 3 +(3 rows) + +SELECT * FROM bug6051_2; + i +--- +(0 rows) + +-- a truly recursive CTE in the same list +WITH RECURSIVE t(a) AS ( + SELECT 0 + UNION ALL + SELECT a+1 FROM t WHERE a+1 < 5 +), t2 as ( + INSERT INTO y + SELECT * FROM t RETURNING * +) +SELECT * FROM t2 JOIN y USING (a) ORDER BY a; + a +--- +(0 rows) + +SELECT * FROM y; + a +---- + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 42 + 0 + 1 + 2 + 3 + 4 +(17 rows) + +-- data-modifying WITH in a modifying statement +WITH t AS ( + DELETE FROM y + WHERE a <= 10 + RETURNING * +) +INSERT INTO y SELECT -a FROM t RETURNING *; +ERROR: writable CTE queries cannot be themselves writable +DETAIL: Greenplum Database currently only support CTEs with one writable clause, called in a non-writable context. +HINT: Rewrite the query to only include one writable clause. +SELECT * FROM y; + a +---- + 0 + 1 + 2 + 3 + 4 + 11 + 12 + 13 + 14 + 15 + 16 + 17 + 18 + 19 + 20 + 21 + 42 +(17 rows) + +-- check that WITH query is run to completion even if outer query isn't +WITH t AS ( + UPDATE y SET a = a * 100 RETURNING * +) +SELECT a BETWEEN 0 AND 4200 FROM t LIMIT 10; + ?column? +---------- + t + t + t + t + t + t + t + t + t + t +(10 rows) + +SELECT * FROM y; + a +------ + 0 + 100 + 200 + 300 + 400 + 1100 + 1200 + 1300 + 1400 + 1500 + 1600 + 1700 + 1800 + 1900 + 2000 + 2100 + 4200 +(17 rows) + +-- check that run to completion happens in proper ordering +TRUNCATE TABLE y; +INSERT INTO y SELECT generate_series(1, 3); +CREATE TEMPORARY TABLE yy (a INTEGER); +WITH RECURSIVE t1 AS ( + INSERT INTO y SELECT * FROM y RETURNING * +), t2 AS ( + INSERT INTO yy SELECT * FROM t1 RETURNING * +) +SELECT 1; +ERROR: only one modifying WITH clause allowed per query +DETAIL: Greenplum Database currently only support CTEs with one writable clause. +HINT: Rewrite the query to only include one writable CTE clause. +SELECT * FROM y; + a +--- + 1 + 2 + 3 +(3 rows) + +SELECT * FROM yy; + a +--- +(0 rows) + +WITH RECURSIVE t1 AS ( + INSERT INTO yy SELECT * FROM t2 RETURNING * +), t2 AS ( + INSERT INTO y SELECT * FROM y RETURNING * +) +SELECT 1; +ERROR: only one modifying WITH clause allowed per query +DETAIL: Greenplum Database currently only support CTEs with one writable clause. +HINT: Rewrite the query to only include one writable CTE clause. +SELECT * FROM y; + a +--- + 1 + 2 + 3 +(3 rows) + +SELECT * FROM yy; + a +--- +(0 rows) + +-- start_ignore +-- These tests actually seem to work, but they have unstable return order +-- in an MPP environment so they are ignored until atmsort can handle this +-- triggers +TRUNCATE TABLE y; +INSERT INTO y SELECT generate_series(1, 10); +CREATE FUNCTION y_trigger() RETURNS trigger AS $$ +begin + raise notice 'y_trigger: a = %', new.a; + return new; +end; +$$ LANGUAGE plpgsql; +CREATE TRIGGER y_trig BEFORE INSERT ON y FOR EACH ROW + EXECUTE PROCEDURE y_trigger(); +WITH t AS ( + INSERT INTO y + VALUES + (21), + (22), + (23) + RETURNING * +) +SELECT * FROM t; +NOTICE: y_trigger: a = 21 +NOTICE: y_trigger: a = 22 +NOTICE: y_trigger: a = 23 + a +---- + 21 + 22 + 23 +(3 rows) + +SELECT * FROM y; + a +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 21 + 22 + 23 +(13 rows) + +DROP TRIGGER y_trig ON y; +CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH ROW + EXECUTE PROCEDURE y_trigger(); +WITH t AS ( + INSERT INTO y + VALUES + (31), + (32), + (33) + RETURNING * +) +SELECT * FROM t LIMIT 1; +NOTICE: y_trigger: a = 31 +NOTICE: y_trigger: a = 32 +NOTICE: y_trigger: a = 33 + a +---- + 31 +(1 row) + +SELECT * FROM y; + a +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 21 + 22 + 23 + 31 + 32 + 33 +(16 rows) + +DROP TRIGGER y_trig ON y; +CREATE OR REPLACE FUNCTION y_trigger() RETURNS trigger AS $$ +begin + raise notice 'y_trigger'; + return null; +end; +$$ LANGUAGE plpgsql; +CREATE TRIGGER y_trig AFTER INSERT ON y FOR EACH STATEMENT + EXECUTE PROCEDURE y_trigger(); +WITH t AS ( + INSERT INTO y + VALUES + (41), + (42), + (43) + RETURNING * +) +SELECT * FROM t; +NOTICE: y_trigger + a +---- + 41 + 42 + 43 +(3 rows) + +SELECT * FROM y; + a +---- + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 21 + 22 + 23 + 31 + 32 + 33 + 41 + 42 + 43 +(19 rows) + +DROP TRIGGER y_trig ON y; +DROP FUNCTION y_trigger(); +-- end_ignore +-- WITH attached to inherited UPDATE or DELETE +CREATE TEMP TABLE parent ( id int, val text ); +CREATE TEMP TABLE child1 ( ) INHERITS ( parent ); +CREATE TEMP TABLE child2 ( ) INHERITS ( parent ); +INSERT INTO parent VALUES ( 1, 'p1' ); +INSERT INTO child1 VALUES ( 11, 'c11' ),( 12, 'c12' ); +INSERT INTO child2 VALUES ( 23, 'c21' ),( 24, 'c22' ); +-- start_ignore +-- This query fails due to the 2 stage agg having issues with inherited tables: +-- ERROR: incompatible loci in target inheritance set (planner.c:1426) +WITH rcte AS ( SELECT sum(id) AS totalid FROM parent ) +UPDATE parent SET id = id + totalid FROM rcte; +SELECT * FROM parent; + id | val +----+----- + 72 | p1 + 82 | c11 + 83 | c12 + 94 | c21 + 95 | c22 +(5 rows) + +-- end_ignore +WITH wcte AS ( INSERT INTO child1 VALUES ( 42, 'new' ) RETURNING id AS newid ) +UPDATE parent SET id = id + newid FROM wcte; +ERROR: writable CTE queries cannot be themselves writable +DETAIL: Greenplum Database currently only support CTEs with one writable clause, called in a non-writable context. +HINT: Rewrite the query to only include one writable clause. +SELECT * FROM parent; + id | val +----+----- + 1 | p1 + 11 | c11 + 12 | c12 + 23 | c21 + 24 | c22 +(5 rows) + +WITH rcte AS ( SELECT max(id) AS maxid FROM parent ) +DELETE FROM parent USING rcte WHERE id = maxid; +SELECT * FROM parent; + id | val +----+----- + 1 | p1 + 11 | c11 + 12 | c12 + 23 | c21 +(4 rows) + +WITH wcte AS ( INSERT INTO child2 VALUES ( 42, 'new2' ) RETURNING id AS newid ) +DELETE FROM parent USING wcte WHERE id = newid; +ERROR: writable CTE queries cannot be themselves writable +DETAIL: Greenplum Database currently only support CTEs with one writable clause, called in a non-writable context. +HINT: Rewrite the query to only include one writable clause. +SELECT * FROM parent; + id | val +----+----- + 1 | p1 + 11 | c11 + 12 | c12 + 23 | c21 +(4 rows) + +-- check EXPLAIN VERBOSE for a wCTE with RETURNING +EXPLAIN (VERBOSE, COSTS OFF) +WITH wcte AS ( INSERT INTO int8_tbl VALUES ( 42, 47 ) RETURNING q2 ) +DELETE FROM a USING wcte WHERE aa = q2; +ERROR: writable CTE queries cannot be themselves writable +DETAIL: Greenplum Database currently only support CTEs with one writable clause, called in a non-writable context. +HINT: Rewrite the query to only include one writable clause. +-- error cases +-- data-modifying WITH tries to use its own output +WITH RECURSIVE t AS ( + INSERT INTO y + SELECT * FROM t +) +VALUES(FALSE); +ERROR: recursive query "t" must not contain data-modifying statements +LINE 1: WITH RECURSIVE t AS ( + ^ +-- no RETURNING in a referenced data-modifying WITH +WITH t AS ( + INSERT INTO y VALUES(0) +) +SELECT * FROM t; +ERROR: WITH query "t" does not have a RETURNING clause +LINE 4: SELECT * FROM t; + ^ +-- data-modifying WITH allowed only at the top level +SELECT * FROM ( + WITH t AS (UPDATE y SET a=a+1 RETURNING *) + SELECT * FROM t +) ss; +ERROR: WITH clause containing a data-modifying statement must be at the top level +LINE 2: WITH t AS (UPDATE y SET a=a+1 RETURNING *) + ^ +-- most variants of rules aren't allowed +CREATE RULE y_rule AS ON INSERT TO y WHERE a=0 DO INSTEAD DELETE FROM y; +WITH t AS ( + INSERT INTO y VALUES(0) +) +VALUES(FALSE); +ERROR: conditional DO INSTEAD rules are not supported for data-modifying statements in WITH +DROP RULE y_rule ON y; +CREATE TABLE rank_tbl (id int, rank int) DISTRIBUTED BY (id); +insert into rank_tbl select i, i % 11 from generate_series(1, 100)i; +-- this used to raise ERROR: INSERT/UPDATE/DELETE must be executed by a writer segworker group +-- case we assign write gang to read slice +WITH updated AS ( + update rank_tbl set rank = 6 where id = 5 returning rank +) +select count(*) from rank_tbl where rank in (select rank from updated); + count +------- + 9 +(1 row) + +-- Test that the planner can build a plan with non-select CTE sharing. +--start_ignore +DROP TABLE IF EXISTS t1; +NOTICE: table "t1" does not exist, skipping +--end_ignore +CREATE TABLE t1 (c1 int, c2 int) DISTRIBUTED RANDOMLY; +EXPLAIN (VERBOSE, COSTS OFF) +WITH cte1 AS ( + INSERT INTO t1 VALUES ( 1, 2 ) RETURNING * +) +SELECT * FROM cte1 a JOIN cte1 b USING (c1); + QUERY PLAN +------------------------------------------------------------------------------------------------ + Gather Motion 3:1 (slice3; segments: 3) + Output: share0_ref3.c1, share0_ref3.c2, share0_ref2.c2 + -> Sequence + Output: share0_ref3.c1, share0_ref3.c2, share0_ref2.c2 + -> Shared Scan (share slice:id 3:0) + Output: share0_ref1.c1, share0_ref1.c2 + -> Materialize + Output: c1, c2 + -> Insert + Output: c1, c2 + -> Redistribute Motion 3:3 (slice2; segments: 3) + Output: "outer".c1, "outer".c2, "outer".ColRef_0036 + -> Result + Output: 1, 2, 1 + -> Result + One-Time Filter: (gp_execution_segment() = 0) + -> Result + -> Result + Output: true + -> Hash Join + Output: share0_ref3.c1, share0_ref3.c2, share0_ref2.c2 + Hash Cond: (share0_ref3.c1 = share0_ref2.c1) + -> Shared Scan (share slice:id 3:0) + Output: share0_ref3.c1, share0_ref3.c2 + -> Hash + Output: share0_ref2.c1, share0_ref2.c2 + -> Broadcast Motion 3:3 (slice1; segments: 3) + Output: share0_ref2.c1, share0_ref2.c2 + -> Shared Scan (share slice:id 1:0) + Output: share0_ref2.c1, share0_ref2.c2 + Optimizer: Pivotal Optimizer (GPORCA) +(31 rows) + +WITH cte1 AS ( + INSERT INTO t1 VALUES ( 1, 2 ) RETURNING * +) +SELECT * FROM cte1 a JOIN cte1 b USING (c1); + c1 | c2 | c2 +----+----+---- + 1 | 2 | 2 +(1 row) + +DROP TABLE t1; +-- Ensure that prefetch is not disabled for HashJoin in case of join at single segment. +-- Test Shared Scan producer is executed under inner part of join first and the +-- deadlock between Shared Scans does not occur +SET optimizer = off; +--start_ignore +DROP TABLE IF EXISTS d; +--end_ignore +CREATE TABLE d (c1 int, c2 int) DISTRIBUTED BY (c1); +INSERT INTO d VALUES ( 2, 0 ),( 2, 0 ); +WITH cte AS ( + SELECT count(*) c1 FROM d +) SELECT * FROM cte a JOIN (SELECT * FROM d JOIN cte USING (c1) LIMIT 1) b USING (c1); + c1 | c2 +----+---- + 2 | 0 +(1 row) + +-- Test cross slice Shared Scan with consumer in slice 0. +-- The consumer should be in slice 0 +EXPLAIN (COSTS OFF) WITH cte AS ( + SELECT c1 FROM d LIMIT 2 +) +SELECT * FROM cte a JOIN (SELECT * FROM d JOIN cte USING (c1) LIMIT 1) b USING (c1); + QUERY PLAN +------------------------------------------------------------------------------------------------------------------ + Hash Join + Hash Cond: (share0_ref2.c1 = b.c1) + -> Shared Scan (share slice:id 0:0) + -> Hash + -> Subquery Scan on b + -> Limit + -> Gather Motion 3:1 (slice3; segments: 3) + -> Limit + -> Hash Join + Hash Cond: (d.c1 = share0_ref1.c1) + -> Seq Scan on d + -> Hash + -> Redistribute Motion 1:3 (slice2) + Hash Key: share0_ref1.c1 + -> Shared Scan (share slice:id 2:0) + -> Materialize + -> Limit + -> Gather Motion 3:1 (slice1; segments: 3) + -> Limit + -> Seq Scan on d d_1 + Optimizer: Postgres query optimizer +(21 rows) + +-- Deadlock shouldn't happen +WITH cte AS ( + SELECT c1 FROM d LIMIT 2 +) +SELECT * FROM cte a JOIN (SELECT * FROM d JOIN cte USING (c1) LIMIT 1) b USING (c1); + c1 | c2 +----+---- + 2 | 0 + 2 | 0 +(2 rows) + +RESET optimizer; +DROP TABLE d; +-- Test if sharing is disabled for a SegmentGeneral CTE to avoid deadlock if CTE is +-- executed with 1-gang and joined with n-gang +SET optimizer = off; +--start_ignore +DROP TABLE IF EXISTS d; +NOTICE: table "d" does not exist, skipping +DROP TABLE IF EXISTS r; +NOTICE: table "r" does not exist, skipping +--end_ignore +CREATE TABLE d (a int, b int) DISTRIBUTED BY (a); +INSERT INTO d VALUES ( 1, 2 ),( 2, 3 ); +CREATE TABLE r (a int, b int) DISTRIBUTED REPLICATED; +INSERT INTO r VALUES ( 1, 2 ),( 3, 4 ); +EXPLAIN (COSTS off) +WITH cte AS ( + SELECT count(*) a FROM r +) SELECT * FROM cte JOIN (SELECT * FROM d JOIN cte USING (a) LIMIT 1) d_join_cte USING (a); + QUERY PLAN +--------------------------------------------------------------- + Hash Join + Hash Cond: (d_join_cte.a = (count(*))) + -> Subquery Scan on d_join_cte + -> Limit + -> Gather Motion 3:1 (slice1; segments: 3) + -> Limit + -> Hash Join + Hash Cond: (d.a = (count(*))) + -> Seq Scan on d + -> Hash + -> Aggregate + -> Seq Scan on r + -> Hash + -> Gather Motion 1:1 (slice2; segments: 1) + -> Aggregate + -> Seq Scan on r r_1 + Optimizer: Postgres query optimizer +(17 rows) + +WITH cte AS ( + SELECT count(*) a FROM r +) SELECT * FROM cte JOIN (SELECT * FROM d JOIN cte USING (a) LIMIT 1) d_join_cte USING (a); + a | b +---+--- + 2 | 3 +(1 row) + +-- Test if sharing is disabled for a General CTE to avoid deadlock if CTE is +-- executed with coordinator gang and joined with n-gang +EXPLAIN (COSTS OFF) +WITH cte AS ( + SELECT count(*) a FROM (VALUES ( 1, 2 ),( 3, 4 )) v +) +SELECT * FROM cte JOIN (SELECT * FROM d JOIN cte USING (a) LIMIT 1) d_join_cte USING (a); + QUERY PLAN +--------------------------------------------------------------------------- + Hash Join + Hash Cond: (d_join_cte.a = (count(*))) + -> Subquery Scan on d_join_cte + -> Limit + -> Gather Motion 3:1 (slice1; segments: 3) + -> Limit + -> Hash Join + Hash Cond: (d.a = (count(*))) + -> Seq Scan on d + -> Hash + -> Aggregate + -> Values Scan on "*VALUES*" + -> Hash + -> Aggregate + -> Values Scan on "*VALUES*_1" + Optimizer: Postgres query optimizer +(16 rows) + +WITH cte AS ( + SELECT count(*) a FROM (VALUES ( 1, 2 ),( 3, 4 )) v +) +SELECT * FROM cte JOIN (SELECT * FROM d JOIN cte USING (a) LIMIT 1) d_join_cte USING (a); + a | b +---+--- + 2 | 3 +(1 row) + +RESET optimizer; +DROP TABLE d; +DROP TABLE r; diff --git a/src/test/regress/sql/returning_gp.sql b/src/test/regress/sql/returning_gp.sql index 8ca158a520e7..3397ea2c5a4b 100644 --- a/src/test/regress/sql/returning_gp.sql +++ b/src/test/regress/sql/returning_gp.sql @@ -2,6 +2,8 @@ -- Extra GPDB tests on INSERT/UPDATE/DELETE RETURNING -- +SET optimizer_trace_fallback=ON; + CREATE TABLE returning_parttab (distkey int4, partkey int4, i int, t text) DISTRIBUTED BY (distkey) PARTITION BY RANGE (partkey) (START (1) END (10)); @@ -31,7 +33,7 @@ returning distkey, partkey, t; update returning_parttab set partkey = 9 where partkey = 3 returning *; update returning_parttab set partkey = 19 where partkey = 13 returning *; --- update that moves the tuple across partitions (not supported) +-- update that moves the tuple across partitions update returning_parttab set partkey = 18 where partkey = 4 returning *; -- delete @@ -41,6 +43,16 @@ delete from returning_parttab where partkey = 14 returning *; -- Check table contents, to be sure that all the commands did what they claimed. select * from returning_parttab; +-- Test DML on partitioned table with RETURNING subquery with cte +explain (costs off) +with cte as ( + select distkey from returning_parttab order by distkey limit 1 +) insert into returning_parttab values (1, 5, 'test') returning (select * from cte); + +with cte as ( + select distkey from returning_parttab order by distkey limit 1 +) insert into returning_parttab values (1, 5, 'test') returning (select * from cte); + -- -- Test UPDATE RETURNING with a split update, i.e. an update of the distribution -- key. diff --git a/src/test/regress/sql/with_clause.sql b/src/test/regress/sql/with_clause.sql index e163ec78c1ab..d87ad44a52f1 100644 --- a/src/test/regress/sql/with_clause.sql +++ b/src/test/regress/sql/with_clause.sql @@ -520,6 +520,17 @@ explain (costs off) create table t_new as (with cte as (delete from with_dml where i > 0 returning *) select * from cte); + +-- Test usage of system columns returned from DML operations +explain (costs off) +with cte as ( + insert into with_dml select i, i * 100 from generate_series(1,5) i + returning i, gp_segment_id +) select i from cte order by gp_segment_id; +with cte as ( + insert into with_dml select i, i * 100 from generate_series(1,5) i + returning i, gp_segment_id +) select i from cte order by gp_segment_id; drop table with_dml; -- Test various SELECT statements from CTE with