/* ---------------------------------------------------------------- * ExecReScanCteScan * * Rescans the relation. * ---------------------------------------------------------------- */ void ExecReScanCteScan(CteScanState *node) { Tuplestorestate *tuplestorestate = node->leader->cte_table; ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ExecScanReScan(&node->ss); if (node->leader == node) { /* * The leader is responsible for clearing the tuplestore if a new scan * of the underlying CTE is required. */ if (node->cteplanstate->chgParam != NULL) { tuplestore_clear(tuplestorestate); node->eof_cte = false; } else { tuplestore_select_read_pointer(tuplestorestate, node->readptr); tuplestore_rescan(tuplestorestate); } } else { /* Not leader, so just rewind my own pointer */ tuplestore_select_read_pointer(tuplestorestate, node->readptr); tuplestore_rescan(tuplestorestate); } }
/* ---------------------------------------------------------------- * ExecReScanCteScan * * Rescans the relation. * ---------------------------------------------------------------- */ void ExecReScanCteScan(CteScanState *node) { Tuplestorestate *tuplestorestate = node->leader->cte_table; ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ExecScanReScan(&node->ss); /* * Clear the tuplestore if a new scan of the underlying CTE is required. * This implicitly resets all the tuplestore's read pointers. Note that * multiple CTE nodes might redundantly clear the tuplestore; that's OK, * and not unduly expensive. We'll stop taking this path as soon as * somebody has attempted to read something from the underlying CTE * (thereby causing its chgParam to be cleared). */ if (node->leader->cteplanstate->chgParam != NULL) { tuplestore_clear(tuplestorestate); node->leader->eof_cte = false; } else { /* * Else, just rewind my own pointer. Either the underlying CTE * doesn't need a rescan (and we can re-read what's in the tuplestore * now), or somebody else already took care of it. */ tuplestore_select_read_pointer(tuplestorestate, node->readptr); tuplestore_rescan(tuplestorestate); } }
/* ---------------------------------------------------------------- * ExecReScanFunctionScan * * Rescans the relation. * ---------------------------------------------------------------- */ void ExecReScanFunctionScan(FunctionScanState *node) { ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ExecScanReScan(&node->ss); /* * If we haven't materialized yet, just return. */ if (!node->tuplestorestate) return; /* * Here we have a choice whether to drop the tuplestore (and recompute the * function outputs) or just rescan it. We must recompute if the * expression contains parameters, else we rescan. XXX maybe we should * recompute if the function is volatile? */ if (node->ss.ps.chgParam != NULL) { tuplestore_end(node->tuplestorestate); node->tuplestorestate = NULL; } else tuplestore_rescan(node->tuplestorestate); }
/* ---------------------------------------------------------------- * ExecMaterialReScan * * Rescans the materialized relation. * ---------------------------------------------------------------- */ void ExecMaterialReScan(MaterialState *node, ExprContext *exprCtxt) { /* * If we haven't materialized yet, just return. If outerplan' chgParam is * not NULL then it will be re-scanned by ExecProcNode, else - no reason * to re-scan it at all. */ if (!node->tuplestorestate) return; ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); /* * If subnode is to be rescanned then we forget previous stored results; * we have to re-read the subplan and re-store. * * Otherwise we can just rewind and rescan the stored output. The state of * the subnode does not change. */ if (((PlanState *) node)->lefttree->chgParam != NULL) { tuplestore_end((Tuplestorestate *) node->tuplestorestate); node->tuplestorestate = NULL; node->eof_underlying = false; } else tuplestore_rescan((Tuplestorestate *) node->tuplestorestate); }
/* ---------------------------------------------------------------- * ExecFunctionReScan * * Rescans the relation. * ---------------------------------------------------------------- */ void ExecFunctionReScan(FunctionScanState *node, ExprContext *exprCtxt) { ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); /* * If we haven't materialized yet, just return. */ if (!node->tuplestorestate) return; /* * Here we have a choice whether to drop the tuplestore (and recompute the * function outputs) or just rescan it. This should depend on whether the * function expression contains parameters and/or is marked volatile. * FIXME soon. */ if (node->ss.ps.chgParam != NULL) { tuplestore_end(node->tuplestorestate); node->tuplestorestate = NULL; } else tuplestore_rescan(node->tuplestorestate); }
/* ---------------------------------------------------------------- * ExecWorkTableScanReScan * * Rescans the relation. * ---------------------------------------------------------------- */ void ExecWorkTableScanReScan(WorkTableScanState *node, ExprContext *exprCtxt) { ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); /* node->ss.ps.ps_TupFromTlist = false; */ /* No need (or way) to rescan if ExecWorkTableScan not called yet */ if (node->rustate) tuplestore_rescan(node->rustate->working_table); }
/* ---------------------------------------------------------------- * ExecReScanWorkTableScan * * Rescans the relation. * ---------------------------------------------------------------- */ void ExecReScanWorkTableScan(WorkTableScanState *node) { ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ExecScanReScan(&node->ss); /* No need (or way) to rescan if ExecWorkTableScan not called yet */ if (node->rustate) tuplestore_rescan(node->rustate->working_table); }
/* ---------------------------------------------------------------- * ExecReScanMaterial * * Rescans the materialized relation. * ---------------------------------------------------------------- */ void ExecReScanMaterial(MaterialState *node) { PlanState *outerPlan = outerPlanState(node); ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); if (node->eflags != 0) { /* * If we haven't materialized yet, just return. If outerplan's * chgParam is not NULL then it will be re-scanned by ExecProcNode, * else no reason to re-scan it at all. */ if (!node->tuplestorestate) return; /* * If subnode is to be rescanned then we forget previous stored * results; we have to re-read the subplan and re-store. Also, if we * told tuplestore it needn't support rescan, we lose and must * re-read. (This last should not happen in common cases; else our * caller lied by not passing EXEC_FLAG_REWIND to us.) * * Otherwise we can just rewind and rescan the stored output. The * state of the subnode does not change. */ if (outerPlan->chgParam != NULL || (node->eflags & EXEC_FLAG_REWIND) == 0) { tuplestore_end(node->tuplestorestate); node->tuplestorestate = NULL; if (outerPlan->chgParam == NULL) ExecReScan(outerPlan); node->eof_underlying = false; } else tuplestore_rescan(node->tuplestorestate); } else { /* In this case we are just passing on the subquery's output */ /* * if chgParam of subnode is not null then plan will be re-scanned by * first ExecProcNode. */ if (outerPlan->chgParam == NULL) ExecReScan(outerPlan); node->eof_underlying = false; } }
/* ---------------------------------------------------------------- * ExecMaterialReScan * * Rescans the materialized relation. * ---------------------------------------------------------------- */ void ExecMaterialReScan(MaterialState *node, ExprContext *exprCtxt) { ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); if (node->randomAccess) { /* * If we haven't materialized yet, just return. If outerplan' chgParam * is not NULL then it will be re-scanned by ExecProcNode, else - no * reason to re-scan it at all. */ if (!node->tuplestorestate) return; /* * If subnode is to be rescanned then we forget previous stored * results; we have to re-read the subplan and re-store. * * Otherwise we can just rewind and rescan the stored output. The * state of the subnode does not change. */ if (((PlanState *) node)->lefttree->chgParam != NULL) { tuplestore_end((Tuplestorestate *) node->tuplestorestate); node->tuplestorestate = NULL; node->eof_underlying = false; } else tuplestore_rescan((Tuplestorestate *) node->tuplestorestate); } else { /* In this case we are just passing on the subquery's output */ /* * if chgParam of subnode is not null then plan will be re-scanned by * first ExecProcNode. */ if (((PlanState *) node)->lefttree->chgParam == NULL) ExecReScan(((PlanState *) node)->lefttree, exprCtxt); node->eof_underlying = false; } }
extern TuplestoreScanState * ExecInitTuplestoreScan(TuplestoreScan *node, EState *estate, int eflags) { TuplestoreScanState *tss = makeNode(TuplestoreScanState); tss->ss.ps.plan = (Plan *) node; tss->ss.ps.state = estate; tss->ss.ps.ps_ExprContext = CreateExprContext(estate); ExecInitResultTupleSlot(estate, &tss->ss.ps); ExecInitScanTupleSlot(estate, &tss->ss); ExecSetSlotDescriptor(tss->ss.ss_ScanTupleSlot, node->desc); ExecSetSlotDescriptor(tss->ss.ps.ps_ResultTupleSlot, node->desc); tss->ss.ps.targetlist = node->scan.plan.targetlist; tuplestore_rescan(node->store); return tss; }
/* ---------------------------------------------------------------- * ExecInitCteScan * ---------------------------------------------------------------- */ CteScanState * ExecInitCteScan(CteScan *node, EState *estate, int eflags) { CteScanState *scanstate; ParamExecData *prmdata; /* check for unsupported flags */ Assert(!(eflags & EXEC_FLAG_MARK)); /* * For the moment we have to force the tuplestore to allow REWIND, because * we might be asked to rescan the CTE even though upper levels didn't * tell us to be prepared to do it efficiently. Annoying, since this * prevents truncation of the tuplestore. XXX FIXME * * Note: if we are in an EPQ recheck plan tree, it's likely that no access * to the tuplestore is needed at all, making this even more annoying. * It's not worth improving that as long as all the read pointers would * have REWIND anyway, but if we ever improve this logic then that aspect * should be considered too. */ eflags |= EXEC_FLAG_REWIND; /* * CteScan should not have any children. */ Assert(outerPlan(node) == NULL); Assert(innerPlan(node) == NULL); /* * create new CteScanState for node */ scanstate = makeNode(CteScanState); scanstate->ss.ps.plan = (Plan *) node; scanstate->ss.ps.state = estate; scanstate->eflags = eflags; scanstate->cte_table = NULL; scanstate->eof_cte = false; /* * Find the already-initialized plan for the CTE query. */ scanstate->cteplanstate = (PlanState *) list_nth(estate->es_subplanstates, node->ctePlanId - 1); /* * The Param slot associated with the CTE query is used to hold a pointer * to the CteState of the first CteScan node that initializes for this * CTE. This node will be the one that holds the shared state for all the * CTEs, particularly the shared tuplestore. */ prmdata = &(estate->es_param_exec_vals[node->cteParam]); Assert(prmdata->execPlan == NULL); Assert(!prmdata->isnull); scanstate->leader = (CteScanState *) DatumGetPointer(prmdata->value); if (scanstate->leader == NULL) { /* I am the leader */ prmdata->value = PointerGetDatum(scanstate); scanstate->leader = scanstate; scanstate->cte_table = tuplestore_begin_heap(true, false, work_mem); tuplestore_set_eflags(scanstate->cte_table, scanstate->eflags); scanstate->readptr = 0; } else { /* Not the leader */ Assert(IsA(scanstate->leader, CteScanState)); /* Create my own read pointer, and ensure it is at start */ scanstate->readptr = tuplestore_alloc_read_pointer(scanstate->leader->cte_table, scanstate->eflags); tuplestore_select_read_pointer(scanstate->leader->cte_table, scanstate->readptr); tuplestore_rescan(scanstate->leader->cte_table); } /* * Miscellaneous initialization * * create expression context for node */ ExecAssignExprContext(estate, &scanstate->ss.ps); /* * initialize child expressions */ scanstate->ss.ps.targetlist = (List *) ExecInitExpr((Expr *) node->scan.plan.targetlist, (PlanState *) scanstate); scanstate->ss.ps.qual = (List *) ExecInitExpr((Expr *) node->scan.plan.qual, (PlanState *) scanstate); /* * tuple table initialization */ ExecInitResultTupleSlot(estate, &scanstate->ss.ps); ExecInitScanTupleSlot(estate, &scanstate->ss); /* * The scan tuple type (ie, the rowtype we expect to find in the work * table) is the same as the result rowtype of the CTE query. */ ExecAssignScanType(&scanstate->ss, ExecGetResultType(scanstate->cteplanstate)); /* * Initialize result tuple type and projection info. */ ExecAssignResultTypeFromTL(&scanstate->ss.ps); ExecAssignScanProjectionInfo(&scanstate->ss); return scanstate; }
/* * PersistHoldablePortal * * Prepare the specified Portal for access outside of the current * transaction. When this function returns, all future accesses to the * portal must be done via the Tuplestore (not by invoking the * executor). */ void PersistHoldablePortal(Portal portal) { QueryDesc *queryDesc = PortalGetQueryDesc(portal); Portal saveActivePortal; ResourceOwner saveResourceOwner; MemoryContext savePortalContext; MemoryContext oldcxt; /* * If we're preserving a holdable portal, we had better be inside the * transaction that originally created it. */ Assert(portal->createSubid != InvalidSubTransactionId); Assert(queryDesc != NULL); /* * Caller must have created the tuplestore already. */ Assert(portal->holdContext != NULL); Assert(portal->holdStore != NULL); /* * Before closing down the executor, we must copy the tupdesc into * long-term memory, since it was created in executor memory. */ oldcxt = MemoryContextSwitchTo(portal->holdContext); portal->tupDesc = CreateTupleDescCopy(portal->tupDesc); MemoryContextSwitchTo(oldcxt); /* * Check for improper portal use, and mark portal active. */ if (portal->status != PORTAL_READY) ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("portal \"%s\" cannot be run", portal->name))); portal->status = PORTAL_ACTIVE; /* * Set up global portal context pointers. */ saveActivePortal = ActivePortal; saveResourceOwner = CurrentResourceOwner; savePortalContext = PortalContext; PG_TRY(); { ActivePortal = portal; CurrentResourceOwner = portal->resowner; PortalContext = PortalGetHeapMemory(portal); MemoryContextSwitchTo(PortalContext); PushActiveSnapshot(queryDesc->snapshot); /* * Rewind the executor: we need to store the entire result set in the * tuplestore, so that subsequent backward FETCHs can be processed. */ ExecutorRewind(queryDesc); /* * Change the destination to output to the tuplestore. Note we tell * the tuplestore receiver to detoast all data passed through it. */ queryDesc->dest = CreateDestReceiver(DestTuplestore); SetTuplestoreDestReceiverParams(queryDesc->dest, portal->holdStore, portal->holdContext, true); /* Fetch the result set into the tuplestore */ ExecutorRun(queryDesc, ForwardScanDirection, 0L); (*queryDesc->dest->rDestroy) (queryDesc->dest); queryDesc->dest = NULL; /* * Now shut down the inner executor. */ portal->queryDesc = NULL; /* prevent double shutdown */ /* we do not need AfterTriggerEndQuery() here */ ExecutorEnd(queryDesc); FreeQueryDesc(queryDesc); /* * Set the position in the result set: ideally, this could be * implemented by just skipping straight to the tuple # that we need * to be at, but the tuplestore API doesn't support that. So we start * at the beginning of the tuplestore and iterate through it until we * reach where we need to be. FIXME someday? (Fortunately, the * typical case is that we're supposed to be at or near the start of * the result set, so this isn't as bad as it sounds.) */ MemoryContextSwitchTo(portal->holdContext); if (portal->atEnd) { /* we can handle this case even if posOverflow */ while (tuplestore_advance(portal->holdStore, true)) /* continue */ ; } else { long store_pos; if (portal->posOverflow) /* oops, cannot trust portalPos */ ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("could not reposition held cursor"))); tuplestore_rescan(portal->holdStore); for (store_pos = 0; store_pos < portal->portalPos; store_pos++) { if (!tuplestore_advance(portal->holdStore, true)) elog(ERROR, "unexpected end of tuple stream"); } } } PG_CATCH(); { /* Uncaught error while executing portal: mark it dead */ portal->status = PORTAL_FAILED; /* Restore global vars and propagate error */ ActivePortal = saveActivePortal; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; PG_RE_THROW(); } PG_END_TRY(); MemoryContextSwitchTo(oldcxt); /* Mark portal not active */ portal->status = PORTAL_READY; ActivePortal = saveActivePortal; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; PopActiveSnapshot(); /* * We can now release any subsidiary memory of the portal's heap context; * we'll never use it again. The executor already dropped its context, * but this will clean up anything that glommed onto the portal's heap via * PortalContext. */ MemoryContextDeleteChildren(PortalGetHeapMemory(portal)); }
/* * PersistHoldablePortal * * Prepare the specified Portal for access outside of the current * transaction. When this function returns, all future accesses to the * portal must be done via the Tuplestore (not by invoking the * executor). */ void PersistHoldablePortal(Portal portal) { QueryDesc *queryDesc = PortalGetQueryDesc(portal); Portal saveActivePortal; ResourceOwner saveResourceOwner; MemoryContext savePortalContext; MemoryContext oldcxt; /* * If we're preserving a holdable portal, we had better be inside the * transaction that originally created it. */ Assert(portal->createSubid != InvalidSubTransactionId); Assert(queryDesc != NULL); /* * Caller must have created the tuplestore already ... but not a snapshot. */ Assert(portal->holdContext != NULL); Assert(portal->holdStore != NULL); Assert(portal->holdSnapshot == NULL); /* * Before closing down the executor, we must copy the tupdesc into * long-term memory, since it was created in executor memory. */ oldcxt = MemoryContextSwitchTo(portal->holdContext); portal->tupDesc = CreateTupleDescCopy(portal->tupDesc); MemoryContextSwitchTo(oldcxt); /* * Check for improper portal use, and mark portal active. */ MarkPortalActive(portal); /* * Set up global portal context pointers. */ saveActivePortal = ActivePortal; saveResourceOwner = CurrentResourceOwner; savePortalContext = PortalContext; PG_TRY(); { ActivePortal = portal; if (portal->resowner) CurrentResourceOwner = portal->resowner; PortalContext = PortalGetHeapMemory(portal); MemoryContextSwitchTo(PortalContext); PushActiveSnapshot(queryDesc->snapshot); /* * Rewind the executor: we need to store the entire result set in the * tuplestore, so that subsequent backward FETCHs can be processed. */ ExecutorRewind(queryDesc); /* * Change the destination to output to the tuplestore. Note we tell * the tuplestore receiver to detoast all data passed through it; this * makes it safe to not keep a snapshot associated with the data. */ queryDesc->dest = CreateDestReceiver(DestTuplestore); SetTuplestoreDestReceiverParams(queryDesc->dest, portal->holdStore, portal->holdContext, true); /* Fetch the result set into the tuplestore */ ExecutorRun(queryDesc, ForwardScanDirection, 0L); (*queryDesc->dest->rDestroy) (queryDesc->dest); queryDesc->dest = NULL; /* * Now shut down the inner executor. */ portal->queryDesc = NULL; /* prevent double shutdown */ ExecutorFinish(queryDesc); ExecutorEnd(queryDesc); FreeQueryDesc(queryDesc); /* * Set the position in the result set. */ MemoryContextSwitchTo(portal->holdContext); if (portal->atEnd) { /* * Just force the tuplestore forward to its end. The size of the * skip request here is arbitrary. */ while (tuplestore_skiptuples(portal->holdStore, 1000000, true)) /* continue */ ; } else { tuplestore_rescan(portal->holdStore); if (!tuplestore_skiptuples(portal->holdStore, portal->portalPos, true)) elog(ERROR, "unexpected end of tuple stream"); } } PG_CATCH(); { /* Uncaught error while executing portal: mark it dead */ MarkPortalFailed(portal); /* Restore global vars and propagate error */ ActivePortal = saveActivePortal; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; PG_RE_THROW(); } PG_END_TRY(); MemoryContextSwitchTo(oldcxt); /* Mark portal not active */ portal->status = PORTAL_READY; ActivePortal = saveActivePortal; CurrentResourceOwner = saveResourceOwner; PortalContext = savePortalContext; PopActiveSnapshot(); /* * We can now release any subsidiary memory of the portal's heap context; * we'll never use it again. The executor already dropped its context, * but this will clean up anything that glommed onto the portal's heap via * PortalContext. */ MemoryContextDeleteChildren(PortalGetHeapMemory(portal)); }
/* * PersistHoldablePortal * * Prepare the specified Portal for access outside of the current * transaction. When this function returns, all future accesses to the * portal must be done via the Tuplestore (not by invoking the * executor). */ void PersistHoldablePortal(Portal portal) { QueryDesc *queryDesc = PortalGetQueryDesc(portal); MemoryContext savePortalContext; MemoryContext saveQueryContext; MemoryContext oldcxt; /* * If we're preserving a holdable portal, we had better be inside the * transaction that originally created it. */ Assert(portal->createXact == GetCurrentTransactionId()); Assert(queryDesc != NULL); Assert(portal->portalReady); Assert(!portal->portalDone); /* * Caller must have created the tuplestore already. */ Assert(portal->holdContext != NULL); Assert(portal->holdStore != NULL); /* * Before closing down the executor, we must copy the tupdesc into * long-term memory, since it was created in executor memory. */ oldcxt = MemoryContextSwitchTo(portal->holdContext); portal->tupDesc = CreateTupleDescCopy(portal->tupDesc); MemoryContextSwitchTo(oldcxt); /* * Check for improper portal use, and mark portal active. */ if (portal->portalActive) ereport(ERROR, (errcode(ERRCODE_OBJECT_IN_USE), errmsg("portal \"%s\" already active", portal->name))); portal->portalActive = true; /* * Set global portal context pointers. */ savePortalContext = PortalContext; PortalContext = PortalGetHeapMemory(portal); saveQueryContext = QueryContext; QueryContext = portal->queryContext; MemoryContextSwitchTo(PortalContext); /* * Rewind the executor: we need to store the entire result set in the * tuplestore, so that subsequent backward FETCHs can be processed. */ ExecutorRewind(queryDesc); /* Change the destination to output to the tuplestore */ queryDesc->dest = CreateDestReceiver(Tuplestore, portal); /* Fetch the result set into the tuplestore */ ExecutorRun(queryDesc, ForwardScanDirection, 0L); (*queryDesc->dest->rDestroy) (queryDesc->dest); queryDesc->dest = NULL; /* * Now shut down the inner executor. */ portal->queryDesc = NULL; /* prevent double shutdown */ ExecutorEnd(queryDesc); /* Mark portal not active */ portal->portalActive = false; PortalContext = savePortalContext; QueryContext = saveQueryContext; /* * Reset the position in the result set: ideally, this could be * implemented by just skipping straight to the tuple # that we need * to be at, but the tuplestore API doesn't support that. So we start * at the beginning of the tuplestore and iterate through it until we * reach where we need to be. FIXME someday? */ MemoryContextSwitchTo(portal->holdContext); if (!portal->atEnd) { long store_pos; if (portal->posOverflow) /* oops, cannot trust portalPos */ ereport(ERROR, (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), errmsg("could not reposition held cursor"))); tuplestore_rescan(portal->holdStore); for (store_pos = 0; store_pos < portal->portalPos; store_pos++) { HeapTuple tup; bool should_free; tup = tuplestore_gettuple(portal->holdStore, true, &should_free); if (tup == NULL) elog(ERROR, "unexpected end of tuple stream"); if (should_free) pfree(tup); } } MemoryContextSwitchTo(oldcxt); /* * We can now release any subsidiary memory of the portal's heap * context; we'll never use it again. The executor already dropped * its context, but this will clean up anything that glommed onto the * portal's heap via PortalContext. */ MemoryContextDeleteChildren(PortalGetHeapMemory(portal)); }