/* * Executor state preparation for evaluation of constraint expressions, * indexes and triggers. * * This is based on similar code in copy.c */ static EState * create_estate_for_relation(LogicalRepRelMapEntry *rel) { EState *estate; ResultRelInfo *resultRelInfo; RangeTblEntry *rte; estate = CreateExecutorState(); rte = makeNode(RangeTblEntry); rte->rtekind = RTE_RELATION; rte->relid = RelationGetRelid(rel->localrel); rte->relkind = rel->localrel->rd_rel->relkind; estate->es_range_table = list_make1(rte); resultRelInfo = makeNode(ResultRelInfo); InitResultRelInfo(resultRelInfo, rel->localrel, 1, NULL, 0); estate->es_result_relations = resultRelInfo; estate->es_num_result_relations = 1; estate->es_result_relation_info = resultRelInfo; estate->es_output_cid = GetCurrentCommandId(true); /* Triggers might need a slot */ if (resultRelInfo->ri_TrigDesc) estate->es_trig_tuple_slot = ExecInitExtraTupleSlot(estate, NULL); /* Prepare to catch AFTER triggers. */ AfterTriggerBeginQuery(); return estate; }
/** * Init nodeDML, which initializes the insert TupleTableSlot. * */ DMLState* ExecInitDML(DML *node, EState *estate, int eflags) { /* check for unsupported flags */ Assert(!(eflags & (EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK | EXEC_FLAG_REWIND))); DMLState *dmlstate = makeNode(DMLState); dmlstate->ps.plan = (Plan *)node; dmlstate->ps.state = estate; ExecInitResultTupleSlot(estate, &dmlstate->ps); dmlstate->ps.targetlist = (List *) ExecInitExpr((Expr *) node->plan.targetlist, (PlanState *) dmlstate); Plan *outerPlan = outerPlan(node); outerPlanState(dmlstate) = ExecInitNode(outerPlan, estate, eflags); ExecAssignResultTypeFromTL(&dmlstate->ps); /* Create expression evaluation context. This will be used for projections */ ExecAssignExprContext(estate, &dmlstate->ps); /* * Create projection info from the child tuple descriptor and our target list * Projection will be placed in the ResultSlot */ TupleTableSlot *childResultSlot = outerPlanState(dmlstate)->ps_ResultTupleSlot; ExecAssignProjectionInfo(&dmlstate->ps, childResultSlot->tts_tupleDescriptor); /* * Initialize slot to insert/delete using output relation descriptor. */ dmlstate->cleanedUpSlot = ExecInitExtraTupleSlot(estate); /* * Both input and output of the junk filter include dropped attributes, so * the junk filter doesn't need to do anything special there about them */ TupleDesc cleanTupType = CreateTupleDescCopy(dmlstate->ps.state->es_result_relation_info->ri_RelationDesc->rd_att); dmlstate->junkfilter = ExecInitJunkFilter(node->plan.targetlist, cleanTupType, dmlstate->cleanedUpSlot); if (estate->es_instrument) { dmlstate->ps.cdbexplainbuf = makeStringInfo(); /* Request a callback at end of query. */ dmlstate->ps.cdbexplainfun = ExecDMLExplainEnd; } initGpmonPktForDML((Plan *)node, &dmlstate->ps.gpmon_pkt, estate); return dmlstate; }
/* * Handle INSERT message. */ static void apply_handle_insert(StringInfo s) { LogicalRepRelMapEntry *rel; LogicalRepTupleData newtup; LogicalRepRelId relid; EState *estate; TupleTableSlot *remoteslot; MemoryContext oldctx; ensure_transaction(); relid = logicalrep_read_insert(s, &newtup); rel = logicalrep_rel_open(relid, RowExclusiveLock); if (!should_apply_changes_for_rel(rel)) { /* * The relation can't become interesting in the middle of the * transaction so it's safe to unlock it. */ logicalrep_rel_close(rel, RowExclusiveLock); return; } /* Initialize the executor state. */ estate = create_estate_for_relation(rel); remoteslot = ExecInitExtraTupleSlot(estate, RelationGetDescr(rel->localrel)); /* Input functions may need an active snapshot, so get one */ PushActiveSnapshot(GetTransactionSnapshot()); /* Process and store remote tuple in the slot */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); slot_store_cstrings(remoteslot, rel, newtup.values); slot_fill_defaults(rel, estate, remoteslot); MemoryContextSwitchTo(oldctx); ExecOpenIndices(estate->es_result_relation_info, false); /* Do the insert. */ ExecSimpleRelationInsert(estate, remoteslot); /* Cleanup. */ ExecCloseIndices(estate->es_result_relation_info); PopActiveSnapshot(); /* Handle queued AFTER triggers. */ AfterTriggerEndQuery(estate); ExecResetTupleTable(estate->es_tupleTable, false); FreeExecutorState(estate); logicalrep_rel_close(rel, NoLock); CommandCounterIncrement(); }
/* ---------------- * ExecInitNullTupleSlot * * Build a slot containing an all-nulls tuple of the given type. * This is used as a substitute for an input tuple when performing an * outer join. * ---------------- */ TupleTableSlot * ExecInitNullTupleSlot(EState *estate, TupleDesc tupType) { TupleTableSlot *slot = ExecInitExtraTupleSlot(estate); ExecSetSlotDescriptor(slot, tupType); return ExecStoreAllNullTuple(slot); }
/* * Set up the data structures that we'll need for Gather Merge. * * We allocate these once on the basis of gm->num_workers, which is an * upper bound for the number of workers we'll actually have. During * a rescan, we reset the structures to empty. This approach simplifies * not leaking memory across rescans. * * In the gm_slots[] array, index 0 is for the leader, and indexes 1 to n * are for workers. The values placed into gm_heap correspond to indexes * in gm_slots[]. The gm_tuple_buffers[] array, however, is indexed from * 0 to n-1; it has no entry for the leader. */ static void gather_merge_setup(GatherMergeState *gm_state) { GatherMerge *gm = castNode(GatherMerge, gm_state->ps.plan); int nreaders = gm->num_workers; int i; /* * Allocate gm_slots for the number of workers + one more slot for leader. * Slot 0 is always for the leader. Leader always calls ExecProcNode() to * read the tuple, and then stores it directly into its gm_slots entry. * For other slots, code below will call ExecInitExtraTupleSlot() to * create a slot for the worker's results. Note that during any single * scan, we might have fewer than num_workers available workers, in which * case the extra array entries go unused. */ gm_state->gm_slots = (TupleTableSlot **) palloc0((nreaders + 1) * sizeof(TupleTableSlot *)); /* Allocate the tuple slot and tuple array for each worker */ gm_state->gm_tuple_buffers = (GMReaderTupleBuffer *) palloc0(nreaders * sizeof(GMReaderTupleBuffer)); for (i = 0; i < nreaders; i++) { /* Allocate the tuple array with length MAX_TUPLE_STORE */ gm_state->gm_tuple_buffers[i].tuple = (HeapTuple *) palloc0(sizeof(HeapTuple) * MAX_TUPLE_STORE); /* Initialize tuple slot for worker */ gm_state->gm_slots[i + 1] = ExecInitExtraTupleSlot(gm_state->ps.state, gm_state->tupDesc, &TTSOpsHeapTuple); } /* Allocate the resources for the merge */ gm_state->gm_heap = binaryheap_allocate(nreaders + 1, heap_compare_slots, gm_state); }
/* ---------------------------------------------------------------- * ExecInitMaterial * ---------------------------------------------------------------- */ MaterialState * ExecInitMaterial(Material *node, EState *estate, int eflags) { MaterialState *matstate; Plan *outerPlan; /* * create state structure */ matstate = makeNode(MaterialState); matstate->ss.ps.plan = (Plan *) node; matstate->ss.ps.state = estate; /* * We must have random access to the subplan output to do backward scan or * mark/restore. We also prefer to materialize the subplan output if we * might be called on to rewind and replay it many times. However, if none * of these cases apply, we can skip storing the data. */ matstate->randomAccess = node->cdb_strict || (eflags & (EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)) != 0; matstate->eof_underlying = false; matstate->ts_state = palloc0(sizeof(GenericTupStore)); matstate->ts_pos = NULL; matstate->ts_markpos = NULL; matstate->share_lk_ctxt = NULL; matstate->ts_destroyed = false; ExecMaterialResetWorkfileState(matstate); /* * Miscellaneous initialization * * Materialization nodes don't need ExprContexts because they never call * ExecQual or ExecProject. */ #define MATERIAL_NSLOTS 2 /* * tuple table initialization * * material nodes only return tuples from their materialized relation. */ ExecInitResultTupleSlot(estate, &matstate->ss.ps); matstate->ss.ss_ScanTupleSlot = ExecInitExtraTupleSlot(estate); /* * If eflag contains EXEC_FLAG_REWIND or EXEC_FLAG_BACKWARD or EXEC_FLAG_MARK, * then this node is not eager free safe. */ matstate->ss.ps.delayEagerFree = ((eflags & (EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)) != 0); /* * initialize child nodes * * We shield the child node from the need to support BACKWARD, or * MARK/RESTORE. */ eflags &= ~(EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK); /* * If Materialize does not have any external parameters, then it * can shield the child node from being rescanned as well, hence * we can clear the EXEC_FLAG_REWIND as well. If there are parameters, * don't clear the REWIND flag, as the child will be rewound. */ if (node->plan.allParam == NULL || node->plan.extParam == NULL) { eflags &= ~EXEC_FLAG_REWIND; } outerPlan = outerPlan(node); /* * A very basic check to see if the optimizer requires the material to do a projection. * Ideally, this check would recursively compare all the target list expressions. However, * such a check is tricky because of the varno mismatch (outer plan may have a varno that * index into range table, while the material may refer to the same relation as "outer" varno) * [JIRA: MPP-25365] */ insist_log(list_length(node->plan.targetlist) == list_length(outerPlan->targetlist), "Material operator does not support projection"); outerPlanState(matstate) = ExecInitNode(outerPlan, estate, eflags); /* * If the child node of a Material is a Motion, then this Material node is * not eager free safe. */ if (IsA(outerPlan((Plan *)node), Motion)) { matstate->ss.ps.delayEagerFree = true; } /* * initialize tuple type. no need to initialize projection info because * this node doesn't do projections. */ ExecAssignResultTypeFromTL(&matstate->ss.ps); ExecAssignScanTypeFromOuterPlan(&matstate->ss); matstate->ss.ps.ps_ProjInfo = NULL; /* * If share input, need to register with range table entry */ if(node->share_type != SHARE_NOTSHARED) { ShareNodeEntry *snEntry = ExecGetShareNodeEntry(estate, node->share_id, true); snEntry->sharePlan = (Node *) node; snEntry->shareState = (Node *) matstate; } initGpmonPktForMaterial((Plan *)node, &matstate->ss.ps.gpmon_pkt, estate); return matstate; }
/* * Handle DELETE message. * * TODO: FDW support */ static void apply_handle_delete(StringInfo s) { LogicalRepRelMapEntry *rel; LogicalRepTupleData oldtup; LogicalRepRelId relid; Oid idxoid; EState *estate; EPQState epqstate; TupleTableSlot *remoteslot; TupleTableSlot *localslot; bool found; MemoryContext oldctx; ensure_transaction(); relid = logicalrep_read_delete(s, &oldtup); rel = logicalrep_rel_open(relid, RowExclusiveLock); if (!should_apply_changes_for_rel(rel)) { /* * The relation can't become interesting in the middle of the * transaction so it's safe to unlock it. */ logicalrep_rel_close(rel, RowExclusiveLock); return; } /* Check if we can do the delete. */ check_relation_updatable(rel); /* Initialize the executor state. */ estate = create_estate_for_relation(rel); remoteslot = ExecInitExtraTupleSlot(estate, RelationGetDescr(rel->localrel)); localslot = ExecInitExtraTupleSlot(estate, RelationGetDescr(rel->localrel)); EvalPlanQualInit(&epqstate, estate, NULL, NIL, -1); PushActiveSnapshot(GetTransactionSnapshot()); ExecOpenIndices(estate->es_result_relation_info, false); /* Find the tuple using the replica identity index. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); slot_store_cstrings(remoteslot, rel, oldtup.values); MemoryContextSwitchTo(oldctx); /* * Try to find tuple using either replica identity index, primary key or * if needed, sequential scan. */ idxoid = GetRelationIdentityOrPK(rel->localrel); Assert(OidIsValid(idxoid) || (rel->remoterel.replident == REPLICA_IDENTITY_FULL)); if (OidIsValid(idxoid)) found = RelationFindReplTupleByIndex(rel->localrel, idxoid, LockTupleExclusive, remoteslot, localslot); else found = RelationFindReplTupleSeq(rel->localrel, LockTupleExclusive, remoteslot, localslot); /* If found delete it. */ if (found) { EvalPlanQualSetSlot(&epqstate, localslot); /* Do the actual delete. */ ExecSimpleRelationDelete(estate, &epqstate, localslot); } else { /* The tuple to be deleted could not be found. */ ereport(DEBUG1, (errmsg("logical replication could not find row for delete " "in replication target relation \"%s\"", RelationGetRelationName(rel->localrel)))); } /* Cleanup. */ ExecCloseIndices(estate->es_result_relation_info); PopActiveSnapshot(); /* Handle queued AFTER triggers. */ AfterTriggerEndQuery(estate); EvalPlanQualEnd(&epqstate); ExecResetTupleTable(estate->es_tupleTable, false); FreeExecutorState(estate); logicalrep_rel_close(rel, NoLock); CommandCounterIncrement(); }
/* * Handle UPDATE message. * * TODO: FDW support */ static void apply_handle_update(StringInfo s) { LogicalRepRelMapEntry *rel; LogicalRepRelId relid; Oid idxoid; EState *estate; EPQState epqstate; LogicalRepTupleData oldtup; LogicalRepTupleData newtup; bool has_oldtup; TupleTableSlot *localslot; TupleTableSlot *remoteslot; bool found; MemoryContext oldctx; ensure_transaction(); relid = logicalrep_read_update(s, &has_oldtup, &oldtup, &newtup); rel = logicalrep_rel_open(relid, RowExclusiveLock); if (!should_apply_changes_for_rel(rel)) { /* * The relation can't become interesting in the middle of the * transaction so it's safe to unlock it. */ logicalrep_rel_close(rel, RowExclusiveLock); return; } /* Check if we can do the update. */ check_relation_updatable(rel); /* Initialize the executor state. */ estate = create_estate_for_relation(rel); remoteslot = ExecInitExtraTupleSlot(estate, RelationGetDescr(rel->localrel)); localslot = ExecInitExtraTupleSlot(estate, RelationGetDescr(rel->localrel)); EvalPlanQualInit(&epqstate, estate, NULL, NIL, -1); PushActiveSnapshot(GetTransactionSnapshot()); ExecOpenIndices(estate->es_result_relation_info, false); /* Build the search tuple. */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); slot_store_cstrings(remoteslot, rel, has_oldtup ? oldtup.values : newtup.values); MemoryContextSwitchTo(oldctx); /* * Try to find tuple using either replica identity index, primary key or * if needed, sequential scan. */ idxoid = GetRelationIdentityOrPK(rel->localrel); Assert(OidIsValid(idxoid) || (rel->remoterel.replident == REPLICA_IDENTITY_FULL && has_oldtup)); if (OidIsValid(idxoid)) found = RelationFindReplTupleByIndex(rel->localrel, idxoid, LockTupleExclusive, remoteslot, localslot); else found = RelationFindReplTupleSeq(rel->localrel, LockTupleExclusive, remoteslot, localslot); ExecClearTuple(remoteslot); /* * Tuple found. * * Note this will fail if there are other conflicting unique indexes. */ if (found) { /* Process and store remote tuple in the slot */ oldctx = MemoryContextSwitchTo(GetPerTupleMemoryContext(estate)); ExecStoreTuple(localslot->tts_tuple, remoteslot, InvalidBuffer, false); slot_modify_cstrings(remoteslot, rel, newtup.values, newtup.changed); MemoryContextSwitchTo(oldctx); EvalPlanQualSetSlot(&epqstate, remoteslot); /* Do the actual update. */ ExecSimpleRelationUpdate(estate, &epqstate, localslot, remoteslot); } else { /* * The tuple to be updated could not be found. * * TODO what to do here, change the log level to LOG perhaps? */ elog(DEBUG1, "logical replication did not find row for update " "in replication target relation \"%s\"", RelationGetRelationName(rel->localrel)); } /* Cleanup. */ ExecCloseIndices(estate->es_result_relation_info); PopActiveSnapshot(); /* Handle queued AFTER triggers. */ AfterTriggerEndQuery(estate); EvalPlanQualEnd(&epqstate); ExecResetTupleTable(estate->es_tupleTable, false); FreeExecutorState(estate); logicalrep_rel_close(rel, NoLock); CommandCounterIncrement(); }
/* ---------------------------------------------------------------- * ExecInitGather * ---------------------------------------------------------------- */ GatherState * ExecInitGather(Gather *node, EState *estate, int eflags) { GatherState *gatherstate; Plan *outerNode; bool hasoid; TupleDesc tupDesc; /* Gather node doesn't have innerPlan node. */ Assert(innerPlan(node) == NULL); /* * create state structure */ gatherstate = makeNode(GatherState); gatherstate->ps.plan = (Plan *) node; gatherstate->ps.state = estate; gatherstate->need_to_scan_locally = !node->single_copy; /* * Miscellaneous initialization * * create expression context for node */ ExecAssignExprContext(estate, &gatherstate->ps); /* * initialize child expressions */ gatherstate->ps.targetlist = (List *) ExecInitExpr((Expr *) node->plan.targetlist, (PlanState *) gatherstate); gatherstate->ps.qual = (List *) ExecInitExpr((Expr *) node->plan.qual, (PlanState *) gatherstate); /* * tuple table initialization */ gatherstate->funnel_slot = ExecInitExtraTupleSlot(estate); ExecInitResultTupleSlot(estate, &gatherstate->ps); /* * now initialize outer plan */ outerNode = outerPlan(node); outerPlanState(gatherstate) = ExecInitNode(outerNode, estate, eflags); gatherstate->ps.ps_TupFromTlist = false; /* * Initialize result tuple type and projection info. */ ExecAssignResultTypeFromTL(&gatherstate->ps); ExecAssignProjectionInfo(&gatherstate->ps, NULL); /* * Initialize funnel slot to same tuple descriptor as outer plan. */ if (!ExecContextForcesOids(&gatherstate->ps, &hasoid)) hasoid = false; tupDesc = ExecTypeFromTL(outerNode->targetlist, hasoid); ExecSetSlotDescriptor(gatherstate->funnel_slot, tupDesc); return gatherstate; }
/* ---------------------------------------------------------------- * ExecInitSort * * Creates the run-time state information for the sort node * produced by the planner and initializes its outer subtree. * ---------------------------------------------------------------- */ SortState * ExecInitSort(Sort *node, EState *estate, int eflags) { SortState *sortstate; SO1_printf("ExecInitSort: %s\n", "initializing sort node"); /* * create state structure */ sortstate = makeNode(SortState); sortstate->ss.ps.plan = (Plan *) node; sortstate->ss.ps.state = estate; /* * We must have random access to the sort output to do backward scan or * mark/restore. We also prefer to materialize the sort output if we * might be called on to rewind and replay it many times. */ sortstate->randomAccess = (eflags & (EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)) != 0; /* If the sort is shared, we need random access */ if(node->share_type != SHARE_NOTSHARED) sortstate->randomAccess = true; sortstate->sort_Done = false; sortstate->tuplesortstate = palloc0(sizeof(GenericTupStore)); sortstate->share_lk_ctxt = NULL; ExecSortResetWorkfileState(sortstate); /* CDB */ /* BUT: * The LIMIT optimizations requires exprcontext in which to * evaluate the limit/offset parameters. */ ExecAssignExprContext(estate, &sortstate->ss.ps); /* CDB */ /* evaluate a limit as part of the sort */ { /* pass node state to sort state */ sortstate->limitOffset = ExecInitExpr((Expr *) node->limitOffset, (PlanState *) sortstate); sortstate->limitCount = ExecInitExpr((Expr *) node->limitCount, (PlanState *) sortstate); sortstate->noduplicates = node->noduplicates; } /* * Miscellaneous initialization * * Sort nodes don't initialize their ExprContexts because they never call * ExecQual or ExecProject. */ #define SORT_NSLOTS 2 /* * tuple table initialization * * sort nodes only return scan tuples from their sorted relation. */ ExecInitResultTupleSlot(estate, &sortstate->ss.ps); sortstate->ss.ss_ScanTupleSlot = ExecInitExtraTupleSlot(estate); /* * CDB: Offer extra info for EXPLAIN ANALYZE. */ if (estate->es_instrument) { /* Allocate string buffer. */ sortstate->ss.ps.cdbexplainbuf = makeStringInfo(); /* Request a callback at end of query. */ sortstate->ss.ps.cdbexplainfun = ExecSortExplainEnd; } /* * If eflag contains EXEC_FLAG_REWIND or EXEC_FLAG_BACKWARD or EXEC_FLAG_MARK, * then this node is not eager free safe. */ sortstate->ss.ps.delayEagerFree = ((eflags & (EXEC_FLAG_REWIND | EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK)) != 0); /* * initialize child nodes * * We shield the child node from the need to support BACKWARD, or * MARK/RESTORE. */ eflags &= ~(EXEC_FLAG_BACKWARD | EXEC_FLAG_MARK); /* * If Sort does not have any external parameters, then it * can shield the child node from being rescanned as well, hence * we can clear the EXEC_FLAG_REWIND as well. If there are parameters, * don't clear the REWIND flag, as the child will be rewound. */ if (node->plan.allParam == NULL || node->plan.extParam == NULL) { eflags &= ~EXEC_FLAG_REWIND; } outerPlanState(sortstate) = ExecInitNode(outerPlan(node), estate, eflags); /* * If the child node of a Material is a Motion, then this Material node is * not eager free safe. */ if (IsA(outerPlan((Plan *)node), Motion)) { sortstate->ss.ps.delayEagerFree = true; } /* * initialize tuple type. no need to initialize projection info because * this node doesn't do projections. */ ExecAssignResultTypeFromTL(&sortstate->ss.ps); ExecAssignScanTypeFromOuterPlan(&sortstate->ss); sortstate->ss.ps.ps_ProjInfo = NULL; if(node->share_type != SHARE_NOTSHARED) { ShareNodeEntry *snEntry = ExecGetShareNodeEntry(estate, node->share_id, true); snEntry->sharePlan = (Node *)node; snEntry->shareState = (Node *)sortstate; } SO1_printf("ExecInitSort: %s\n", "sort node initialized"); initGpmonPktForSort((Plan *)node, &sortstate->ss.ps.gpmon_pkt, estate); return sortstate; }
/* ------------------------------------------------------------------ * ExecInitShareInputScan * ------------------------------------------------------------------ */ ShareInputScanState * ExecInitShareInputScan(ShareInputScan *node, EState *estate, int eflags) { ShareInputScanState *sisstate; Plan *outerPlan; TupleDesc tupDesc; Assert(innerPlan(node) == NULL); /* create state data structure */ sisstate = makeNode(ShareInputScanState); sisstate->ss.ps.plan = (Plan *) node; sisstate->ss.ps.state = estate; sisstate->ts_state = NULL; sisstate->ts_pos = NULL; sisstate->ts_markpos = NULL; sisstate->share_lk_ctxt = NULL; sisstate->freed = false; /* * init child node. * if outerPlan is NULL, this is no-op (so that the ShareInput node will be * only init-ed once). */ outerPlan = outerPlan(node); outerPlanState(sisstate) = ExecInitNode(outerPlan, estate, eflags); sisstate->ss.ps.targetlist = (List *) ExecInitExpr((Expr *) node->plan.targetlist, (PlanState *) sisstate); Assert(node->plan.qual == NULL); sisstate->ss.ps.qual = NULL; /* Misc initialization * * Create expression context */ ExecAssignExprContext(estate, &sisstate->ss.ps); /* tuple table init */ ExecInitResultTupleSlot(estate, &sisstate->ss.ps); sisstate->ss.ss_ScanTupleSlot = ExecInitExtraTupleSlot(estate); /* * init tuple type. */ ExecAssignResultTypeFromTL(&sisstate->ss.ps); { bool hasoid; if (!ExecContextForcesOids(&sisstate->ss.ps, &hasoid)) hasoid = false; tupDesc = ExecTypeFromTL(node->plan.targetlist, hasoid); } ExecAssignScanType(&sisstate->ss, tupDesc); sisstate->ss.ps.ps_ProjInfo = NULL; /* * If this is an intra-slice share node, increment reference count to * tell the underlying node not to be freed before this node is ready to * be freed. fCreate flag to ExecGetShareNodeEntry is true because * at this point we don't have the entry which will be initialized in * the underlying node initialization later. */ if (node->share_type == SHARE_MATERIAL || node->share_type == SHARE_SORT) { ShareNodeEntry *snEntry = ExecGetShareNodeEntry(estate, node->share_id, true); snEntry->refcount++; } initGpmonPktForShareInputScan((Plan *)node, &sisstate->ss.ps.gpmon_pkt, estate); return sisstate; }
/* ---------------------------------------------------------------- * ExecInitGather * ---------------------------------------------------------------- */ GatherState * ExecInitGather(Gather *node, EState *estate, int eflags) { GatherState *gatherstate; Plan *outerNode; bool hasoid; TupleDesc tupDesc; /* Gather node doesn't have innerPlan node. */ Assert(innerPlan(node) == NULL); /* * create state structure */ gatherstate = makeNode(GatherState); gatherstate->ps.plan = (Plan *) node; gatherstate->ps.state = estate; gatherstate->ps.ExecProcNode = ExecGather; gatherstate->initialized = false; gatherstate->need_to_scan_locally = !node->single_copy && parallel_leader_participation; gatherstate->tuples_needed = -1; /* * Miscellaneous initialization * * create expression context for node */ ExecAssignExprContext(estate, &gatherstate->ps); /* * Gather doesn't support checking a qual (it's always more efficient to * do it in the child node). */ Assert(!node->plan.qual); /* * tuple table initialization */ gatherstate->funnel_slot = ExecInitExtraTupleSlot(estate); ExecInitResultTupleSlot(estate, &gatherstate->ps); /* * now initialize outer plan */ outerNode = outerPlan(node); outerPlanState(gatherstate) = ExecInitNode(outerNode, estate, eflags); /* * Initialize funnel slot to same tuple descriptor as outer plan. */ if (!ExecContextForcesOids(outerPlanState(gatherstate), &hasoid)) hasoid = false; tupDesc = ExecTypeFromTL(outerNode->targetlist, hasoid); ExecSetSlotDescriptor(gatherstate->funnel_slot, tupDesc); /* * Initialize result tuple type and projection info. */ ExecAssignResultTypeFromTL(&gatherstate->ps); ExecConditionalAssignProjectionInfo(&gatherstate->ps, tupDesc, OUTER_VAR); return gatherstate; }
/* * Initialize the Gather merge tuple read. * * Pull at least a single tuple from each worker + leader and set up the heap. */ static void gather_merge_init(GatherMergeState *gm_state) { int nreaders = gm_state->nreaders; bool initialize = true; int i; /* * Allocate gm_slots for the number of worker + one more slot for leader. * Last slot is always for leader. Leader always calls ExecProcNode() to * read the tuple which will return the TupleTableSlot. Later it will * directly get assigned to gm_slot. So just initialize leader gm_slot * with NULL. For other slots below code will call * ExecInitExtraTupleSlot() which will do the initialization of worker * slots. */ gm_state->gm_slots = palloc((gm_state->nreaders + 1) * sizeof(TupleTableSlot *)); gm_state->gm_slots[gm_state->nreaders] = NULL; /* Initialize the tuple slot and tuple array for each worker */ gm_state->gm_tuple_buffers = (GMReaderTupleBuffer *) palloc0(sizeof(GMReaderTupleBuffer) * (gm_state->nreaders + 1)); for (i = 0; i < gm_state->nreaders; i++) { /* Allocate the tuple array with MAX_TUPLE_STORE size */ gm_state->gm_tuple_buffers[i].tuple = (HeapTuple *) palloc0(sizeof(HeapTuple) * MAX_TUPLE_STORE); /* Initialize slot for worker */ gm_state->gm_slots[i] = ExecInitExtraTupleSlot(gm_state->ps.state); ExecSetSlotDescriptor(gm_state->gm_slots[i], gm_state->tupDesc); } /* Allocate the resources for the merge */ gm_state->gm_heap = binaryheap_allocate(gm_state->nreaders + 1, heap_compare_slots, gm_state); /* * First, try to read a tuple from each worker (including leader) in * nowait mode, so that we initialize read from each worker as well as * leader. After this, if all active workers are unable to produce a * tuple, then re-read and this time use wait mode. For workers that were * able to produce a tuple in the earlier loop and are still active, just * try to fill the tuple array if more tuples are avaiable. */ reread: for (i = 0; i < nreaders + 1; i++) { CHECK_FOR_INTERRUPTS(); if (!gm_state->gm_tuple_buffers[i].done && (TupIsNull(gm_state->gm_slots[i]) || gm_state->gm_slots[i]->tts_isempty)) { if (gather_merge_readnext(gm_state, i, initialize)) { binaryheap_add_unordered(gm_state->gm_heap, Int32GetDatum(i)); } } else form_tuple_array(gm_state, i); } initialize = false; for (i = 0; i < nreaders; i++) if (!gm_state->gm_tuple_buffers[i].done && (TupIsNull(gm_state->gm_slots[i]) || gm_state->gm_slots[i]->tts_isempty)) goto reread; binaryheap_build(gm_state->gm_heap); gm_state->gm_initialized = true; }