/* ---------------------------------------------------------------- * 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); }
/* ---------------------------------------------------------------- * 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; } }
/* ---------------------------------------------------------------- * ExecEndRecursiveUnionScan * * frees any storage allocated through C routines. * ---------------------------------------------------------------- */ void ExecEndRecursiveUnion(RecursiveUnionState *node) { /* Release tuplestores */ tuplestore_end(node->working_table); tuplestore_end(node->intermediate_table); /* free subsidiary stuff including hashtable */ if (node->tempContext) MemoryContextDelete(node->tempContext); if (node->tableContext) MemoryContextDelete(node->tableContext); /* * clean out the upper tuple table */ ExecClearTuple(node->ps.ps_ResultTupleSlot); /* * close down subplans */ ExecEndNode(outerPlanState(node)); ExecEndNode(innerPlanState(node)); }
/* * worker_copy_shard_placement implements a internal UDF to copy a table's data from * a healthy placement into a receiving table on an unhealthy placement. This * function returns a boolean reflecting success or failure. */ Datum worker_copy_shard_placement(PG_FUNCTION_ARGS) { text *shardRelationNameText = PG_GETARG_TEXT_P(0); text *nodeNameText = PG_GETARG_TEXT_P(1); int32 nodePort = PG_GETARG_INT32(2); char *shardRelationName = text_to_cstring(shardRelationNameText); char *nodeName = text_to_cstring(nodeNameText); bool fetchSuccessful = false; Oid shardRelationId = ResolveRelationId(shardRelationNameText); Relation shardTable = heap_open(shardRelationId, RowExclusiveLock); TupleDesc tupleDescriptor = RelationGetDescr(shardTable); Tuplestorestate *tupleStore = tuplestore_begin_heap(false, false, work_mem); StringInfo selectAllQuery = NULL; ShardPlacement *placement = NULL; Task *task = NULL; selectAllQuery = makeStringInfo(); appendStringInfo(selectAllQuery, SELECT_ALL_QUERY, quote_identifier(shardRelationName)); placement = (ShardPlacement *) palloc0(sizeof(ShardPlacement)); placement->nodeName = nodeName; placement->nodePort = nodePort; task = (Task *) palloc0(sizeof(Task)); task->queryString = selectAllQuery; task->taskPlacementList = list_make1(placement); fetchSuccessful = ExecuteTaskAndStoreResults(task, tupleDescriptor, tupleStore); if (!fetchSuccessful) { ereport(ERROR, (errmsg("could not store shard rows from healthy placement"), errhint("Consult recent messages in the server logs for " "details."))); } CopyDataFromTupleStoreToRelation(tupleStore, shardTable); tuplestore_end(tupleStore); heap_close(shardTable, RowExclusiveLock); PG_RETURN_VOID(); }
/* * PortalDrop * Destroy the portal. * * isError: if true, we are destroying portals at the end of a failed * transaction. (This causes PortalCleanup to skip unneeded steps.) */ void PortalDrop(Portal portal, bool isError) { AssertArg(PortalIsValid(portal)); /* Not sure if this case can validly happen or not... */ if (portal->portalActive) elog(ERROR, "cannot drop active portal"); /* * Remove portal from hash table. Because we do this first, we will * not come back to try to remove the portal again if there's any * error in the subsequent steps. Better to leak a little memory than * to get into an infinite error-recovery loop. */ PortalHashTableDelete(portal); /* let portalcmds.c clean up the state it knows about */ if (PointerIsValid(portal->cleanup)) (*portal->cleanup) (portal, isError); /* * Delete tuplestore if present. We should do this even under error * conditions; since the tuplestore would have been using cross- * transaction storage, its temp files need to be explicitly deleted. */ if (portal->holdStore) { MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(portal->holdContext); tuplestore_end(portal->holdStore); MemoryContextSwitchTo(oldcontext); portal->holdStore = NULL; } /* delete tuplestore storage, if any */ if (portal->holdContext) MemoryContextDelete(portal->holdContext); /* release subsidiary storage */ MemoryContextDelete(PortalGetHeapMemory(portal)); /* release portal struct (it's in PortalMemory) */ pfree(portal); }
/* ---------------------------------------------------------------- * 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; } }
/* ---------------------------------------------------------------- * ExecEndMaterial * ---------------------------------------------------------------- */ void ExecEndMaterial(MaterialState *node) { /* * clean out the tuple table */ ExecClearTuple(node->ss.ss_ScanTupleSlot); /* * Release tuplestore resources */ if (node->tuplestorestate != NULL) tuplestore_end((Tuplestorestate *) node->tuplestorestate); node->tuplestorestate = NULL; /* * shut down the subplan */ ExecEndNode(outerPlanState(node)); }
/* ---------------------------------------------------------------- * ExecEndCteScan * * frees any storage allocated through C routines. * ---------------------------------------------------------------- */ void ExecEndCteScan(CteScanState *node) { /* * Free exprcontext */ ExecFreeExprContext(&node->ss.ps); /* * clean out the tuple table */ ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ExecClearTuple(node->ss.ss_ScanTupleSlot); /* * If I am the leader, free the tuplestore. */ if (node->leader == node) tuplestore_end(node->cte_table); }
/* * callback function in case a SetExprState needs to be shut down before it * has been run to completion */ static void ShutdownSetExpr(Datum arg) { SetExprState *sexpr = castNode(SetExprState, DatumGetPointer(arg)); /* If we have a slot, make sure it's let go of any tuplestore pointer */ if (sexpr->funcResultSlot) ExecClearTuple(sexpr->funcResultSlot); /* Release any open tuplestore */ if (sexpr->funcResultStore) tuplestore_end(sexpr->funcResultStore); sexpr->funcResultStore = NULL; /* Clear any active set-argument state */ sexpr->setArgsValid = false; /* execUtils will deregister the callback... */ sexpr->shutdown_reg = false; }
/* ---------------------------------------------------------------- * ExecEndFunctionScan * * frees any storage allocated through C routines. * ---------------------------------------------------------------- */ void ExecEndFunctionScan(FunctionScanState *node) { /* * Free the exprcontext */ ExecFreeExprContext(&node->ss.ps); /* * clean out the tuple table */ ExecClearTuple(node->ss.ps.ps_ResultTupleSlot); ExecClearTuple(node->ss.ss_ScanTupleSlot); /* * Release tuplestore resources */ if (node->tuplestorestate != NULL) tuplestore_end(node->tuplestorestate); node->tuplestorestate = NULL; }
/* * PortalDrop * Destroy the portal. */ void PortalDrop(Portal portal, bool isTopCommit) { AssertArg(PortalIsValid(portal)); /* Not sure if this case can validly happen or not... */ if (portal->status == PORTAL_ACTIVE) elog(ERROR, "cannot drop active portal"); /* * Remove portal from hash table. Because we do this first, we will not * come back to try to remove the portal again if there's any error in the * subsequent steps. Better to leak a little memory than to get into an * infinite error-recovery loop. */ PortalHashTableDelete(portal); /* let portalcmds.c clean up the state it knows about */ if (PointerIsValid(portal->cleanup)) (*portal->cleanup) (portal); /* * Release any resources still attached to the portal. There are several * cases being covered here: * * Top transaction commit (indicated by isTopCommit): normally we should * do nothing here and let the regular end-of-transaction resource * releasing mechanism handle these resources too. However, if we have a * FAILED portal (eg, a cursor that got an error), we'd better clean up * its resources to avoid resource-leakage warning messages. * * Sub transaction commit: never comes here at all, since we don't kill * any portals in AtSubCommit_Portals(). * * Main or sub transaction abort: we will do nothing here because * portal->resowner was already set NULL; the resources were already * cleaned up in transaction abort. * * Ordinary portal drop: must release resources. However, if the portal * is not FAILED then we do not release its locks. The locks become the * responsibility of the transaction's ResourceOwner (since it is the * parent of the portal's owner) and will be released when the transaction * eventually ends. */ if (portal->resowner && (!isTopCommit || portal->status == PORTAL_FAILED)) { bool isCommit = (portal->status != PORTAL_FAILED); ResourceOwnerRelease(portal->resowner, RESOURCE_RELEASE_BEFORE_LOCKS, isCommit, false); ResourceOwnerRelease(portal->resowner, RESOURCE_RELEASE_LOCKS, isCommit, false); ResourceOwnerRelease(portal->resowner, RESOURCE_RELEASE_AFTER_LOCKS, isCommit, false); ResourceOwnerDelete(portal->resowner); } portal->resowner = NULL; /* * Delete tuplestore if present. We should do this even under error * conditions; since the tuplestore would have been using cross- * transaction storage, its temp files need to be explicitly deleted. */ if (portal->holdStore) { MemoryContext oldcontext; oldcontext = MemoryContextSwitchTo(portal->holdContext); tuplestore_end(portal->holdStore); MemoryContextSwitchTo(oldcontext); portal->holdStore = NULL; } /* delete tuplestore storage, if any */ if (portal->holdContext) MemoryContextDelete(portal->holdContext); /* release subsidiary storage */ MemoryContextDelete(PortalGetHeapMemory(portal)); /* release portal struct (it's in PortalMemory) */ pfree(portal); }
/* * ExecMakeFunctionResultSet * * Evaluate the arguments to a set-returning function and then call the * function itself. The argument expressions may not contain set-returning * functions (the planner is supposed to have separated evaluation for those). * * This should be called in a short-lived (per-tuple) context, argContext * needs to live until all rows have been returned (i.e. *isDone set to * ExprEndResult or ExprSingleResult). * * This is used by nodeProjectSet.c. */ Datum ExecMakeFunctionResultSet(SetExprState *fcache, ExprContext *econtext, MemoryContext argContext, bool *isNull, ExprDoneCond *isDone) { List *arguments; Datum result; FunctionCallInfo fcinfo; PgStat_FunctionCallUsage fcusage; ReturnSetInfo rsinfo; bool callit; int i; restart: /* Guard against stack overflow due to overly complex expressions */ check_stack_depth(); /* * If a previous call of the function returned a set result in the form of * a tuplestore, continue reading rows from the tuplestore until it's * empty. */ if (fcache->funcResultStore) { TupleTableSlot *slot = fcache->funcResultSlot; MemoryContext oldContext; bool foundTup; /* * Have to make sure tuple in slot lives long enough, otherwise * clearing the slot could end up trying to free something already * freed. */ oldContext = MemoryContextSwitchTo(slot->tts_mcxt); foundTup = tuplestore_gettupleslot(fcache->funcResultStore, true, false, fcache->funcResultSlot); MemoryContextSwitchTo(oldContext); if (foundTup) { *isDone = ExprMultipleResult; if (fcache->funcReturnsTuple) { /* We must return the whole tuple as a Datum. */ *isNull = false; return ExecFetchSlotTupleDatum(fcache->funcResultSlot); } else { /* Extract the first column and return it as a scalar. */ return slot_getattr(fcache->funcResultSlot, 1, isNull); } } /* Exhausted the tuplestore, so clean up */ tuplestore_end(fcache->funcResultStore); fcache->funcResultStore = NULL; *isDone = ExprEndResult; *isNull = true; return (Datum) 0; } /* * arguments is a list of expressions to evaluate before passing to the * function manager. We skip the evaluation if it was already done in the * previous call (ie, we are continuing the evaluation of a set-valued * function). Otherwise, collect the current argument values into fcinfo. * * The arguments have to live in a context that lives at least until all * rows from this SRF have been returned, otherwise ValuePerCall SRFs * would reference freed memory after the first returned row. */ fcinfo = &fcache->fcinfo_data; arguments = fcache->args; if (!fcache->setArgsValid) { MemoryContext oldContext = MemoryContextSwitchTo(argContext); ExecEvalFuncArgs(fcinfo, arguments, econtext); MemoryContextSwitchTo(oldContext); } else { /* Reset flag (we may set it again below) */ fcache->setArgsValid = false; } /* * Now call the function, passing the evaluated parameter values. */ /* Prepare a resultinfo node for communication. */ fcinfo->resultinfo = (Node *) &rsinfo; rsinfo.type = T_ReturnSetInfo; rsinfo.econtext = econtext; rsinfo.expectedDesc = fcache->funcResultDesc; rsinfo.allowedModes = (int) (SFRM_ValuePerCall | SFRM_Materialize); /* note we do not set SFRM_Materialize_Random or _Preferred */ rsinfo.returnMode = SFRM_ValuePerCall; /* isDone is filled below */ rsinfo.setResult = NULL; rsinfo.setDesc = NULL; /* * If function is strict, and there are any NULL arguments, skip calling * the function. */ callit = true; if (fcache->func.fn_strict) { for (i = 0; i < fcinfo->nargs; i++) { if (fcinfo->argnull[i]) { callit = false; break; } } } if (callit) { pgstat_init_function_usage(fcinfo, &fcusage); fcinfo->isnull = false; rsinfo.isDone = ExprSingleResult; result = FunctionCallInvoke(fcinfo); *isNull = fcinfo->isnull; *isDone = rsinfo.isDone; pgstat_end_function_usage(&fcusage, rsinfo.isDone != ExprMultipleResult); } else { /* for a strict SRF, result for NULL is an empty set */ result = (Datum) 0; *isNull = true; *isDone = ExprEndResult; } /* Which protocol does function want to use? */ if (rsinfo.returnMode == SFRM_ValuePerCall) { if (*isDone != ExprEndResult) { /* * Save the current argument values to re-use on the next call. */ if (*isDone == ExprMultipleResult) { fcache->setArgsValid = true; /* Register cleanup callback if we didn't already */ if (!fcache->shutdown_reg) { RegisterExprContextCallback(econtext, ShutdownSetExpr, PointerGetDatum(fcache)); fcache->shutdown_reg = true; } } } } else if (rsinfo.returnMode == SFRM_Materialize) { /* check we're on the same page as the function author */ if (rsinfo.isDone != ExprSingleResult) ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), errmsg("table-function protocol for materialize mode was not followed"))); if (rsinfo.setResult != NULL) { /* prepare to return values from the tuplestore */ ExecPrepareTuplestoreResult(fcache, econtext, rsinfo.setResult, rsinfo.setDesc); /* loop back to top to start returning from tuplestore */ goto restart; } /* if setResult was left null, treat it as empty set */ *isDone = ExprEndResult; *isNull = true; result = (Datum) 0; } else ereport(ERROR, (errcode(ERRCODE_E_R_I_E_SRF_PROTOCOL_VIOLATED), errmsg("unrecognized table-function returnMode: %d", (int) rsinfo.returnMode))); return result; }
/* ---------------------------------------------------------------- * ExecRecursiveUnion(node) * * Scans the recursive query sequentially and returns the next * qualifying tuple. * * 1. evaluate non recursive term and assign the result to RT * * 2. execute recursive terms * * 2.1 WT := RT * 2.2 while WT is not empty repeat 2.3 to 2.6. if WT is empty returns RT * 2.3 replace the name of recursive term with WT * 2.4 evaluate the recursive term and store into WT * 2.5 append WT to RT * 2.6 go back to 2.2 * ---------------------------------------------------------------- */ TupleTableSlot * ExecRecursiveUnion(RecursiveUnionState *node) { PlanState *outerPlan = outerPlanState(node); PlanState *innerPlan = innerPlanState(node); RecursiveUnion *plan = (RecursiveUnion *) node->ps.plan; TupleTableSlot *slot; RUHashEntry entry; bool isnew; /* 1. Evaluate non-recursive term */ if (!node->recursing) { for (;;) { slot = ExecProcNode(outerPlan); if (TupIsNull(slot)) break; if (plan->numCols > 0) { /* Find or build hashtable entry for this tuple's group */ entry = (RUHashEntry) LookupTupleHashEntry(node->hashtable, slot, &isnew); /* Must reset temp context after each hashtable lookup */ MemoryContextReset(node->tempContext); /* Ignore tuple if already seen */ if (!isnew) continue; } /* Each non-duplicate tuple goes to the working table ... */ tuplestore_puttupleslot(node->working_table, slot); /* ... and to the caller */ return slot; } node->recursing = true; } /* 2. Execute recursive term */ for (;;) { slot = ExecProcNode(innerPlan); if (TupIsNull(slot)) { /* Done if there's nothing in the intermediate table */ if (node->intermediate_empty) break; /* done with old working table ... */ tuplestore_end(node->working_table); /* intermediate table becomes working table */ node->working_table = node->intermediate_table; /* create new empty intermediate table */ node->intermediate_table = tuplestore_begin_heap(false, false, work_mem); node->intermediate_empty = true; /* reset the recursive term */ innerPlan->chgParam = bms_add_member(innerPlan->chgParam, plan->wtParam); /* and continue fetching from recursive term */ continue; } if (plan->numCols > 0) { /* Find or build hashtable entry for this tuple's group */ entry = (RUHashEntry) LookupTupleHashEntry(node->hashtable, slot, &isnew); /* Must reset temp context after each hashtable lookup */ MemoryContextReset(node->tempContext); /* Ignore tuple if already seen */ if (!isnew) continue; } /* Else, tuple is good; stash it in intermediate table ... */ node->intermediate_empty = false; tuplestore_puttupleslot(node->intermediate_table, slot); /* ... and return it */ return slot; } return NULL; }