/* ---------------------------------------------------------------- * ExecResult(node) * * returns the tuples from the outer plan which satisfy the * qualification clause. Since result nodes with right * subtrees are never planned, we ignore the right subtree * entirely (for now).. -cim 10/7/89 * * The qualification containing only constant clauses are * checked first before any processing is done. It always returns * 'nil' if the constant qualification is not satisfied. * ---------------------------------------------------------------- */ TupleTableSlot * ExecResult(ResultState *node) { TupleTableSlot *outerTupleSlot; TupleTableSlot *resultSlot; PlanState *outerPlan; ExprContext *econtext; ExprDoneCond isDone; econtext = node->ps.ps_ExprContext; /* * check constant qualifications like (2 > 1), if not already done */ if (node->rs_checkqual) { bool qualResult = ExecQual((List *) node->resconstantqual, econtext, false); node->rs_checkqual = false; if (!qualResult) { node->rs_done = true; return NULL; } } /* * Check to see if we're still projecting out tuples from a previous scan * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ if (node->ps.ps_TupFromTlist) { resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone); if (isDone == ExprMultipleResult) return resultSlot; /* Done with that source tuple... */ node->ps.ps_TupFromTlist = false; } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. Note this can't happen * until we're done projecting out tuples from a scan tuple. */ ResetExprContext(econtext); /* * if rs_done is true then it means that we were asked to return a * constant tuple and we already did the last time ExecResult() was * called, OR that we failed the constant qual check. Either way, now we * are through. */ while (!node->rs_done) { outerPlan = outerPlanState(node); if (outerPlan != NULL) { /* * retrieve tuples from the outer plan until there are no more. */ outerTupleSlot = ExecProcNode(outerPlan); if (TupIsNull(outerTupleSlot)) return NULL; /* * prepare to compute projection expressions, which will expect to * access the input tuples as varno OUTER. */ econtext->ecxt_outertuple = outerTupleSlot; } else { /* * if we don't have an outer plan, then we are just generating the * results from a constant target list. Do it only once. */ node->rs_done = true; } /* * form the result tuple using ExecProject(), and return it --- unless * the projection produces an empty set, in which case we must loop * back to see if there are more outerPlan tuples. */ resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return resultSlot; } } return NULL; }
/* * ExecGroup - * * Return one tuple for each group of matching input tuples. */ TupleTableSlot * ExecGroup(GroupState *node) { ExprContext *econtext; int numCols; AttrNumber *grpColIdx; TupleTableSlot *firsttupleslot; TupleTableSlot *outerslot; /* * get state info from node */ if (node->grp_done) return NULL; econtext = node->ss.ps.ps_ExprContext; numCols = ((Group *) node->ss.ps.plan)->numCols; grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx; /* * The ScanTupleSlot holds the (copied) first tuple of each group. */ firsttupleslot = node->ss.ss_ScanTupleSlot; /* * We need not call ResetExprContext here because execTuplesMatch will * reset the per-tuple memory context once per input tuple. */ /* * If first time through, acquire first input tuple and determine whether * to return it or not. */ if (TupIsNull(firsttupleslot)) { outerslot = ExecProcNode(outerPlanState(node)); if (TupIsNull(outerslot)) { /* empty input, so return nothing */ node->grp_done = TRUE; return NULL; } /* Copy tuple, set up as input for qual test and projection */ ExecCopySlot(firsttupleslot, outerslot); econtext->ecxt_scantuple = firsttupleslot; /* * Check the qual (HAVING clause); if the group does not match, ignore * it and fall into scan loop. */ if (ExecQual(node->ss.ps.qual, econtext, false)) { /* * Form and return a projection tuple using the first input tuple. */ return ExecProject(node->ss.ps.ps_ProjInfo, NULL); } } /* * This loop iterates once per input tuple group. At the head of the * loop, we have finished processing the first tuple of the group and now * need to scan over all the other group members. */ for (;;) { /* * Scan over all remaining tuples that belong to this group */ for (;;) { outerslot = ExecProcNode(outerPlanState(node)); if (TupIsNull(outerslot)) { /* no more groups, so we're done */ node->grp_done = TRUE; return NULL; } /* * Compare with first tuple and see if this tuple is of the same * group. If so, ignore it and keep scanning. */ if (!execTuplesMatch(firsttupleslot, outerslot, numCols, grpColIdx, node->eqfunctions, econtext->ecxt_per_tuple_memory)) break; } /* * We have the first tuple of the next input group. See if we want to * return it. */ /* Copy tuple, set up as input for qual test and projection */ ExecCopySlot(firsttupleslot, outerslot); econtext->ecxt_scantuple = firsttupleslot; /* * Check the qual (HAVING clause); if the group does not match, ignore * it and loop back to scan the rest of the group. */ if (ExecQual(node->ss.ps.qual, econtext, false)) { /* * Form and return a projection tuple using the first input tuple. */ return ExecProject(node->ss.ps.ps_ProjInfo, NULL); } } /* NOTREACHED */ return NULL; }
/* ---------------------------------------------------------------- * ExecPartitionSelector(node) * * Compute and propagate partition table Oids that will be * used by Dynamic table scan. There are two ways of * executing PartitionSelector. * * 1. Constant partition elimination * Plan structure: * Sequence * |--PartitionSelector * |--DynamicTableScan * In this case, PartitionSelector evaluates constant partition * constraints to compute and propagate partition table Oids. * It only need to be called once. * * 2. Join partition elimination * Plan structure: * ...: * |--DynamicTableScan * |--... * |--PartitionSelector * |--... * In this case, PartitionSelector is in the same slice as * DynamicTableScan, DynamicIndexScan or DynamicBitmapHeapScan. * It is executed for each tuple coming from its child node. * It evaluates partition constraints with the input tuple and * propagate matched partition table Oids. * * * Instead of a Dynamic Table Scan, there can be other nodes that use * a PartSelected qual to filter rows, based on which partitions are * selected. Currently, ORCA uses Dynamic Table Scans, while plans * produced by the non-ORCA planner use gating Result nodes with * PartSelected quals, to exclude unwanted partitions. * * ---------------------------------------------------------------- */ TupleTableSlot * ExecPartitionSelector(PartitionSelectorState *node) { PartitionSelector *ps = (PartitionSelector *) node->ps.plan; EState *estate = node->ps.state; ExprContext *econtext = node->ps.ps_ExprContext; TupleTableSlot *inputSlot = NULL; TupleTableSlot *candidateOutputSlot = NULL; if (ps->staticSelection) { /* propagate the part oids obtained via static partition selection */ partition_propagation(estate, ps->staticPartOids, ps->staticScanIds, ps->selectorId); return NULL; } /* Retrieve PartitionNode and access method from root table. * We cannot do it during node initialization as * DynamicTableScanInfo is not properly initialized yet. */ if (NULL == node->rootPartitionNode) { Assert(NULL != estate->dynamicTableScanInfo); getPartitionNodeAndAccessMethod ( ps->relid, estate->dynamicTableScanInfo->partsMetadata, estate->es_query_cxt, &node->rootPartitionNode, &node->accessMethods ); } if (NULL != outerPlanState(node)) { /* Join partition elimination */ /* get tuple from outer children */ PlanState *outerPlan = outerPlanState(node); Assert(outerPlan); inputSlot = ExecProcNode(outerPlan); if (TupIsNull(inputSlot)) { /* no more tuples from outerPlan */ /* * Make sure we have an entry for this scan id in * dynamicTableScanInfo. Normally, this would've been done the * first time a partition is selected, but we must ensure that * there is an entry even if no partitions were selected. * (The traditional Postgres planner uses this method.) */ if (ps->partTabTargetlist) InsertPidIntoDynamicTableScanInfo(estate, ps->scanId, InvalidOid, ps->selectorId); else LogPartitionSelection(estate, ps->selectorId); return NULL; } } /* partition elimination with the given input tuple */ ResetExprContext(econtext); node->ps.ps_OuterTupleSlot = inputSlot; econtext->ecxt_outertuple = inputSlot; econtext->ecxt_scantuple = inputSlot; if (NULL != inputSlot) { candidateOutputSlot = ExecProject(node->ps.ps_ProjInfo, NULL); } /* * If we have a partitioning projection, project the input tuple * into a tuple that looks like tuples from the partitioned table, and use * selectPartitionMulti() to select the partitions. (The traditional * Postgres planner uses this method.) */ if (ps->partTabTargetlist) { TupleTableSlot *slot; List *oids; ListCell *lc; slot = ExecProject(node->partTabProj, NULL); slot_getallattrs(slot); oids = selectPartitionMulti(node->rootPartitionNode, slot_get_values(slot), slot_get_isnull(slot), slot->tts_tupleDescriptor, node->accessMethods); foreach (lc, oids) { InsertPidIntoDynamicTableScanInfo(estate, ps->scanId, lfirst_oid(lc), ps->selectorId); }
/* ---------------------------------------------------------------- * ExecNestLoop(node) * * old comments * Returns the tuple joined from inner and outer tuples which * satisfies the qualification clause. * * It scans the inner relation to join with current outer tuple. * * If none is found, next tuple form the outer relation is retrieved * and the inner relation is scanned from the beginning again to join * with the outer tuple. * * Nil is returned if all the remaining outer tuples are tried and * all fail to join with the inner tuples. * * Nil is also returned if there is no tuple from inner realtion. * * Conditions: * -- outerTuple contains current tuple from outer relation and * the right son(inner realtion) maintains "cursor" at the tuple * returned previously. * This is achieved by maintaining a scan position on the outer * relation. * * Initial States: * -- the outer child and the inner child * are prepared to return the first tuple. * ---------------------------------------------------------------- */ TupleTableSlot * ExecNestLoop(NestLoop *node, Plan* parent) { NestLoopState *nlstate; Plan *innerPlan; Plan *outerPlan; bool needNewOuterTuple; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; List *qual; bool qualResult; ExprContext *econtext; /* ---------------- * get information from the node * ---------------- */ ENL1_printf("getting info from node"); nlstate = node->nlstate; qual = node->join.qual; outerPlan = outerPlan(&node->join); innerPlan = innerPlan(&node->join); /* ---------------- * initialize expression context * ---------------- */ econtext = nlstate->jstate.cs_ExprContext; /* ---------------- * get the current outer tuple * ---------------- */ outerTupleSlot = nlstate->jstate.cs_OuterTupleSlot; econtext->ecxt_outertuple = outerTupleSlot; /* ---------------- * Ok, everything is setup for the join so now loop until * we return a qualifying join tuple.. * ---------------- */ if (nlstate->jstate.cs_TupFromTlist) { TupleTableSlot *result; bool isDone; result = ExecProject(nlstate->jstate.cs_ProjInfo, &isDone); if (!isDone) return result; } ENL1_printf("entering main loop"); for(;;) { /* ---------------- * The essential idea now is to get the next inner tuple * and join it with the current outer tuple. * ---------------- */ needNewOuterTuple = false; /* ---------------- * If outer tuple is not null then that means * we are in the middle of a scan and we should * restore our previously saved scan position. * ---------------- */ if (! TupIsNull(outerTupleSlot)) { ENL1_printf("have outer tuple, restoring outer plan"); ExecRestrPos(outerPlan); } else { ENL1_printf("outer tuple is nil, need new outer tuple"); needNewOuterTuple = true; } /* ---------------- * if we have an outerTuple, try to get the next inner tuple. * ---------------- */ if (!needNewOuterTuple) { ENL1_printf("getting new inner tuple"); innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node); econtext->ecxt_innertuple = innerTupleSlot; if (TupIsNull(innerTupleSlot)) { ENL1_printf("no inner tuple, need new outer tuple"); needNewOuterTuple = true; } } /* ---------------- * loop until we have a new outer tuple and a new * inner tuple. * ---------------- */ while (needNewOuterTuple) { /* ---------------- * now try to get the next outer tuple * ---------------- */ ENL1_printf("getting new outer tuple"); outerTupleSlot = ExecProcNode(outerPlan, (Plan*)node); econtext->ecxt_outertuple = outerTupleSlot; /* ---------------- * if there are no more outer tuples, then the join * is complete.. * ---------------- */ if (TupIsNull(outerTupleSlot)) { ENL1_printf("no outer tuple, ending join"); return NULL; } /* ---------------- * we have a new outer tuple so we mark our position * in the outer scan and save the outer tuple in the * NestLoop state * ---------------- */ ENL1_printf("saving new outer tuple information"); ExecMarkPos(outerPlan); nlstate->jstate.cs_OuterTupleSlot = outerTupleSlot; /* ---------------- * now rescan the inner plan and get a new inner tuple * ---------------- */ ENL1_printf("rescanning inner plan"); /* * The scan key of the inner plan might depend on the current * outer tuple (e.g. in index scans), that's why we pass our * expr context. */ ExecReScan(innerPlan, econtext, parent); ENL1_printf("getting new inner tuple"); innerTupleSlot = ExecProcNode(innerPlan, (Plan*)node); econtext->ecxt_innertuple = innerTupleSlot; if (TupIsNull(innerTupleSlot)) { ENL1_printf("couldn't get inner tuple - need new outer tuple"); } else { ENL1_printf("got inner and outer tuples"); needNewOuterTuple = false; } } /* while (needNewOuterTuple) */ /* ---------------- * at this point we have a new pair of inner and outer * tuples so we test the inner and outer tuples to see * if they satisify the node's qualification. * ---------------- */ ENL1_printf("testing qualification"); qualResult = ExecQual((List*)qual, econtext); if (qualResult) { /* ---------------- * qualification was satisified so we project and * return the slot containing the result tuple * using ExecProject(). * ---------------- */ ProjectionInfo *projInfo; TupleTableSlot *result; bool isDone; ENL1_printf("qualification succeeded, projecting tuple"); projInfo = nlstate->jstate.cs_ProjInfo; result = ExecProject(projInfo, &isDone); nlstate->jstate.cs_TupFromTlist = !isDone; return result; } /* ---------------- * qualification failed so we have to try again.. * ---------------- */ ENL1_printf("qualification failed, looping"); } }
/* ---------------------------------------------------------------- * ExecScan * * Scans the relation using the 'access method' indicated and * returns the next qualifying tuple in the direction specified * in the global variable ExecDirection. * The access method returns the next tuple and ExecScan() is * responsible for checking the tuple returned against the qual-clause. * * A 'recheck method' must also be provided that can check an * arbitrary tuple of the relation against any qual conditions * that are implemented internal to the access method. * * Conditions: * -- the "cursor" maintained by the AMI is positioned at the tuple * returned previously. * * Initial States: * -- the relation indicated is opened for scanning so that the * "cursor" is positioned before the first qualifying tuple. * ---------------------------------------------------------------- */ TupleTableSlot * ExecScan(ScanState *node, ExecScanAccessMtd accessMtd, /* function returning a tuple */ ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; List *qual; ProjectionInfo *projInfo; /* * Fetch data from node */ qual = node->ps.qual; projInfo = node->ps.ps_ProjInfo; econtext = node->ps.ps_ExprContext; /* * If we have neither a qual to check nor a projection to do, just skip * all the overhead and return the raw scan tuple. */ if (!qual && !projInfo) { ResetExprContext(econtext); return ExecScanFetch(node, accessMtd, recheckMtd); } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. */ ResetExprContext(econtext); /* * get a tuple from the access method. Loop until we obtain a tuple that * passes the qualification. */ for (;;) { TupleTableSlot *slot; CHECK_FOR_INTERRUPTS(); slot = ExecScanFetch(node, accessMtd, recheckMtd); /* * if the slot returned by the accessMtd contains NULL, then it means * there is nothing more to scan so we just return an empty slot, * being careful to use the projection result slot so it has correct * tupleDesc. */ if (TupIsNull(slot)) { if (projInfo) return ExecClearTuple(projInfo->pi_slot); else return slot; } /* * place the current tuple into the expr context */ econtext->ecxt_scantuple = slot; /* * check that the current tuple satisfies the qual-clause * * check for non-nil qual here to avoid a function call to ExecQual() * when the qual is nil ... saves only a few cycles, but they add up * ... */ if (!qual || ExecQual(qual, econtext, false)) { /* * Found a satisfactory scan tuple. */ if (projInfo) { /* * Form a projection tuple, store it in the result tuple slot * and return it. */ return ExecProject(projInfo); } else { /* * Here, we aren't projecting, so just return scan tuple. */ return slot; } } else InstrCountFiltered1(node, 1); /* * Tuple fails qual, so free per-tuple memory and try again. */ ResetExprContext(econtext); } }
/* ---------------------------------------------------------------- * ExecGather(node) * * Scans the relation via multiple workers and returns * the next qualifying tuple. * ---------------------------------------------------------------- */ static TupleTableSlot * ExecGather(PlanState *pstate) { GatherState *node = castNode(GatherState, pstate); TupleTableSlot *slot; ExprContext *econtext; CHECK_FOR_INTERRUPTS(); /* * Initialize the parallel context and workers on first execution. We do * this on first execution rather than during node initialization, as it * needs to allocate a large dynamic segment, so it is better to do it * only if it is really needed. */ if (!node->initialized) { EState *estate = node->ps.state; Gather *gather = (Gather *) node->ps.plan; /* * Sometimes we might have to run without parallelism; but if parallel * mode is active then we can try to fire up some workers. */ if (gather->num_workers > 0 && estate->es_use_parallel_mode) { ParallelContext *pcxt; /* Initialize, or re-initialize, shared state needed by workers. */ if (!node->pei) node->pei = ExecInitParallelPlan(node->ps.lefttree, estate, gather->initParam, gather->num_workers, node->tuples_needed); else ExecParallelReinitialize(node->ps.lefttree, node->pei, gather->initParam); /* * Register backend workers. We might not get as many as we * requested, or indeed any at all. */ pcxt = node->pei->pcxt; LaunchParallelWorkers(pcxt); /* We save # workers launched for the benefit of EXPLAIN */ node->nworkers_launched = pcxt->nworkers_launched; /* Set up tuple queue readers to read the results. */ if (pcxt->nworkers_launched > 0) { ExecParallelCreateReaders(node->pei); /* Make a working array showing the active readers */ node->nreaders = pcxt->nworkers_launched; node->reader = (TupleQueueReader **) palloc(node->nreaders * sizeof(TupleQueueReader *)); memcpy(node->reader, node->pei->reader, node->nreaders * sizeof(TupleQueueReader *)); } else { /* No workers? Then never mind. */ node->nreaders = 0; node->reader = NULL; } node->nextreader = 0; } /* Run plan locally if no workers or enabled and not single-copy. */ node->need_to_scan_locally = (node->nreaders == 0) || (!gather->single_copy && parallel_leader_participation); node->initialized = true; } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. */ econtext = node->ps.ps_ExprContext; ResetExprContext(econtext); /* * Get next tuple, either from one of our workers, or by running the plan * ourselves. */ slot = gather_getnext(node); if (TupIsNull(slot)) return NULL; /* If no projection is required, we're done. */ if (node->ps.ps_ProjInfo == NULL) return slot; /* * Form the result tuple using ExecProject(), and return it. */ econtext->ecxt_outertuple = slot; return ExecProject(node->ps.ps_ProjInfo); }
/* ---------------------------------------------------------------- * ExecGather(node) * * Scans the relation via multiple workers and returns * the next qualifying tuple. * ---------------------------------------------------------------- */ TupleTableSlot * ExecGather(GatherState *node) { TupleTableSlot *fslot = node->funnel_slot; int i; TupleTableSlot *slot; TupleTableSlot *resultSlot; ExprDoneCond isDone; ExprContext *econtext; /* * Initialize the parallel context and workers on first execution. We do * this on first execution rather than during node initialization, as it * needs to allocate large dynamic segement, so it is better to do if it * is really needed. */ if (!node->initialized) { EState *estate = node->ps.state; Gather *gather = (Gather *) node->ps.plan; /* * Sometimes we might have to run without parallelism; but if * parallel mode is active then we can try to fire up some workers. */ if (gather->num_workers > 0 && IsInParallelMode()) { ParallelContext *pcxt; bool got_any_worker = false; /* Initialize the workers required to execute Gather node. */ if (!node->pei) node->pei = ExecInitParallelPlan(node->ps.lefttree, estate, gather->num_workers); /* * Register backend workers. We might not get as many as we * requested, or indeed any at all. */ pcxt = node->pei->pcxt; LaunchParallelWorkers(pcxt); /* Set up tuple queue readers to read the results. */ if (pcxt->nworkers > 0) { node->nreaders = 0; node->reader = palloc(pcxt->nworkers * sizeof(TupleQueueReader *)); for (i = 0; i < pcxt->nworkers; ++i) { if (pcxt->worker[i].bgwhandle == NULL) continue; shm_mq_set_handle(node->pei->tqueue[i], pcxt->worker[i].bgwhandle); node->reader[node->nreaders++] = CreateTupleQueueReader(node->pei->tqueue[i], fslot->tts_tupleDescriptor); got_any_worker = true; } } /* No workers? Then never mind. */ if (!got_any_worker) ExecShutdownGatherWorkers(node); } /* Run plan locally if no workers or not single-copy. */ node->need_to_scan_locally = (node->reader == NULL) || !gather->single_copy; node->initialized = true; } /* * Check to see if we're still projecting out tuples from a previous scan * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ if (node->ps.ps_TupFromTlist) { resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone); if (isDone == ExprMultipleResult) return resultSlot; /* Done with that source tuple... */ node->ps.ps_TupFromTlist = false; } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. Note we can't do this * until we're done projecting. */ econtext = node->ps.ps_ExprContext; ResetExprContext(econtext); /* Get and return the next tuple, projecting if necessary. */ for (;;) { /* * Get next tuple, either from one of our workers, or by running the * plan ourselves. */ slot = gather_getnext(node); if (TupIsNull(slot)) return NULL; /* * form the result tuple using ExecProject(), and return it --- unless * the projection produces an empty set, in which case we must loop * back around for another tuple */ econtext->ecxt_outertuple = slot; resultSlot = ExecProject(node->ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return resultSlot; } } return slot; }
/* * ExecIndexRecommend * * This function obtains data directly from the RecView, which we * assume is populated with predictions for this user. */ static TupleTableSlot* ExecIndexRecommend(RecScanState *recnode, ExecScanAccessMtd accessMtd, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; List *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; ScanState *node; AttributeInfo *attributes; node = recnode->subscan; attributes = (AttributeInfo*) recnode->attributes; /* * Fetch data from node */ qual = node->ps.qual; projInfo = node->ps.ps_ProjInfo; econtext = node->ps.ps_ExprContext; /* * Check to see if we're still projecting out tuples from a previous scan * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ if (node->ps.ps_TupFromTlist) { Assert(projInfo); /* can't get here if not projecting */ resultSlot = ExecProject(projInfo, &isDone); if (isDone == ExprMultipleResult) return resultSlot; /* Done with that source tuple... */ node->ps.ps_TupFromTlist = false; } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. Note this can't happen * until we're done projecting out tuples from a scan tuple. */ ResetExprContext(econtext); /* * get a tuple from the access method. Loop until we obtain a tuple that * passes the qualification. */ for (;;) { TupleTableSlot *slot; int userID; bool recQual = true; CHECK_FOR_INTERRUPTS(); slot = recnode->ss.ps.ps_ResultTupleSlot; /* If we're using the RecView, we're going to fetch a new * tuple every time. */ slot = ExecRecFetch(node, accessMtd, recheckMtd); /* If the slot is null now, then we've run out of tuples * to return, so we're done. */ if (TupIsNull(slot)) { if (projInfo) return ExecClearTuple(projInfo->pi_slot); else return slot; } /* * Before we check the qualifications, we're going to manually check * to see that the tuple matches the provided user ID, because this * is always necessary and it's easier than messing with the target * list. */ /* First, we'll make sure we're dealing with the right user. */ userID = getTupleInt(slot,attributes->userkey); /* How we could fail to find the user ID, I don't know. */ if (userID < 0) elog(ERROR, "user ID column not found"); /* If this tuple doesn't match the user ID, just skip it * and move on. */ if (userID != attributes->userID) recQual = false; /* * place the current tuple into the expr context */ econtext->ecxt_scantuple = slot; /* * check that the current tuple satisfies the qual-clause * * check for non-nil qual here to avoid a function call to ExecQual() * when the qual is nil ... saves only a few cycles, but they add up * ... * * we also make sure that the tuple passes our recommender qual */ if (recQual && (!qual || ExecQual(qual, econtext, false))) { /* * Found a satisfactory scan tuple. */ if (projInfo) { /* * Form a projection tuple, store it in the result tuple slot * and return it --- unless we find we can project no tuples * from this scan tuple, in which case continue scan. */ resultSlot = ExecProject(projInfo, &isDone); if (isDone != ExprEndResult) { node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return resultSlot; } } else { /* * Here, we aren't projecting, so just return scan tuple. */ return slot; } } else InstrCountFiltered1(node, 1); /* * Tuple fails qual, so free per-tuple memory and try again. */ ResetExprContext(econtext); } }
/* * ExecFilterRecommend * * This function just borrows a tuple descriptor from the RecView, * but we create the data ourselves through various means. */ static TupleTableSlot* ExecFilterRecommend(RecScanState *recnode, ExecScanAccessMtd accessMtd, ExecScanRecheckMtd recheckMtd) { ExprContext *econtext; List *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; ScanState *node; AttributeInfo *attributes; node = recnode->subscan; attributes = (AttributeInfo*) recnode->attributes; /* * Fetch data from node */ qual = node->ps.qual; projInfo = node->ps.ps_ProjInfo; econtext = node->ps.ps_ExprContext; /* * Check to see if we're still projecting out tuples from a previous scan * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ if (node->ps.ps_TupFromTlist) { Assert(projInfo); /* can't get here if not projecting */ resultSlot = ExecProject(projInfo, &isDone); if (isDone == ExprMultipleResult) return resultSlot; /* Done with that source tuple... */ node->ps.ps_TupFromTlist = false; } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. Note this can't happen * until we're done projecting out tuples from a scan tuple. */ ResetExprContext(econtext); /* * get a tuple from the access method. Loop until we obtain a tuple that * passes the qualification. */ for (;;) { TupleTableSlot *slot; int natts, i, userID, userindex, itemID, itemindex; CHECK_FOR_INTERRUPTS(); slot = recnode->ss.ps.ps_ResultTupleSlot; /* The first thing we need to do is initialize our recommender * model and other things, if we haven't done so already. */ if (!recnode->initialized) InitializeRecommender(recnode); /* * If we've exhausted our item list, then we're totally * finished. We set a flag for this. It's possible that * we'll be in the inner loop of a join, through poor * planning, so we'll reset the appropriate data in case * we have to do this again, though our JoinRecommend * should assure this doesn't happen. */ if (recnode->finished) { recnode->finished = false; recnode->userNum = 0; recnode->itemNum = 0; return NULL; } /* We're only going to fetch one tuple and store its tuple * descriptor. We can use this tuple descriptor to make as * many new tuples as we want. */ if (recnode->base_slot == NULL) { slot = ExecRecFetch(node, accessMtd, recheckMtd); recnode->base_slot = CreateTupleDescCopy(slot->tts_tupleDescriptor); } /* Create a new slot to operate on. */ slot = MakeSingleTupleTableSlot(recnode->base_slot); slot->tts_isempty = false; /* * place the current tuple into the expr context */ econtext->ecxt_scantuple = slot; /* Mark all slots as usable. */ natts = slot->tts_tupleDescriptor->natts; for (i = 0; i < natts; i++) { /* Mark slot. */ slot->tts_values[i] = Int32GetDatum(0); slot->tts_isnull[i] = false; slot->tts_nvalid++; } /* While we're here, record what tuple attributes * correspond to our key columns. This will save * us unnecessary strcmp functions. */ if (recnode->useratt < 0) { for (i = 0; i < natts; i++) { char* col_name = slot->tts_tupleDescriptor->attrs[i]->attname.data; //printf("%s\n",col_name); if (strcmp(col_name,attributes->userkey) == 0) recnode->useratt = i; else if (strcmp(col_name,attributes->itemkey) == 0) recnode->itematt = i; else if (strcmp(col_name,attributes->eventval) == 0) recnode->eventatt = i; } } /* * We now have a problem: we need to create prediction structures * for a user before we do filtering, so that we can have a proper * item list. But we also need to filter before creating those * structures, so we don't end up taking forever with it. The * solution is to filter twice. */ userID = -1; itemID = -1; /* First, replace the user ID. */ userindex = recnode->userNum; userID = recnode->userList[userindex]; /* * We now have a blank tuple slot that we need to fill with data. * We have a working user ID, but not a valid item list. We'd like to * use the filter to determine if this is a good user, but we can't * do that without an item, in many cases. The solution is to add in * dummy items, then compare it against the filter. If a given user ID * doesn't make it past the filter with any item ID, then that user is * being filtered out, and we'll move on to the next. */ if (recnode->newUser) { recnode->fullItemNum = 0; itemindex = recnode->fullItemNum; itemID = recnode->fullItemList[itemindex]; slot->tts_values[recnode->useratt] = Int32GetDatum(userID); slot->tts_values[recnode->itematt] = Int32GetDatum(itemID); slot->tts_values[recnode->eventatt] = Int32GetDatum(-1); /* We have a preliminary slot - let's test it. */ while (qual && !ExecQual(qual, econtext, false)) { /* We failed the test. Try the next item. */ recnode->fullItemNum++; if (recnode->fullItemNum >= recnode->fullTotalItems) { /* If we've reached the last item, move onto the next user. * If we've reached the last user, we're done. */ InstrCountFiltered1(node, recnode->fullTotalItems); recnode->userNum++; recnode->newUser = true; recnode->fullItemNum = 0; if (recnode->userNum >= recnode->totalUsers) { recnode->userNum = 0; recnode->itemNum = 0; return NULL; } userindex = recnode->userNum; userID = recnode->userList[userindex]; } itemindex = recnode->fullItemNum; itemID = recnode->fullItemList[itemindex]; slot->tts_values[recnode->useratt] = Int32GetDatum(userID); slot->tts_values[recnode->itematt] = Int32GetDatum(itemID); } /* If we get here, then we found a user who will be actually * returned in the results. One quick reset here. */ recnode->fullItemNum = 0; } /* Mark the user ID and index. */ attributes->userID = userID; recnode->userindex = userindex; /* With the user ID determined, we need to investigate and see * if this is a new user. If so, attempt to create prediction * data structures, or report that this user is invalid. We have * to do this here, so we can establish the item list. */ if (recnode->newUser) { recnode->validUser = prepUserForRating(recnode,userID); recnode->newUser = false; } /* Now replace the item ID, if the user is valid. Otherwise, * leave the item ID as is, as it doesn't matter what it is. */ if (recnode->validUser) itemID = recnode->itemList[recnode->itemNum]; while (recnode->fullItemList[recnode->fullItemNum] < itemID) recnode->fullItemNum++; itemindex = recnode->fullItemNum; if (recnode->fullItemList[itemindex] > itemID) elog(ERROR, "critical item mismatch in ExecRecommend"); /* Plug in the data, marking those columns full. We also need to * mark the rating column with something temporary. */ slot->tts_values[recnode->useratt] = Int32GetDatum(userID); slot->tts_values[recnode->itematt] = Int32GetDatum(itemID); slot->tts_values[recnode->eventatt] = Int32GetDatum(-1); /* It's possible our filter criteria involves the RecScore somehow. * If that's the case, we need to calculate it before we do the * qual filtering. Also, if we're doing a JoinRecommend, we should * not calculate the RecScore in this node. In the current version * of RecDB, an OP_NOFILTER shouldn't be allowed. */ if (attributes->opType == OP_NOFILTER) applyRecScore(recnode, slot, itemID, itemindex); /* Move onto the next item, for next time. If we're doing a RecJoin, * though, we'll move onto the next user instead. */ recnode->itemNum++; if (recnode->itemNum >= recnode->totalItems || attributes->opType == OP_JOIN || attributes->opType == OP_GENERATEJOIN) { /* If we've reached the last item, move onto the next user. * If we've reached the last user, we're done. */ recnode->userNum++; recnode->newUser = true; recnode->itemNum = 0; recnode->fullItemNum = 0; if (recnode->userNum >= recnode->totalUsers) recnode->finished = true; } /* * check that the current tuple satisfies the qual-clause * * check for non-nil qual here to avoid a function call to ExecQual() * when the qual is nil ... saves only a few cycles, but they add up * ... */ if (!qual || ExecQual(qual, econtext, false)) { /* * If this is an invalid user, then we'll skip this tuple, * adding one to the filter count. */ if (!recnode->validUser) { InstrCountFiltered1(node, 1); ResetExprContext(econtext); ExecDropSingleTupleTableSlot(slot); continue; } /* * Found a satisfactory scan tuple. This is usually when * we will calculate and apply the RecScore. */ if (attributes->opType == OP_FILTER || attributes->opType == OP_GENERATE) applyRecScore(recnode, slot, itemID, itemindex); if (projInfo) { /* * Form a projection tuple, store it in the result tuple slot * and return it --- unless we find we can project no tuples * from this scan tuple, in which case continue scan. */ resultSlot = ExecProject(projInfo, &isDone); if (isDone != ExprEndResult) { node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return resultSlot; } } else { /* * Here, we aren't projecting, so just return scan tuple. */ return slot; } } else InstrCountFiltered1(node, 1); /* * Tuple fails qual, so free per-tuple memory and try again. */ ResetExprContext(econtext); ExecDropSingleTupleTableSlot(slot); } }
/* ---------------------------------------------------------------- * ExecNestLoop(node) * * old comments * Returns the tuple joined from inner and outer tuples which * satisfies the qualification clause. * * It scans the inner relation to join with current outer tuple. * * If none is found, next tuple from the outer relation is retrieved * and the inner relation is scanned from the beginning again to join * with the outer tuple. * * NULL is returned if all the remaining outer tuples are tried and * all fail to join with the inner tuples. * * NULL is also returned if there is no tuple from inner relation. * * Conditions: * -- outerTuple contains current tuple from outer relation and * the right son(inner relation) maintains "cursor" at the tuple * returned previously. * This is achieved by maintaining a scan position on the outer * relation. * * Initial States: * -- the outer child and the inner child * are prepared to return the first tuple. * ---------------------------------------------------------------- */ TupleTableSlot * ExecNestLoop(NestLoopState *node) { PlanState *innerPlan; PlanState *outerPlan; TupleTableSlot *outerTupleSlot; TupleTableSlot *innerTupleSlot; List *joinqual; List *otherqual; ExprContext *econtext; /* * get information from the node */ ENL1_printf("getting info from node"); joinqual = node->js.joinqual; otherqual = node->js.ps.qual; outerPlan = outerPlanState(node); innerPlan = innerPlanState(node); econtext = node->js.ps.ps_ExprContext; /* * get the current outer tuple */ outerTupleSlot = node->js.ps.ps_OuterTupleSlot; econtext->ecxt_outertuple = outerTupleSlot; /* * Check to see if we're still projecting out tuples from a previous join * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ if (node->js.ps.ps_TupFromTlist) { TupleTableSlot *result; ExprDoneCond isDone; result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone == ExprMultipleResult) return result; /* Done with that source tuple... */ node->js.ps.ps_TupFromTlist = false; } /* * If we're doing an IN join, we want to return at most one row per outer * tuple; so we can stop scanning the inner scan if we matched on the * previous try. */ if (node->js.jointype == JOIN_IN && node->nl_MatchedOuter) node->nl_NeedNewOuter = true; /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. Note this can't happen * until we're done projecting out tuples from a join tuple. */ ResetExprContext(econtext); /* * Ok, everything is setup for the join so now loop until we return a * qualifying join tuple. */ ENL1_printf("entering main loop"); for (;;) { /* * If we don't have an outer tuple, get the next one and reset the * inner scan. */ if (node->nl_NeedNewOuter) { ENL1_printf("getting new outer tuple"); outerTupleSlot = ExecProcNode(outerPlan); /* * if there are no more outer tuples, then the join is complete.. */ if (TupIsNull(outerTupleSlot)) { ENL1_printf("no outer tuple, ending join"); return NULL; } ENL1_printf("saving new outer tuple information"); node->js.ps.ps_OuterTupleSlot = outerTupleSlot; econtext->ecxt_outertuple = outerTupleSlot; node->nl_NeedNewOuter = false; node->nl_MatchedOuter = false; /* * now rescan the inner plan */ ENL1_printf("rescanning inner plan"); /* * The scan key of the inner plan might depend on the current * outer tuple (e.g. in index scans), that's why we pass our expr * context. */ ExecReScan(innerPlan, econtext); } /* * we have an outerTuple, try to get the next inner tuple. */ ENL1_printf("getting new inner tuple"); innerTupleSlot = ExecProcNode(innerPlan); econtext->ecxt_innertuple = innerTupleSlot; if (TupIsNull(innerTupleSlot)) { ENL1_printf("no inner tuple, need new outer tuple"); node->nl_NeedNewOuter = true; if (!node->nl_MatchedOuter && node->js.jointype == JOIN_LEFT) { /* * We are doing an outer join and there were no join matches * for this outer tuple. Generate a fake join tuple with * nulls for the inner tuple, and return it if it passes the * non-join quals. */ econtext->ecxt_innertuple = node->nl_NullInnerTupleSlot; ENL1_printf("testing qualification for outer-join tuple"); if (ExecQual(otherqual, econtext, false)) { /* * qualification was satisfied so we project and return * the slot containing the result tuple using * ExecProject(). */ TupleTableSlot *result; ExprDoneCond isDone; ENL1_printf("qualification succeeded, projecting tuple"); result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return result; } } } /* * Otherwise just return to top of loop for a new outer tuple. */ continue; } /* * at this point we have a new pair of inner and outer tuples so we * test the inner and outer tuples to see if they satisfy the node's * qualification. * * Only the joinquals determine MatchedOuter status, but all quals * must pass to actually return the tuple. */ ENL1_printf("testing qualification"); if (ExecQual(joinqual, econtext, false)) { node->nl_MatchedOuter = true; if (otherqual == NIL || ExecQual(otherqual, econtext, false)) { /* * qualification was satisfied so we project and return the * slot containing the result tuple using ExecProject(). */ TupleTableSlot *result; ExprDoneCond isDone; ENL1_printf("qualification succeeded, projecting tuple"); result = ExecProject(node->js.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { node->js.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return result; } } /* If we didn't return a tuple, may need to set NeedNewOuter */ if (node->js.jointype == JOIN_IN) node->nl_NeedNewOuter = true; } /* * Tuple fails qual, so free per-tuple memory and try again. */ ResetExprContext(econtext); ENL1_printf("qualification failed, looping"); } }
/* * ExecGroup - * * Return one tuple for each group of matching input tuples. */ TupleTableSlot * ExecGroup(GroupState *node) { ExprContext *econtext; int numCols; AttrNumber *grpColIdx; TupleTableSlot *firsttupleslot; TupleTableSlot *outerslot; /* * get state info from node */ if (node->grp_done) return NULL; econtext = node->ss.ps.ps_ExprContext; numCols = ((Group *) node->ss.ps.plan)->numCols; grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx; /* * Check to see if we're still projecting out tuples from a previous group * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ if (node->ss.ps.ps_TupFromTlist) { TupleTableSlot *result; ExprDoneCond isDone; result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone); if (isDone == ExprMultipleResult) return result; /* Done with that source tuple... */ node->ss.ps.ps_TupFromTlist = false; } /* * The ScanTupleSlot holds the (copied) first tuple of each group. */ firsttupleslot = node->ss.ss_ScanTupleSlot; /* * We need not call ResetExprContext here because execTuplesMatch will * reset the per-tuple memory context once per input tuple. */ /* * If first time through, acquire first input tuple and determine whether * to return it or not. */ if (TupIsNull(firsttupleslot)) { outerslot = ExecProcNode(outerPlanState(node)); if (TupIsNull(outerslot)) { /* empty input, so return nothing */ node->grp_done = TRUE; return NULL; } /* Copy tuple into firsttupleslot */ ExecCopySlot(firsttupleslot, outerslot); /* * Set it up as input for qual test and projection. The expressions * will access the input tuple as varno OUTER. */ econtext->ecxt_outertuple = firsttupleslot; /* * Check the qual (HAVING clause); if the group does not match, ignore * it and fall into scan loop. */ if (ExecQual(node->ss.ps.qual, econtext, false)) { /* * Form and return a projection tuple using the first input tuple. */ TupleTableSlot *result; ExprDoneCond isDone; result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return result; } } else InstrCountFiltered1(node, 1); } /* * This loop iterates once per input tuple group. At the head of the * loop, we have finished processing the first tuple of the group and now * need to scan over all the other group members. */ for (;;) { /* * Scan over all remaining tuples that belong to this group */ for (;;) { outerslot = ExecProcNode(outerPlanState(node)); if (TupIsNull(outerslot)) { /* no more groups, so we're done */ node->grp_done = TRUE; return NULL; } /* * Compare with first tuple and see if this tuple is of the same * group. If so, ignore it and keep scanning. */ if (!execTuplesMatch(firsttupleslot, outerslot, numCols, grpColIdx, node->eqfunctions, econtext->ecxt_per_tuple_memory)) break; } /* * We have the first tuple of the next input group. See if we want to * return it. */ /* Copy tuple, set up as input for qual test and projection */ ExecCopySlot(firsttupleslot, outerslot); econtext->ecxt_outertuple = firsttupleslot; /* * Check the qual (HAVING clause); if the group does not match, ignore * it and loop back to scan the rest of the group. */ if (ExecQual(node->ss.ps.qual, econtext, false)) { /* * Form and return a projection tuple using the first input tuple. */ TupleTableSlot *result; ExprDoneCond isDone; result = ExecProject(node->ss.ps.ps_ProjInfo, &isDone); if (isDone != ExprEndResult) { node->ss.ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return result; } } else InstrCountFiltered1(node, 1); } }
/* * Executes INSERT and DELETE DML operations. The * action is specified within the TupleTableSlot at * plannode->actionColIdx.The ctid of the tuple to delete * is in position plannode->ctidColIdx in the current slot. * */ TupleTableSlot* ExecDML(DMLState *node) { PlanState *outerNode = outerPlanState(node); DML *plannode = (DML *) node->ps.plan; Assert(outerNode != NULL); TupleTableSlot *slot = ExecProcNode(outerNode); if (TupIsNull(slot)) { return NULL; } bool isnull = false; int action = DatumGetUInt32(slot_getattr(slot, plannode->actionColIdx, &isnull)); Assert(!isnull); isnull = false; Datum oid = slot_getattr(slot, plannode->oidColIdx, &isnull); slot->tts_tableOid = DatumGetUInt32(oid); bool isUpdate = false; if (node->ps.state->es_plannedstmt->commandType == CMD_UPDATE) { isUpdate = true; } Assert(action == DML_INSERT || action == DML_DELETE); /* * 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); /* Prepare cleaned-up tuple by projecting it and filtering junk columns */ econtext->ecxt_outertuple = slot; TupleTableSlot *projectedSlot = ExecProject(node->ps.ps_ProjInfo, NULL); /* remove 'junk' columns from tuple */ node->cleanedUpSlot = ExecFilterJunk(node->junkfilter, projectedSlot); if (DML_INSERT == action) { /* Respect any given tuple Oid when updating a tuple. */ if(isUpdate && plannode->tupleoidColIdx != 0) { isnull = false; oid = slot_getattr(slot, plannode->tupleoidColIdx, &isnull); HeapTuple 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.) */ ExecInsert(node->cleanedUpSlot, NULL /* destReceiver */, node->ps.state, PLANGEN_OPTIMIZER /* Plan origin */, isUpdate); } else /* DML_DELETE */ { Datum ctid = slot_getattr(slot, plannode->ctidColIdx, &isnull); Assert(!isnull); ItemPointer tupleid = (ItemPointer) DatumGetPointer(ctid); ItemPointerData tuple_ctid = *tupleid; tupleid = &tuple_ctid; /* Correct tuple count by ignoring deletes when splitting tuples. */ ExecDelete(tupleid, node->cleanedUpSlot, NULL /* DestReceiver */, node->ps.state, PLANGEN_OPTIMIZER /* Plan origin */, isUpdate); } return slot; }
/* * ExecGroup - * * Return one tuple for each group of matching input tuples. */ TupleTableSlot * ExecGroup(GroupState *node) { EState *estate; ExprContext *econtext; TupleDesc tupdesc; int numCols; AttrNumber *grpColIdx; HeapTuple outerTuple = NULL; HeapTuple firsttuple; TupleTableSlot *outerslot; ProjectionInfo *projInfo; TupleTableSlot *resultSlot; /* * get state info from node */ if (node->grp_done) return NULL; estate = node->ss.ps.state; econtext = node->ss.ps.ps_ExprContext; tupdesc = ExecGetScanType(&node->ss); numCols = ((Group *) node->ss.ps.plan)->numCols; grpColIdx = ((Group *) node->ss.ps.plan)->grpColIdx; /* * We need not call ResetExprContext here because execTuplesMatch will * reset the per-tuple memory context once per input tuple. */ /* If we don't already have first tuple of group, fetch it */ /* this should occur on the first call only */ firsttuple = node->grp_firstTuple; if (firsttuple == NULL) { outerslot = ExecProcNode(outerPlanState(node)); if (TupIsNull(outerslot)) { node->grp_done = TRUE; return NULL; } node->grp_firstTuple = firsttuple = heap_copytuple(outerslot->val); } /* * Scan over all tuples that belong to this group */ for (;;) { outerslot = ExecProcNode(outerPlanState(node)); if (TupIsNull(outerslot)) { node->grp_done = TRUE; outerTuple = NULL; break; } outerTuple = outerslot->val; /* * Compare with first tuple and see if this tuple is of the same * group. */ if (!execTuplesMatch(firsttuple, outerTuple, tupdesc, numCols, grpColIdx, node->eqfunctions, econtext->ecxt_per_tuple_memory)) break; } /* * form a projection tuple based on the (copied) first tuple of the * group, and store it in the result tuple slot. */ ExecStoreTuple(firsttuple, node->ss.ss_ScanTupleSlot, InvalidBuffer, false); econtext->ecxt_scantuple = node->ss.ss_ScanTupleSlot; projInfo = node->ss.ps.ps_ProjInfo; resultSlot = ExecProject(projInfo, NULL); /* save first tuple of next group, if we are not done yet */ if (!node->grp_done) { heap_freetuple(firsttuple); node->grp_firstTuple = heap_copytuple(outerTuple); } return resultSlot; }
/* ---------------------------------------------------------------- * ExecGatherMerge(node) * * Scans the relation via multiple workers and returns * the next qualifying tuple. * ---------------------------------------------------------------- */ static TupleTableSlot * ExecGatherMerge(PlanState *pstate) { GatherMergeState *node = castNode(GatherMergeState, pstate); TupleTableSlot *slot; ExprContext *econtext; CHECK_FOR_INTERRUPTS(); /* * As with Gather, we don't launch workers until this node is actually * executed. */ if (!node->initialized) { EState *estate = node->ps.state; GatherMerge *gm = castNode(GatherMerge, node->ps.plan); /* * Sometimes we might have to run without parallelism; but if parallel * mode is active then we can try to fire up some workers. */ if (gm->num_workers > 0 && estate->es_use_parallel_mode) { ParallelContext *pcxt; /* Initialize, or re-initialize, shared state needed by workers. */ if (!node->pei) node->pei = ExecInitParallelPlan(node->ps.lefttree, estate, gm->initParam, gm->num_workers, node->tuples_needed); else ExecParallelReinitialize(node->ps.lefttree, node->pei, gm->initParam); /* Try to launch workers. */ pcxt = node->pei->pcxt; LaunchParallelWorkers(pcxt); /* We save # workers launched for the benefit of EXPLAIN */ node->nworkers_launched = pcxt->nworkers_launched; /* Set up tuple queue readers to read the results. */ if (pcxt->nworkers_launched > 0) { ExecParallelCreateReaders(node->pei); /* Make a working array showing the active readers */ node->nreaders = pcxt->nworkers_launched; node->reader = (TupleQueueReader **) palloc(node->nreaders * sizeof(TupleQueueReader *)); memcpy(node->reader, node->pei->reader, node->nreaders * sizeof(TupleQueueReader *)); } else { /* No workers? Then never mind. */ node->nreaders = 0; node->reader = NULL; } } /* allow leader to participate if enabled or no choice */ if (parallel_leader_participation || node->nreaders == 0) node->need_to_scan_locally = true; node->initialized = true; } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. */ econtext = node->ps.ps_ExprContext; ResetExprContext(econtext); /* * Get next tuple, either from one of our workers, or by running the plan * ourselves. */ slot = gather_merge_getnext(node); if (TupIsNull(slot)) return NULL; /* If no projection is required, we're done. */ if (node->ps.ps_ProjInfo == NULL) return slot; /* * Form the result tuple using ExecProject(), and return it. */ econtext->ecxt_outertuple = slot; return ExecProject(node->ps.ps_ProjInfo); }
/* * Repeatly output each tuple received from the outer plan with some * defined number of times. The number of times to output a tuple is * determined by the value of a given column in the received tuple. * * Note that the Repeat node also have the functionality to evaluate * the GroupingFunc. */ TupleTableSlot * ExecRepeat(RepeatState *repeatstate) { TupleTableSlot *outerslot; ExprContext *econtext = repeatstate->ps.ps_ExprContext; Repeat *node = (Repeat *)repeatstate->ps.plan; if (repeatstate->repeat_done) return NULL; /* * If the previous tuple still needs to be outputted, * output it here. */ if (repeatstate->slot != NULL) { if (repeatstate->repeat_count > 0) { /* Output the previous tuple */ econtext->ecxt_outertuple = repeatstate->slot; econtext->ecxt_scantuple = repeatstate->slot; do { econtext->group_id = repeatstate->repeat_count - 1; econtext->grouping = node->grouping; repeatstate->repeat_count--; /* Check the qual until we find one output tuple. */ if (ExecQual(repeatstate->ps.qual, econtext, false)) { Gpmon_M_Incr_Rows_Out(GpmonPktFromRepeatState(repeatstate)); CheckSendPlanStateGpmonPkt(&repeatstate->ps); return ExecProject(repeatstate->ps.ps_ProjInfo, NULL); } } while (repeatstate->repeat_count > 0); } else repeatstate->slot = NULL; } ResetExprContext(econtext); while (!repeatstate->repeat_done) { MemoryContext oldcxt; bool isNull = false; outerslot = ExecProcNode(outerPlanState(repeatstate)); if (TupIsNull(outerslot)) { repeatstate->repeat_done = true; return NULL; } econtext->ecxt_outertuple = outerslot; econtext->ecxt_scantuple = outerslot; /* Compute the number of times to output this tuple. */ oldcxt = MemoryContextSwitchTo(econtext->ecxt_per_tuple_memory); repeatstate->repeat_count = DatumGetInt32(ExecEvalExpr(repeatstate->expr_state, econtext, &isNull, NULL)); Assert(!isNull); MemoryContextSwitchTo(oldcxt); if (repeatstate->repeat_count == 0) continue; if (repeatstate->repeat_count > 1) repeatstate->slot = outerslot; do { econtext->group_id = repeatstate->repeat_count - 1; econtext->grouping = node->grouping; repeatstate->repeat_count--; /* Check the qual until we find one output tuple. */ if (ExecQual(repeatstate->ps.qual, econtext, false)) { Gpmon_M_Incr_Rows_Out(GpmonPktFromRepeatState(repeatstate)); CheckSendPlanStateGpmonPkt(&repeatstate->ps); return ExecProject(repeatstate->ps.ps_ProjInfo, NULL); } } while (repeatstate->repeat_count > 0); } return NULL; }
/* ---------------------------------------------------------------- * ExecResult(node) * * returns the tuples from the outer plan which satisfy the * qualification clause. Since result nodes with right * subtrees are never planned, we ignore the right subtree * entirely (for now).. -cim 10/7/89 * * The qualification containing only constant clauses are * checked first before any processing is done. It always returns * 'nil' if the constant qualification is not satisfied. * ---------------------------------------------------------------- */ static TupleTableSlot * ExecResult(PlanState *pstate) { ResultState *node = castNode(ResultState, pstate); TupleTableSlot *outerTupleSlot; PlanState *outerPlan; ExprContext *econtext; CHECK_FOR_INTERRUPTS(); econtext = node->ps.ps_ExprContext; /* * check constant qualifications like (2 > 1), if not already done */ if (node->rs_checkqual) { bool qualResult = ExecQual(node->resconstantqual, econtext); node->rs_checkqual = false; if (!qualResult) { node->rs_done = true; return NULL; } } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. */ ResetExprContext(econtext); /* * if rs_done is true then it means that we were asked to return a * constant tuple and we already did the last time ExecResult() was * called, OR that we failed the constant qual check. Either way, now we * are through. */ while (!node->rs_done) { outerPlan = outerPlanState(node); if (outerPlan != NULL) { /* * retrieve tuples from the outer plan until there are no more. */ outerTupleSlot = ExecProcNode(outerPlan); if (TupIsNull(outerTupleSlot)) return NULL; /* * prepare to compute projection expressions, which will expect to * access the input tuples as varno OUTER. */ econtext->ecxt_outertuple = outerTupleSlot; } else { /* * if we don't have an outer plan, then we are just generating the * results from a constant target list. Do it only once. */ node->rs_done = true; } /* form the result tuple using ExecProject(), and return it */ return ExecProject(node->ps.ps_ProjInfo); } return NULL; }
/* ---------------------------------------------------------------- * ExecScan * * Scans the relation using the 'access method' indicated and * returns the next qualifying tuple in the direction specified * in the global variable ExecDirection. * The access method returns the next tuple and execScan() is * responsible for checking the tuple returned against the qual-clause. * * Conditions: * -- the "cursor" maintained by the AMI is positioned at the tuple * returned previously. * * Initial States: * -- the relation indicated is opened for scanning so that the * "cursor" is positioned before the first qualifying tuple. * ---------------------------------------------------------------- */ TupleTableSlot * ExecScan(ScanState *node, ExecScanAccessMtd accessMtd) /* function returning a tuple */ { ExprContext *econtext; List *qual; ProjectionInfo *projInfo; ExprDoneCond isDone; TupleTableSlot *resultSlot; /* * Fetch data from node */ qual = node->ps.qual; projInfo = node->ps.ps_ProjInfo; /* * If we have neither a qual to check nor a projection to do, just skip * all the overhead and return the raw scan tuple. */ if (!qual && !projInfo) return (*accessMtd) (node); /* * Check to see if we're still projecting out tuples from a previous scan * tuple (because there is a function-returning-set in the projection * expressions). If so, try to project another one. */ if (node->ps.ps_TupFromTlist) { Assert(projInfo); /* can't get here if not projecting */ resultSlot = ExecProject(projInfo, &isDone); if (isDone == ExprMultipleResult) return resultSlot; /* Done with that source tuple... */ node->ps.ps_TupFromTlist = false; } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. Note this can't happen * until we're done projecting out tuples from a scan tuple. */ econtext = node->ps.ps_ExprContext; ResetExprContext(econtext); /* * get a tuple from the access method loop until we obtain a tuple which * passes the qualification. */ for (;;) { TupleTableSlot *slot; CHECK_FOR_INTERRUPTS(); slot = (*accessMtd) (node); /* * if the slot returned by the accessMtd contains NULL, then it means * there is nothing more to scan so we just return an empty slot, * being careful to use the projection result slot so it has correct * tupleDesc. */ if (TupIsNull(slot)) { if (projInfo) return ExecClearTuple(projInfo->pi_slot); else return slot; } /* * place the current tuple into the expr context */ econtext->ecxt_scantuple = slot; /* * check that the current tuple satisfies the qual-clause * * check for non-nil qual here to avoid a function call to ExecQual() * when the qual is nil ... saves only a few cycles, but they add up * ... */ if (!qual || ExecQual(qual, econtext, false)) { /* * Found a satisfactory scan tuple. */ if (projInfo) { /* * Form a projection tuple, store it in the result tuple slot * and return it --- unless we find we can project no tuples * from this scan tuple, in which case continue scan. */ resultSlot = ExecProject(projInfo, &isDone); if (isDone != ExprEndResult) { node->ps.ps_TupFromTlist = (isDone == ExprMultipleResult); return resultSlot; } } else { /* * Here, we aren't projecting, so just return scan tuple. */ return slot; } } /* * Tuple fails qual, so free per-tuple memory and try again. */ ResetExprContext(econtext); } }
/* ---------------------------------------------------------------- * ExecGatherMerge(node) * * Scans the relation via multiple workers and returns * the next qualifying tuple. * ---------------------------------------------------------------- */ static TupleTableSlot * ExecGatherMerge(PlanState *pstate) { GatherMergeState *node = castNode(GatherMergeState, pstate); TupleTableSlot *slot; ExprContext *econtext; int i; CHECK_FOR_INTERRUPTS(); /* * As with Gather, we don't launch workers until this node is actually * executed. */ if (!node->initialized) { EState *estate = node->ps.state; GatherMerge *gm = (GatherMerge *) node->ps.plan; /* * Sometimes we might have to run without parallelism; but if parallel * mode is active then we can try to fire up some workers. */ if (gm->num_workers > 0 && IsInParallelMode()) { ParallelContext *pcxt; /* Initialize data structures for workers. */ if (!node->pei) node->pei = ExecInitParallelPlan(node->ps.lefttree, estate, gm->num_workers); /* Try to launch workers. */ pcxt = node->pei->pcxt; LaunchParallelWorkers(pcxt); node->nworkers_launched = pcxt->nworkers_launched; /* Set up tuple queue readers to read the results. */ if (pcxt->nworkers_launched > 0) { node->nreaders = 0; node->reader = palloc(pcxt->nworkers_launched * sizeof(TupleQueueReader *)); Assert(gm->numCols); for (i = 0; i < pcxt->nworkers_launched; ++i) { shm_mq_set_handle(node->pei->tqueue[i], pcxt->worker[i].bgwhandle); node->reader[node->nreaders++] = CreateTupleQueueReader(node->pei->tqueue[i], node->tupDesc); } } else { /* No workers? Then never mind. */ ExecShutdownGatherMergeWorkers(node); } } /* always allow leader to participate */ node->need_to_scan_locally = true; node->initialized = true; } /* * Reset per-tuple memory context to free any expression evaluation * storage allocated in the previous tuple cycle. */ econtext = node->ps.ps_ExprContext; ResetExprContext(econtext); /* * Get next tuple, either from one of our workers, or by running the plan * ourselves. */ slot = gather_merge_getnext(node); if (TupIsNull(slot)) return NULL; /* * form the result tuple using ExecProject(), and return it --- unless the * projection produces an empty set, in which case we must loop back * around for another tuple */ econtext->ecxt_outertuple = slot; return ExecProject(node->ps.ps_ProjInfo); }