static int pg_callable_func(lua_State *L) { MemoryContext m; int i; FunctionCallInfoData fcinfo; Lua_pgfunc *fi; fi = (Lua_pgfunc *) lua_touserdata(L, lua_upvalueindex(1)); InitFunctionCallInfoData(fcinfo, &fi->fi, fi->numargs, InvalidOid, NULL, NULL); if(tmpcontext_usage> RESET_CONTEXT_AFTER ){ MemoryContextReset(tmpcontext); tmpcontext_usage = 0; } ++tmpcontext_usage; m = MemoryContextSwitchTo(tmpcontext); for (i=0; i<fi->numargs; ++i){ fcinfo.arg[i] = luaP_todatum(L, fi->argtypes[i], 0, &fcinfo.argnull[i], i+1); } if(!fi->options.only_internal && fi->options.throwable){ SPI_push(); PG_TRY(); { Datum d = FunctionCallInvoke(&fcinfo); MemoryContextSwitchTo(m); if (fcinfo.isnull) { lua_pushnil(L); } else { luaP_pushdatum(L, d, fi->prorettype); } SPI_pop(); } PG_CATCH(); { lua_pop(L, lua_gettop(L)); push_spi_error(L, m); /*context switch to m inside push_spi_error*/ SPI_pop(); return lua_error(L); }PG_END_TRY(); }else{ Datum d = FunctionCallInvoke(&fcinfo); MemoryContextSwitchTo(m); if (fcinfo.isnull) { lua_pushnil(L); } else { luaP_pushdatum(L, d, fi->prorettype); } } return 1; }
/* * def load_module(fullname) -> types.ModuleType * * Evaluate the function's code in a new module * or if the fullname exists in sys.modules, return * the existing module. [PEP302] * * This code must go through pl_handler in order to properly load the * module. The execution context can dictate much about what happens during * load time. * * i.e., tricky shit happens here. It may be preferrable to make the execution * context rigging more accessible, but for now, it's the handler's job. */ static PyObj load_module(PyObj self, PyObj args, PyObj kw) { MemoryContext former = CurrentMemoryContext; volatile PyObj rob = NULL; FmgrInfo flinfo; FunctionCallInfoData fcinfo; if (invalid_fullname(self, args, kw)) return(NULL); /* * Disallow execution of "anonymous" functions. */ flinfo.fn_addr = PyPgFunction_GetPGFunction(self); flinfo.fn_oid = PyPgFunction_GetOid(self); flinfo.fn_retset = false; if (flinfo.fn_addr == NULL || flinfo.fn_oid == InvalidOid) { PyErr_SetString(PyExc_TypeError, "internal functions cannot be preloaded"); return(NULL); } flinfo.fn_nargs = -1; flinfo.fn_extra = NULL; flinfo.fn_mcxt = CurrentMemoryContext; flinfo.fn_expr = NULL; fcinfo.nargs = -1; fcinfo.flinfo = &flinfo; fcinfo.context = NULL; fcinfo.resultinfo = NULL; /* * Better be true afterwards. */ fcinfo.isnull = false; SPI_push(); PG_TRY(); { rob = (PyObj) DatumGetPointer(FunctionCallInvoke(&fcinfo)); } PG_CATCH(); { rob = NULL; PyErr_SetPgError(false); } PG_END_TRY(); SPI_pop(); MemoryContextSwitchTo(former); if (fcinfo.isnull == false) { PyErr_SetString(PyExc_RuntimeError, "function module load protocol did not set isnull"); rob = NULL; } Py_XINCREF(rob); return(rob); }
static void _SPI_disc(bool pop) { int ret; if( (ret = SPI_finish()) != SPI_OK_FINISH ) elog( ERROR, "SPI_finish returned %s", SPI_result_code_string(ret)); if(pop) SPI_pop(); }
size_t append_datum(char* buf, Datum val, bool isnull, Oid typeoid) { HeapTuple typeTup; Form_pg_type typeStruct; FmgrInfo tmp_flinfo; char *str; size_t len; typeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(typeoid), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) { elog(ERROR, "Cache lookup failed for %u", typeoid); } typeStruct = (Form_pg_type)GETSTRUCT(typeTup); if (typeStruct->typtype != 'b') { // Non-basic type elog(ERROR, "Don't support non-basic types (%s)", format_type_be(typeoid)); } fmgr_info_cxt(typeStruct->typoutput, &tmp_flinfo, CurTransactionContext); ReleaseSysCache(typeTup); if (!isnull) { if (typeoid == INT4OID) { *((int*)buf) = DatumGetInt32(val); return 4; } SPI_push(); str = OutputFunctionCall(&tmp_flinfo, val); SPI_pop(); len = strlen(str); strncpy(buf, str, len); return len; } return 0; }
Datum pl_bf_call_handler(PG_FUNCTION_ARGS) { FmgrInfo flinfo; Datum retval, proc_source_datum; Form_pg_proc procStruct; Form_pg_type typeStruct; HeapTuple procTup; HeapTuple typeTup; bool isnull; Oid resultTypeIOParam, returnTypeOID; char *proc_source; char *a, *p; int i; // Get the function tuple procTup = SearchSysCache(PROCOID, ObjectIdGetDatum(fcinfo->flinfo->fn_oid), 0, 0, 0); if (!HeapTupleIsValid(procTup)) { elog(ERROR, "Cache lookup failed for procedure %u", fcinfo->flinfo->fn_oid); } procStruct = (Form_pg_proc)GETSTRUCT(procTup); // Get the function source proc_source_datum = SysCacheGetAttr(PROCOID, procTup, Anum_pg_proc_prosrc, &isnull); if (isnull) { elog(ERROR, "Function source is null"); } proc_source = DatumGetCString(DirectFunctionCall1(textout, proc_source_datum)); // Get the return type tuple typeTup = SearchSysCache(TYPEOID, ObjectIdGetDatum(procStruct->prorettype), 0, 0, 0); if (!HeapTupleIsValid(typeTup)) { elog(ERROR, "Cache lookup failed for type %u", procStruct->prorettype); } typeStruct = (Form_pg_type)GETSTRUCT(typeTup); resultTypeIOParam = getTypeIOParam(typeTup); returnTypeOID = procStruct -> prorettype; a = calloc(5000, 1); if (!a) { elog(ERROR, "BAD A!"); } p = a; for (i = 0; i < procStruct->pronargs; i++) { p += append_datum(p, fcinfo->arg[i], fcinfo->argnull[i], procStruct->proargtypes.values[i]); } interpret(a, 0, proc_source); fmgr_info_cxt(typeStruct->typinput, &flinfo, TopMemoryContext); if (returnTypeOID != VOIDOID) { if (returnTypeOID == INT4OID) { retval = Int32GetDatum(*((int*)a)); } else { SPI_push(); if (returnTypeOID == BOOLOID) retval = InputFunctionCall(&flinfo, a[0] ? "TRUE" : "FALSE", resultTypeIOParam, -1); else { retval = InputFunctionCall(&flinfo, pstrdup(a), resultTypeIOParam, -1); } SPI_pop(); } } ReleaseSysCache(procTup); ReleaseSysCache(typeTup); free(a); SPI_finish(); return retval; }
static PyObj func_call(PyObj self, PyObj args, PyObj kw) { MemoryContext former = CurrentMemoryContext; PyObj fn_input, fn_output, input, rob = NULL; TupleDesc td; FmgrInfo flinfo; FunctionCallInfoData fcinfo; volatile Datum datum = 0; /* * Disallow execution of "anonymous" functions. */ flinfo.fn_addr = PyPgFunction_GetPGFunction(self); flinfo.fn_oid = PyPgFunction_GetOid(self); flinfo.fn_retset = PyPgFunction_GetReturnsSet(self); if (flinfo.fn_addr == NULL || flinfo.fn_oid == InvalidOid) { PyErr_SetString(PyExc_TypeError, "internal functions are not directly callable"); return(NULL); } if (flinfo.fn_retset) { PyErr_SetString(PyExc_NotImplementedError, "cannot directly execute set returning functions"); return(NULL); } fn_input = PyPgFunction_GetInput(self); fn_output = PyPgFunction_GetOutput(self); if (PyPgTupleDesc_IsPolymorphic(fn_input) || PyPgType_IsPolymorphic(fn_output)) { PyErr_SetString(PyExc_NotImplementedError, "cannot directly execute polymorphic functions"); return(NULL); } if (PyPgType_GetOid(fn_output) == TRIGGEROID) { PyErr_SetString(PyExc_NotImplementedError, "cannot directly execute TRIGGER returning functions"); return(NULL); } /* No access if failed transaction */ if (DB_IS_NOT_READY()) return(NULL); td = PyPgTupleDesc_GetTupleDesc(fn_input); /* * Normalize the parameters. */ input = PyTuple_FromTupleDescAndParameters(td, args, kw); if (input == NULL) return(NULL); flinfo.fn_nargs = td->natts; flinfo.fn_extra = NULL; flinfo.fn_mcxt = CurrentMemoryContext; flinfo.fn_expr = NULL; fcinfo.flinfo = &flinfo; fcinfo.context = NULL; fcinfo.resultinfo = NULL; fcinfo.isnull = false; /* * Custom built descriptor; no dropped attributes. */ fcinfo.nargs = td->natts; SPI_push(); PG_TRY(); { Py_BuildDatumsAndNulls(td, PyPgTupleDesc_GetTypesTuple(fn_input), input, fcinfo.arg, fcinfo.argnull); datum = FunctionCallInvoke(&fcinfo); /* * Special casing void to avoid the singleton. */ if (fcinfo.isnull || PyPgType_GetOid(fn_output) == VOIDOID) { rob = Py_None; Py_INCREF(rob); } else { /* * Some functions will return a parameter that its given. * This is problematic if we are going to free the output * after re-allocating as a Postgres.Object. */ if (PyPgType_ShouldFree(fn_output)) { int i; /* * Scan for !typbyval parameters. * When one is found, compare the datum to the result datum. */ for (i = 0; i < PyTuple_GET_SIZE(input); ++i) { PyObj param = PyTuple_GET_ITEM(input, i); /* * It's tempting to check the types first, but in situations * of functions doing binary compatible coercion, it would be a * mistake. */ if (PyPgType_ShouldFree(Py_TYPE(param))) { if (PyPgObject_GetDatum(param) == datum) { /* * It's the same Datum of an argument, * inc the ref and return the param. */ if (fn_output == (PyObj) Py_TYPE(param)) { rob = param; Py_INCREF(rob); } else { /* * It's the same Datum, but a different type. * Make a Copy. */ rob = PyPgObject_New(fn_output, datum); } break; } } } /* * It's a newly allocated result? (not an argument) */ if (rob == NULL) { /* * New result, Datum is copied into the PythonMemoryContext */ rob = PyPgObject_New(fn_output, datum); /* * Cleanup. */ pfree(DatumGetPointer(datum)); } } else { /* Not pfree'ing typbyval, so no need to check parameters. */ rob = PyPgObject_New(fn_output, datum); } } } PG_CATCH(); { Py_XDECREF(rob); rob = NULL; PyErr_SetPgError(false); } PG_END_TRY(); SPI_pop(); Py_DECREF(input); MemoryContextSwitchTo(former); return(rob); }