static PyObj func_new_from_oid(PyTypeObject *subtype, Oid fn_oid, PyObj fn_oid_int, PyObj fn_oid_str) { volatile HeapTuple ht = NULL; volatile PyObj rob = NULL; Assert(OidIsValid(fn_oid)); Assert(fn_oid_int != NULL); Assert(fn_oid_str != NULL); rob = subtype->tp_alloc(subtype, 0); if (rob == NULL) return(NULL); PyPgFunction_SetOid(rob, fn_oid); PyPgFunction_SetStateful(rob, false); Py_INCREF(fn_oid_int); Py_INCREF(fn_oid_str); PyPgFunction_SetPyLongOid(rob, fn_oid_int); PyPgFunction_SetPyUnicodeOid(rob, fn_oid_str); /* * Collect the Function information from the system cache */ PG_TRY(); { Form_pg_proc ps; Form_pg_namespace ns; FmgrInfo flinfo; text *prosrc; Datum prosrc_datum; bool isnull = true; const char *filename = NULL, *nspname, *q_nspname; TupleDesc argdesc = NULL, result_desc = NULL; Oid prorettype = InvalidOid; PyObj id_str_ob = NULL, nspname_str_ob = NULL; PyObj filename_str_ob = NULL, q_nspname_str_ob = NULL; PyObj output = NULL, src = NULL; PyObj input; ht = SearchSysCache(PROCOID, fn_oid, 0, 0, 0); if (!HeapTupleIsValid(ht)) { ereport(ERROR,( errcode(ERRCODE_UNDEFINED_FUNCTION), errmsg("failed to find function at oid %d", fn_oid) )); } PyPgFunction_SetXMin(rob, HeapTupleHeaderGetXmin(ht->t_data)); PyPgFunction_SetItemPointer(rob, &(ht->t_self)); ps = (Form_pg_proc) GETSTRUCT(ht); PyPgFunction_SetNamespace(rob, ps->pronamespace); PyPgFunction_SetLanguage(rob, ps->prolang); PyPgFunction_SetReturnsSet(rob, ps->proretset); PyPgFunction_SetVolatile(rob, ps->provolatile); prorettype = ps->prorettype; prosrc_datum = SysCacheGetAttr( PROCOID, ht, Anum_pg_proc_prosrc, &isnull); if (!isnull) { prosrc = DatumGetTextPCopy(prosrc_datum); src = PyUnicode_FromTEXT(prosrc); PyPgFunction_SetSource(rob, src); pfree(prosrc); prosrc = NULL; } else { src = Py_None; Py_INCREF(src); PyPgFunction_SetSource(rob, src); } if (src == NULL) PyErr_RelayException(); /* * Get the function's address. */ fmgr_info(fn_oid, &flinfo); PyPgFunction_SetPGFunction(rob, flinfo.fn_addr); /* * Build function parameters TupleDesc */ if (ps->pronargs > 0) { argdesc = TupleDesc_From_pg_proc_arginfo(ht); input = PyPgTupleDesc_FromCopy(argdesc); if (input == NULL) PyErr_RelayException(); PyPgFunction_SetInput(rob, input); FreeTupleDesc(argdesc); } else { Py_INCREF(EmptyPyPgTupleDesc); PyPgFunction_SetInput(rob, EmptyPyPgTupleDesc); } /* * If it's a registered composite, * PyPgType_FromOid will resolve that below. */ if (prorettype == RECORDOID) { /* * Otherwise, build out a function result tupdesc. */ result_desc = build_function_result_tupdesc_t(ht); if (result_desc != NULL) { /* * Anonymous composite returned by function. */ output = PyPgType_FromTupleDesc(result_desc); PyPgFunction_SetOutput(rob, output); FreeTupleDesc(result_desc); /* * We will certainly be using it, so bless it right now iff * it's *not* polymorphic. */ if (output && !PyPgType_IsPolymorphic(output)) BlessTupleDesc(PyPgType_GetTupleDesc(output)); } else { /* * ew.. */ goto lookup_output_type; } } else { lookup_output_type: output = PyPgType_FromOid(prorettype); if (output == NULL) PyErr_RelayException(); PyPgFunction_SetOutput(rob, output); } RELEASESYSCACHE(&ht); /* * Don't worry *too* much about leaking memory. */ filename = format_procedure(fn_oid); Assert(filename != NULL); ht = SearchSysCache(NAMESPACEOID, PyPgFunction_GetNamespace(rob), 0, 0, 0); if (!HeapTupleIsValid(ht)) { pfree((char *) filename); elog(ERROR, "function %u namespace %u does not exist", fn_oid, PyPgFunction_GetNamespace(rob)); } ns = (Form_pg_namespace) GETSTRUCT(ht); nspname = pstrdup(NameStr(ns->nspname)); RELEASESYSCACHE(&ht); /* * Build the filename string. */ q_nspname = quote_identifier(nspname); nspname_str_ob = PyUnicode_FromCString(nspname); PyPgFunction_SetNamespaceName(rob, nspname_str_ob); if (nspname_str_ob == NULL) { /* * Invalid encoded string? */ if (nspname != q_nspname) pfree((char *) q_nspname); pfree((char *) nspname); PyErr_RelayException(); } q_nspname_str_ob = PyUnicode_FromCString(q_nspname); if (nspname != q_nspname) pfree((char *) q_nspname); pfree((char *) nspname); /* * Ignore the potential exception for a moment. */ id_str_ob = PyUnicode_FromCString(filename); /* * Skip the filename_str_ob if either of the above failed. */ if (id_str_ob != NULL && q_nspname_str_ob != NULL) { if (FunctionIsVisible(fn_oid)) filename_str_ob = PyUnicode_FromFormat("%U.%U", q_nspname_str_ob, id_str_ob); else { filename_str_ob = id_str_ob; Py_INCREF(id_str_ob); } } PyPgFunction_SetFilename(rob, filename_str_ob); Py_XDECREF(q_nspname_str_ob); Py_XDECREF(id_str_ob); pfree((char *) filename); if (filename_str_ob == NULL) PyErr_RelayException(); } PG_CATCH(); { Py_XDECREF(rob); rob = NULL; PyErr_SetPgError(false); if (ht != NULL) ReleaseSysCache(ht); } PG_END_TRY(); return(rob); }
/* * Polymorph the polymorphic types in the TupleDesc to the appropriate type * related to the 'target' base type. * * Polymorphic types are pseudo types and thus Postgres won't store them, * so 'self' will never be a relation type. */ PyObj PyPgTupleDesc_Polymorph(PyObj self, PyObj target) { int i; TupleDesc td; PyObj td_types, rob; MemoryContext former; Assert(PyPgTupleDesc_CheckExact(self)); Assert(PyPgTupleDesc_GetPolymorphic(self) != -1); Assert(PyPgType_Check(target)); Assert(!PyPgType_IsPolymorphic(target)); td_types = PyPgTupleDesc_GetTypesTuple(self); td = PyPgTupleDesc_GetTupleDesc(self); /* * We need to update any polymorphic attributes, so * grab a copy. */ former = MemoryContextSwitchTo(PythonMemoryContext); if (td->constr != NULL) td = Py_CreateTupleDescCopyConstr(td); else td = Py_CreateTupleDescCopy(td); MemoryContextSwitchTo(former); if (td == NULL) return(NULL); for (i = 0; i < td->natts; ++i) { PyObj polymorphic_type, polymorphed_type; PyPgTypeInfo typinfo; if (!IsPolymorphicType(td->attrs[i]->atttypid)) continue; polymorphic_type = PyTuple_GET_ITEM(td_types, i); polymorphed_type = PyPgType_Polymorph(polymorphic_type, target); if (polymorphed_type == NULL) { Py_FreeTupleDesc(td); return(NULL); } typinfo = PyPgTypeInfo(polymorphed_type); /* * It's not a pseudo type anymore; change the oid. */ td->attrs[i]->atttypid = typinfo->typoid; td->attrs[i]->attalign = typinfo->typalign; td->attrs[i]->attbyval = typinfo->typbyval; td->attrs[i]->attlen = typinfo->typlen; /* * Done with that type for now. * PyPgTupleDesc_New() will grab a new reference when it builds the types * tuple. */ Py_DECREF(polymorphed_type); } /* * Make and return the PyPgTupleDesc object.. */ rob = PyPgTupleDesc_New(td); Assert(PyPgTupleDesc_GetPolymorphic(rob) == -1); return(rob); }
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); }