📄 functions.c
字号:
else paramLI = (ParamListInfo) NULL; if (fcache->paramLI) pfree(fcache->paramLI); fcache->paramLI = paramLI;}static TupleTableSlot *copy_function_result(SQLFunctionCachePtr fcache, TupleTableSlot *resultSlot){ TupleTableSlot *funcSlot; TupleDesc resultTd; HeapTuple resultTuple; HeapTuple newTuple; Assert(!TupIsNull(resultSlot)); resultTuple = resultSlot->val; funcSlot = fcache->funcSlot; if (funcSlot == NULL) return resultSlot; /* no need to copy result */ /* * If first time through, we have to initialize the funcSlot's tuple * descriptor. */ if (funcSlot->ttc_tupleDescriptor == NULL) { resultTd = CreateTupleDescCopy(resultSlot->ttc_tupleDescriptor); ExecSetSlotDescriptor(funcSlot, resultTd, true); ExecSetSlotDescriptorIsNew(funcSlot, true); } newTuple = heap_copytuple(resultTuple); return ExecStoreTuple(newTuple, funcSlot, InvalidBuffer, true);}static Datumpostquel_execute(execution_state *es, FunctionCallInfo fcinfo, SQLFunctionCachePtr fcache){ TupleTableSlot *slot; Datum value; if (es->status == F_EXEC_START) postquel_start(es, fcache); slot = postquel_getnext(es); if (TupIsNull(slot)) { postquel_end(es); fcinfo->isnull = true; /* * If this isn't the last command for the function we have to * increment the command counter so that subsequent commands can * see changes made by previous ones. */ if (!LAST_POSTQUEL_COMMAND(es)) CommandCounterIncrement(); return (Datum) NULL; } if (LAST_POSTQUEL_COMMAND(es)) { TupleTableSlot *resSlot; /* * Copy the result. copy_function_result is smart enough to do * nothing when no action is called for. This helps reduce the * logic and code redundancy here. */ resSlot = copy_function_result(fcache, slot); /* * If we are supposed to return a tuple, we return the tuple slot * pointer converted to Datum. If we are supposed to return a * simple value, then project out the first attribute of the * result tuple (ie, take the first result column of the final * SELECT). */ if (fcache->returnsTuple) { /* * XXX do we need to remove junk attrs from the result tuple? * Probably OK to leave them, as long as they are at the end. */ value = PointerGetDatum(resSlot); fcinfo->isnull = false; } else { value = heap_getattr(resSlot->val, 1, resSlot->ttc_tupleDescriptor, &(fcinfo->isnull)); /* * Note: if result type is pass-by-reference then we are * returning a pointer into the tuple copied by * copy_function_result. This is OK. */ } /* * If this is a single valued function we have to end the function * execution now. */ if (!fcinfo->flinfo->fn_retset) postquel_end(es); return value; } /* * If this isn't the last command for the function, we don't return * any results, but we have to increment the command counter so that * subsequent commands can see changes made by previous ones. */ CommandCounterIncrement(); return (Datum) NULL;}Datumfmgr_sql(PG_FUNCTION_ARGS){ MemoryContext oldcontext; SQLFunctionCachePtr fcache; ErrorContextCallback sqlerrcontext; execution_state *es; Datum result = 0; /* * Switch to context in which the fcache lives. This ensures that * parsetrees, plans, etc, will have sufficient lifetime. The * sub-executor is responsible for deleting per-tuple information. */ oldcontext = MemoryContextSwitchTo(fcinfo->flinfo->fn_mcxt); /* * Setup error traceback support for ereport() */ sqlerrcontext.callback = sql_exec_error_callback; sqlerrcontext.arg = fcinfo->flinfo; sqlerrcontext.previous = error_context_stack; error_context_stack = &sqlerrcontext; /* * Initialize fcache (build plans) if first time through. */ fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra; if (fcache == NULL) { init_sql_fcache(fcinfo->flinfo); fcache = (SQLFunctionCachePtr) fcinfo->flinfo->fn_extra; } es = fcache->func_state; /* * Convert params to appropriate format if starting a fresh execution. * (If continuing execution, we can re-use prior params.) */ if (es && es->status == F_EXEC_START) postquel_sub_params(fcache, fcinfo); /* * Find first unfinished query in function. */ while (es && es->status == F_EXEC_DONE) es = es->next; /* * Execute each command in the function one after another until we're * executing the final command and get a result or we run out of * commands. */ while (es) { result = postquel_execute(es, fcinfo, fcache); if (es->status != F_EXEC_DONE) break; es = es->next; } /* * If we've gone through every command in this function, we are done. */ if (es == (execution_state *) NULL) { /* * Reset the execution states to start over again on next call. */ es = fcache->func_state; while (es) { es->status = F_EXEC_START; es = es->next; } /* * Let caller know we're finished. */ if (fcinfo->flinfo->fn_retset) { ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; if (rsi && IsA(rsi, ReturnSetInfo)) rsi->isDone = ExprEndResult; else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); fcinfo->isnull = true; result = (Datum) 0; /* Deregister shutdown callback, if we made one */ if (fcache->shutdown_reg) { UnregisterExprContextCallback(rsi->econtext, ShutdownSQLFunction, PointerGetDatum(fcache)); fcache->shutdown_reg = false; } } error_context_stack = sqlerrcontext.previous; MemoryContextSwitchTo(oldcontext); return result; } /* * If we got a result from a command within the function it has to be * the final command. All others shouldn't be returning anything. */ Assert(LAST_POSTQUEL_COMMAND(es)); /* * Let caller know we're not finished. */ if (fcinfo->flinfo->fn_retset) { ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; if (rsi && IsA(rsi, ReturnSetInfo)) rsi->isDone = ExprMultipleResult; else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("set-valued function called in context that cannot accept a set"))); /* * Ensure we will get shut down cleanly if the exprcontext is not * run to completion. */ if (!fcache->shutdown_reg) { RegisterExprContextCallback(rsi->econtext, ShutdownSQLFunction, PointerGetDatum(fcache)); fcache->shutdown_reg = true; } } error_context_stack = sqlerrcontext.previous; MemoryContextSwitchTo(oldcontext); return result;}/* * error context callback to let us supply a call-stack traceback */static voidsql_exec_error_callback(void *arg){ FmgrInfo *flinfo = (FmgrInfo *) arg; SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) flinfo->fn_extra; char *fn_name; fn_name = get_func_name(flinfo->fn_oid); /* safety check, shouldn't happen */ if (fn_name == NULL) return; /* * Try to determine where in the function we failed. If there is a * query with non-null QueryDesc, finger it. (We check this rather * than looking for F_EXEC_RUN state, so that errors during * ExecutorStart or ExecutorEnd are blamed on the appropriate query; * see postquel_start and postquel_end.) */ if (fcache) { execution_state *es; int query_num; es = fcache->func_state; query_num = 1; while (es) { if (es->qd) { errcontext("SQL function \"%s\" statement %d", fn_name, query_num); break; } es = es->next; query_num++; } if (es == NULL) { /* * couldn't identify a running query; might be function entry, * function exit, or between queries. */ errcontext("SQL function \"%s\"", fn_name); } } else { /* must have failed during init_sql_fcache() */ errcontext("SQL function \"%s\" during startup", fn_name); } /* free result of get_func_name (in case this is only a notice) */ pfree(fn_name);}/* * callback function in case a function-returning-set needs to be shut down * before it has been run to completion */static voidShutdownSQLFunction(Datum arg){ SQLFunctionCachePtr fcache = (SQLFunctionCachePtr) DatumGetPointer(arg); execution_state *es = fcache->func_state; while (es != NULL) { /* Shut down anything still running */ if (es->status == F_EXEC_RUN) postquel_end(es); /* Reset states to START in case we're called again */ es->status = F_EXEC_START; es = es->next; } /* execUtils will deregister the callback... */ fcache->shutdown_reg = false;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -