/* function subhandler */ Datum PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc) { Datum rv; PyObject *volatile plargs = NULL; PyObject *volatile plrv = NULL; FuncCallContext *volatile funcctx = NULL; PLySRFState *volatile srfstate = NULL; ErrorContextCallback plerrcontext; /* * If the function is called recursively, we must push outer-level * arguments into the stack. This must be immediately before the PG_TRY * to ensure that the corresponding pop happens. */ PLy_global_args_push(proc); PG_TRY(); { if (proc->is_setof) { /* First Call setup */ if (SRF_IS_FIRSTCALL()) { funcctx = SRF_FIRSTCALL_INIT(); srfstate = (PLySRFState *) MemoryContextAllocZero(funcctx->multi_call_memory_ctx, sizeof(PLySRFState)); /* Immediately register cleanup callback */ srfstate->callback.func = plpython_srf_cleanup_callback; srfstate->callback.arg = (void *) srfstate; MemoryContextRegisterResetCallback(funcctx->multi_call_memory_ctx, &srfstate->callback); funcctx->user_fctx = (void *) srfstate; } /* Every call setup */ funcctx = SRF_PERCALL_SETUP(); Assert(funcctx != NULL); srfstate = (PLySRFState *) funcctx->user_fctx; } if (srfstate == NULL || srfstate->iter == NULL) { /* * Non-SETOF function or first time for SETOF function: build * args, then actually execute the function. */ plargs = PLy_function_build_args(fcinfo, proc); plrv = PLy_procedure_call(proc, "args", plargs); Assert(plrv != NULL); } else { /* * Second or later call for a SETOF function: restore arguments in * globals dict to what they were when we left off. We must do * this in case multiple evaluations of the same SETOF function * are interleaved. It's a bit annoying, since the iterator may * not look at the arguments at all, but we have no way to know * that. Fortunately this isn't terribly expensive. */ if (srfstate->savedargs) PLy_function_restore_args(proc, srfstate->savedargs); srfstate->savedargs = NULL; /* deleted by restore_args */ } /* * If it returns a set, call the iterator to get the next return item. * We stay in the SPI context while doing this, because PyIter_Next() * calls back into Python code which might contain SPI calls. */ if (proc->is_setof) { if (srfstate->iter == NULL) { /* first time -- do checks and setup */ ReturnSetInfo *rsi = (ReturnSetInfo *) fcinfo->resultinfo; if (!rsi || !IsA(rsi, ReturnSetInfo) || (rsi->allowedModes & SFRM_ValuePerCall) == 0) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("unsupported set function return mode"), errdetail("PL/Python set-returning functions only support returning one value per call."))); } rsi->returnMode = SFRM_ValuePerCall; /* Make iterator out of returned object */ srfstate->iter = PyObject_GetIter(plrv); Py_DECREF(plrv); plrv = NULL; if (srfstate->iter == NULL) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("returned object cannot be iterated"), errdetail("PL/Python set-returning functions must return an iterable object."))); } /* Fetch next from iterator */ plrv = PyIter_Next(srfstate->iter); if (plrv == NULL) { /* Iterator is exhausted or error happened */ bool has_error = (PyErr_Occurred() != NULL); Py_DECREF(srfstate->iter); srfstate->iter = NULL; if (has_error) PLy_elog(ERROR, "error fetching next item from iterator"); /* Pass a null through the data-returning steps below */ Py_INCREF(Py_None); plrv = Py_None; } else { /* * This won't be last call, so save argument values. We do * this again each time in case the iterator is changing those * values. */ srfstate->savedargs = PLy_function_save_args(proc); } } /* * Disconnect from SPI manager and then create the return values datum * (if the input function does a palloc for it this must not be * allocated in the SPI memory context because SPI_finish would free * it). */ if (SPI_finish() != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed"); plerrcontext.callback = plpython_return_error_callback; plerrcontext.previous = error_context_stack; error_context_stack = &plerrcontext; /* * If the function is declared to return void, the Python return value * must be None. For void-returning functions, we also treat a None * return value as a special "void datum" rather than NULL (as is the * case for non-void-returning functions). */ if (proc->result.out.d.typoid == VOIDOID) { if (plrv != Py_None) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("PL/Python function with return type \"void\" did not return None"))); fcinfo->isnull = false; rv = (Datum) 0; } else if (plrv == Py_None) { fcinfo->isnull = true; /* * In a SETOF function, the iteration-ending null isn't a real * value; don't pass it through the input function, which might * complain. */ if (srfstate && srfstate->iter == NULL) rv = (Datum) 0; else if (proc->result.is_rowtype < 1) rv = InputFunctionCall(&proc->result.out.d.typfunc, NULL, proc->result.out.d.typioparam, -1); else /* Tuple as None */ rv = (Datum) NULL; } else if (proc->result.is_rowtype >= 1) { TupleDesc desc; /* make sure it's not an unnamed record */ Assert((proc->result.out.d.typoid == RECORDOID && proc->result.out.d.typmod != -1) || (proc->result.out.d.typoid != RECORDOID && proc->result.out.d.typmod == -1)); desc = lookup_rowtype_tupdesc(proc->result.out.d.typoid, proc->result.out.d.typmod); rv = PLyObject_ToCompositeDatum(&proc->result, desc, plrv); fcinfo->isnull = (rv == (Datum) NULL); ReleaseTupleDesc(desc); } else { fcinfo->isnull = false; rv = (proc->result.out.d.func) (&proc->result.out.d, -1, plrv); } } PG_CATCH(); { /* Pop old arguments from the stack if they were pushed above */ PLy_global_args_pop(proc); Py_XDECREF(plargs); Py_XDECREF(plrv); /* * If there was an error within a SRF, the iterator might not have * been exhausted yet. Clear it so the next invocation of the * function will start the iteration again. (This code is probably * unnecessary now; plpython_srf_cleanup_callback should take care of * cleanup. But it doesn't hurt anything to do it here.) */ if (srfstate) { Py_XDECREF(srfstate->iter); srfstate->iter = NULL; /* And drop any saved args; we won't need them */ if (srfstate->savedargs) PLy_function_drop_args(srfstate->savedargs); srfstate->savedargs = NULL; } PG_RE_THROW(); } PG_END_TRY(); error_context_stack = plerrcontext.previous; /* Pop old arguments from the stack if they were pushed above */ PLy_global_args_pop(proc); Py_XDECREF(plargs); Py_DECREF(plrv); if (srfstate) { /* We're in a SRF, exit appropriately */ if (srfstate->iter == NULL) { /* Iterator exhausted, so we're done */ SRF_RETURN_DONE(funcctx); } else if (fcinfo->isnull) SRF_RETURN_NEXT_NULL(funcctx); else SRF_RETURN_NEXT(funcctx, rv); } /* Plain function, just return the Datum value (possibly null) */ return rv; }
Datum ta_f( PG_FUNCTION_ARGS) { // Declare for TA TA_RetCode retCode; TA_Real *closePrice = NULL; TA_Real *out; TA_Integer outBeg; TA_Integer outNbElement; // Declare for postgres FuncCallContext *funcctx; int call_cntr; int max_calls; Datum *result = NULL; int dim; int z; ArrayType *ur = PG_GETARG_ARRAYTYPE_P(0); if (array_contains_nulls(ur)) { ereport(ERROR, ( errcode(ERRCODE_ARRAY_ELEMENT_ERROR), errmsg("cannot work with arrays containing NULLs") )); } dim = ARRNELEMS(ur); closePrice = ARRPTR(ur); if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); /* One-time setup code appears here: */ retCode = TA_Initialize(); if (retCode != TA_SUCCESS) { ereport(ERROR, ( errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Cannot initialize TA-Lib (%d)!\n", retCode) )); } out = palloc(dim * sizeof(TA_Real)); retCode = TA_MA(0, dim - 1, &closePrice[0], 5, TA_MAType_SMA, &outBeg, &outNbElement, &out[0]); result = palloc(outNbElement * sizeof(Datum)); // Error log for debugging /* ereport(NOTICE, (errmsg("dims %d, outBeg: %d, outNbElement %d\n", dim, outBeg, outNbElement))); */ for (z = 0; z < dim; z++) { // Error log for debugging //ereport(NOTICE, (errmsg("z: %d, out[z]: %5.1f", z, out[z]))); if(z > outBeg-1) { result[z] = Float8GetDatum(out[z-outBeg]); continue; } result[z] = NULL; } TA_Shutdown(); funcctx->max_calls = dim; funcctx->user_fctx = result; MemoryContextSwitchTo(oldcontext); } /* stuff done on every call of the function */ funcctx = SRF_PERCALL_SETUP(); call_cntr = funcctx->call_cntr; max_calls = funcctx->max_calls; if (call_cntr < max_calls) { if(((Datum *) funcctx->user_fctx)[call_cntr]) { SRF_RETURN_NEXT(funcctx, ((Datum *) funcctx->user_fctx)[call_cntr]); } SRF_RETURN_NEXT_NULL(funcctx); } SRF_RETURN_DONE(funcctx); }