/* * 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 int obj_bool(PyObj self) { int r; /* * Use self's Datum if it's a bool object. * Otherwise, cast to a bool. */ if (PyPg_bool_Check(self)) r = DatumGetBool(PyPgObject_GetDatum(self)) ? 1 : 0; else { Datum d; bool isnull; PG_TRY(); { PyPgType_typcast((PyObj) &PyPg_bool_Type, self, -1, &d, &isnull); if (isnull) r = 0; else r = DatumGetBool(d) ? 1 : 0; } PG_CATCH(); { r = -1; PyErr_SetPgError(false); } PG_END_TRY(); } return(r); }
bool pl_ist_begin(char state) { bool r = true; PG_TRY(); { /* Prevent wrap around */ if (pl_ist_count + 1 == 0) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("too many internal subtransactions"), errdetail("Subtransaction %lu was expected to exit next.", pl_ist_count) )); } if (state == pl_ist_committed) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("cannot start a committed subtransaction") )); } if (state == pl_ist_aborted) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("cannot start an aborted subtransaction") )); } if (state == pl_ist_open) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("subtransaction is already open") )); } BeginInternalSubTransaction(NULL); pl_ist_count = pl_ist_count + 1; } PG_CATCH(); { PyErr_SetPgError(true); pl_state = pl_in_failed_transaction; r = false; } PG_END_TRY(); return(r); }
/* * There should be only one. */ void EmptyPyPgTupleDesc_Initialize(void) { PG_TRY(); { TupleDesc td; td = CreateTemplateTupleDesc(0, false); EmptyPyPgTupleDesc = PyPgTupleDesc_FromCopy(td); FreeTupleDesc(td); } PG_CATCH(); { PyErr_SetPgError(true); } PG_END_TRY(); }
static PyObj array_slice(PyObj self, Py_ssize_t from, Py_ssize_t to) { PyObj elm; PyPgTypeInfo etc; ArrayType *at, *rat = NULL; PyObj rob = NULL; int idx_lower[MAXDIM] = {(int) from+1, 0,}; int idx_upper[MAXDIM] = {(int) to+1, 0,}; elm = PyPgType_GetElementType(Py_TYPE(self)); Assert(elm != NULL); etc = PyPgTypeInfo(elm); Assert(etc != NULL); at = DatumGetArrayTypeP(PyPgObject_GetDatum(self)); Assert(at != NULL); PG_TRY(); { rat = array_get_slice(at, 1, idx_upper, idx_lower, PyPgTypeInfo(Py_TYPE(self))->typlen, etc->typlen, etc->typbyval, etc->typalign); rob = PyPgObject_New(Py_TYPE(self), PointerGetDatum(rat)); if (rob == NULL) pfree(rat); } PG_CATCH(); { PyErr_SetPgError(false); return(NULL); } PG_END_TRY(); return(rob); }
static PyObj array_item(PyObj self, Py_ssize_t item) { volatile PyObj rob = NULL; PyPgTypeInfo typinfo, atypinfo; ArrayType *at; Datum rd; bool isnull = false; int index = (int) item; PyObj elm; elm = PyPgType_GetElementType(Py_TYPE(self)); typinfo = PyPgTypeInfo(elm); atypinfo = PyPgTypeInfo(Py_TYPE(self)); at = DatumGetArrayTypeP(PyPgObject_GetDatum(self)); /* convert index */ ++index; if (ARR_NDIM(at) == 0) { PyErr_SetString(PyExc_IndexError, "empty array"); return(NULL); } /* * Note that the comparison is '>', not '>='. */ if (index > ARR_DIMS(at)[0]) { PyErr_Format(PyExc_IndexError, "index %d out of range %d", item, ARR_DIMS(at)[0]); return(NULL); } /* * Single dimenion array? Get an element. */ if (ARR_NDIM(at) == 1) { PG_TRY(); { rd = array_ref(at, 1, &index, atypinfo->typlen, typinfo->typlen, typinfo->typbyval, typinfo->typalign, &isnull); if (isnull) { rob = Py_None; Py_INCREF(rob); } else { /* * It points into the array structure, so there's no need to free. */ rob = PyPgObject_New(elm, rd); } } PG_CATCH(); { Py_XDECREF(rob); rob = NULL; PyErr_SetPgError(false); return(NULL); } PG_END_TRY(); } else { ArrayType *rat; int lower[MAXDIM] = {index,0,}; int upper[MAXDIM] = {index,0,}; /* * Multiple dimensions, so get a slice. */ PG_TRY(); { ArrayType *xat; Datum *elements; bool *nulls; int nelems; int ndims, i; int lbs[MAXDIM]; int dims[MAXDIM]; xat = array_get_slice(at, 1, upper, lower, atypinfo->typlen, typinfo->typlen, typinfo->typbyval, typinfo->typalign); /* * Eventually, this should probably be changed to change the already * allocated ArrayType at 'xat', but for now use the available * interfaces for creating the expected result. */ deconstruct_array(xat, typinfo->typoid, typinfo->typlen, typinfo->typbyval, typinfo->typalign, &elements, &nulls, &nelems ); /* * Alter dims, lbs, and ndims: we are removing the first dimension. */ ndims = ARR_NDIM(xat); for (i = 1; i < ndims; ++i) lbs[i-1] = ARR_LBOUND(xat)[i]; for (i = 1; i < ndims; ++i) dims[i-1] = ARR_DIMS(xat)[i]; --ndims; /* * Construct the expected result to a Python itemget call. */ rat = construct_md_array(elements, nulls, ndims, dims, lbs, typinfo->typoid, typinfo->typlen, typinfo->typbyval, typinfo->typalign); pfree(elements); pfree(nulls); pfree(xat); rob = PyPgObject_New(Py_TYPE(self), PointerGetDatum(rat)); pfree(rat); } PG_CATCH(); { PyErr_SetPgError(false); return(NULL); } PG_END_TRY(); } return(rob); }
static PyObj binary_operate(const char *op, PyObj left, PyObj right) { PyObj base = PyPgObject_Check(left) ? left : right; PyObj rob = NULL; Datum dleft, dright; Datum dcoerce; bool lisnull = false, risnull = false, coerce_isnull = true; Oid left_oid, right_oid; Py_ALLOCATE_OWNER(); { volatile Datum rd = 0; List * volatile namelist = NULL; PyObj rtype; PyObj coerce = NULL; PG_TRY(); { struct FmgrInfo flinfo = {0,}; struct FunctionCallInfoData fcinfo = {0,}; Operator opt; Form_pg_operator ops; Oid actual[2]; Oid declared[2]; Oid result_type, fn_oid; /* * base and coerce are used to manage preliminary coercion. * If either side of the operator is not a PyPgObject, convert the * object to the type of the other side. */ if (base == left) { if (!PyPgObject_Check(right)) coerce = right; } else coerce = left; if (coerce != NULL) { PyPgType_DatumNew((PyObj) Py_TYPE(base), coerce, -1, &dcoerce, &coerce_isnull); if (base == left) { dleft = PyPgObject_GetDatum(left); lisnull = false; dright = dcoerce; risnull = coerce_isnull; } else { dleft = dcoerce; lisnull = coerce_isnull; dright = PyPgObject_GetDatum(right); risnull = false; } /* * Both are the same type as base due to coercion. */ left_oid = right_oid = PyPgType_GetOid(Py_TYPE(base)); } else { /* * Both objects are PyPgObjects. */ dleft = PyPgObject_GetDatum(left); left_oid = PyPgType_GetOid(Py_TYPE(left)); dright = PyPgObject_GetDatum(right); right_oid = PyPgType_GetOid(Py_TYPE(right)); } namelist = stringToQualifiedNameList(op); opt = oper(NULL, (List *) namelist, left_oid, right_oid, false, 1); ops = (Form_pg_operator) GETSTRUCT(opt); fn_oid = ops->oprcode; declared[0] = ops->oprleft; declared[1] = ops->oprright; actual[0] = left_oid; actual[1] = right_oid; result_type = ops->oprresult; ReleaseSysCache((HeapTuple) opt); result_type = enforce_generic_type_consistency( actual, declared, 2, result_type, true); rtype = PyPgType_FromOid(result_type); rtype = Py_XACQUIRE(rtype); list_free((List *) namelist); namelist = NULL; if (rtype == NULL) PyErr_RelayException(); fmgr_info(fn_oid, &flinfo); fcinfo.flinfo = &flinfo; fcinfo.nargs = 2; fcinfo.arg[0] = dleft; fcinfo.argnull[0] = lisnull; fcinfo.arg[1] = dright; fcinfo.argnull[1] = risnull; rd = FunctionCallInvoke(&fcinfo); if (fcinfo.isnull) rob = Py_None; else { rob = PyPgObject_New(rtype, rd); Py_XACQUIRE(rob); if (PyPgType_ShouldFree(rtype)) pfree(DatumGetPointer(rd)); } if (!coerce_isnull && PyPgType_ShouldFree(Py_TYPE(base))) pfree(DatumGetPointer(dcoerce)); } PG_CATCH(); { PyErr_SetPgError(false); rob = NULL; } PG_END_TRY(); Py_XINCREF(rob); } Py_DEALLOCATE_OWNER(); return(rob); }
/* * XXX: If type methods ever come along, hopefully this will be implemented * using a more generalized function. */ static PyObj obj_absolute(PyObj self) { MemoryContext former; Oid typoid; volatile PyObj rob = NULL; if (DB_IS_NOT_READY() || PyPgObjectType_Require(Py_TYPE(self))) return(NULL); typoid = PyPgType_GetOid(Py_TYPE(self)); former = CurrentMemoryContext; PG_TRY(); { HeapTuple procTuple; Datum rd = 0; Oid procoid, roid; List *qnl; qnl = stringToQualifiedNameList("abs"); procoid = LookupFuncName(qnl, 1, &(typoid), true); list_free(qnl); if (procoid == InvalidOid) { PyErr_Format(PyExc_LookupError, "no such function named 'abs' for type %u", typoid); return(NULL); } procTuple = SearchSysCache(PROCOID, procoid, 0, 0, 0); if (procTuple == NULL) { PyErr_Format(PyExc_LookupError, "no procedure with Oid %u", procoid); return(NULL); } roid = ((Form_pg_proc) GETSTRUCT(procTuple))->prorettype; ReleaseSysCache(procTuple); rd = OidFunctionCall1(procoid, PyPgObject_GetDatum(self)); rob = PyPgObject_FromTypeOidAndDatum(roid, rd); if (PyPgType_ShouldFree(Py_TYPE(rob))) { /* * That's our datum... */ if (PyPgObject_GetDatum(self) != rd) pfree(DatumGetPointer(rd)); } } PG_CATCH(); { Py_XDECREF(rob); PyErr_SetPgError(false); return(NULL); } PG_END_TRY(); return(rob); }
bool pl_ist_abort(unsigned long xid, char state) { bool r = true; PG_TRY(); { if (pl_ist_count != xid) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("out-of-order abort attempt on subtransaction %lu", xid), errdetail("Subtransaction %lu was expected to exit next.", pl_ist_count) )); } /* Prevent wrap around */ if (pl_ist_count == 0) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("no current internal subtransaction"), errhint("Attempt to abort the current IST, when none running.") )); } if (state == pl_ist_committed) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("cannot abort a committed subtransaction") )); } if (state == pl_ist_aborted) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("subtransaction was already aborted") )); } if (state == pl_ist_new) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("cannot abort a subtransaction that has not been started") )); } pl_ist_count = pl_ist_count - 1; RollbackAndReleaseCurrentSubTransaction(); pl_state = pl_ready_for_access; /* No longer in an error state. */ SPI_restore_connection(); } PG_CATCH(); { PyErr_SetPgError(true); pl_state = pl_in_failed_transaction; r = false; } PG_END_TRY(); return(r); }
bool pl_ist_commit(unsigned long xid, char state) { bool r = true; /* * Attempted commit while in an error state? * Resolve to abort, and raise an exception. */ if (pl_state) { r = false; /* Always raises an exception in here */ if (pl_ist_abort(xid, state)) { /* * The abort was successful, but that fact needs to be * communicated as the original action was a commit. * Raise an exception. */ PyErr_SetInFailedTransaction(); } Assert(PyErr_Occurred()); } else { PG_TRY(); { if (pl_ist_count != xid) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("out-of-order commit attempted on subtransaction %lu", xid), errdetail("Subtransaction %lu was expected to exit next.", pl_ist_count) )); } /* Prevent wrap around */ if (pl_ist_count == 0) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("no current internal subtransaction"), errhint("Attempt to commit an IST, when none running.") )); } if (state == pl_ist_committed) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("subtransaction already committed") )); } if (state == pl_ist_aborted) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("cannot commit aborted subtransaction") )); } if (state == pl_ist_new) { ereport(ERROR,( errcode(ERRCODE_SAVEPOINT_EXCEPTION), errmsg("cannot commit a subtransaction that has not been started") )); } pl_ist_count = pl_ist_count - 1; ReleaseCurrentSubTransaction(); } PG_CATCH(); { PyErr_SetPgError(true); pl_state = pl_in_failed_transaction; r = false; } PG_END_TRY(); /* end of commit */ } return(r); }
static int load_rows(PyObj self, PyObj row_iter, uint32 *total) { MemoryContext former = CurrentMemoryContext; volatile PyObj row = NULL; Datum *datums; bool *nulls; char *cnulls; int r = 0; SPIPlanPtr plan; Assert(!ext_state); Assert(PyIter_Check(row_iter)); plan = PyPgStatement_GetPlan(self); if (plan == NULL) return(-1); PG_TRY(); { PyObj tdo = PyPgStatement_GetInput(self); PyObj typs = PyPgTupleDesc_GetTypesTuple(tdo); PyObj namemap = PyPgTupleDesc_GetNameMap(tdo); TupleDesc td = PyPgTupleDesc_GetTupleDesc(tdo); int rnatts = PyPgTupleDesc_GetNatts(tdo); int *freemap = PyPgTupleDesc_GetFreeMap(tdo); int spi_r; datums = palloc(sizeof(Datum) * td->natts); nulls = palloc(sizeof(bool) * td->natts); cnulls = palloc(sizeof(char) * td->natts); while ((row = PyIter_Next(row_iter))) { PyObj pargs; pargs = Py_NormalizeRow(rnatts, td, namemap, row); Py_DECREF(row); if (pargs == NULL) { r = -1; break; } row = pargs; Py_BuildDatumsAndNulls(td, typs, row, datums, nulls); Py_DECREF(row); row = NULL; /* borrow spi_r for a moment */ for (spi_r = 0; spi_r < td->natts; ++spi_r) { cnulls[spi_r] = nulls[spi_r] ? 'n' : ' '; } spi_r = SPI_execute_plan(plan, datums, cnulls, false, 1); /* * Free the built datums. */ FreeReferences(freemap, datums, nulls); if (spi_r < 0) raise_spi_error(spi_r); *total = *total + SPI_processed; } pfree(datums); pfree(nulls); pfree(cnulls); } PG_CATCH(); { /* * WARNING: Leaks datums & nulls on error. Yay, procCxt. */ PyErr_SetPgError(false); Py_XDECREF(row); r = -1; } PG_END_TRY(); MemoryContextSwitchTo(former); return(r); }
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); }
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); }
/* * Python only supports prefix unary operators. */ static PyObj unary_operate(const char *op, PyObj right) { PyObj rob = NULL; Datum dright = PyPgObject_GetDatum(right); Oid right_oid = PyPgType_GetOid(Py_TYPE(right)); Py_ALLOCATE_OWNER(); { PyObj rtype; Operator opt; volatile Datum rd = 0; List * volatile namelist = NULL; PG_TRY(); { struct FmgrInfo flinfo = {0,}; struct FunctionCallInfoData fcinfo = {0,}; Form_pg_operator ops; Oid declared, result_type, fn_oid; namelist = stringToQualifiedNameList(op); opt = oper(NULL, (List *) namelist, InvalidOid, right_oid, false, 1); ops = (Form_pg_operator) GETSTRUCT(opt); fn_oid = ops->oprcode; declared = ops->oprright; result_type = ops->oprresult; ReleaseSysCache((HeapTuple) opt); result_type = enforce_generic_type_consistency( &right_oid, &declared, 1, result_type, true); rtype = PyPgType_FromOid(result_type); Py_XACQUIRE(rtype); list_free((List *) namelist); namelist = NULL; if (rtype == NULL) elog(ERROR, "operator result type could not be created"); fmgr_info(fn_oid, &flinfo); fcinfo.flinfo = &flinfo; fcinfo.nargs = 1; fcinfo.arg[0] = dright; fcinfo.argnull[0] = false; rd = FunctionCallInvoke(&fcinfo); if (fcinfo.isnull) { rob = Py_None; Py_INCREF(rob); Py_ACQUIRE(rob); } else { rob = PyPgObject_New(rtype, rd); Py_XACQUIRE(rob); if (PyPgType_ShouldFree(rtype)) pfree(DatumGetPointer(rd)); } } PG_CATCH(); { PyErr_SetPgError(false); rob = NULL; } PG_END_TRY(); Py_XINCREF(rob); } Py_DEALLOCATE_OWNER(); return(rob); }
/* * array_from_list - given an element type and a list(), build an array * using the described structure. * * Sets a Python error and returns NULL on failure. */ static ArrayType * array_from_list_and_info(PyObj element_type, PyObj listob, int elemmod, int ndims, int *dims, int *lbs) { PyPgTypeInfo typinfo = PyPgTypeInfo(element_type); unsigned int nelems; int i; Datum * volatile datums = NULL; bool * volatile nulls = NULL; ArrayType * volatile rat = NULL; Assert(PyList_CheckExact(listob)); nelems = PyList_GET_SIZE(listob); PG_TRY(); { /* * palloc0 as cleanup in the PG_CATCH() will depend on this. */ nulls = palloc0(sizeof(bool) * nelems); datums = palloc0(sizeof(Datum) * nelems); for (i = 0; i < nelems; ++i) { PyPgType_DatumNew(element_type, PyList_GET_ITEM(listob, i), elemmod, (Datum *) &(datums[i]), (bool *) &(nulls[i])); } /* * Everything has been allocated, make the array. */ rat = construct_md_array( (Datum *) datums, (bool *) nulls, ndims, dims, lbs, typinfo->typoid, typinfo->typlen, typinfo->typbyval, typinfo->typalign); /* * Cleanup. */ if (!typinfo->typbyval) { for (i = 0; i < nelems; ++i) { /* * Array construction completed successfully, * so go over the entire array of datums. */ if (!nulls[i]) pfree(DatumGetPointer(datums[i])); } } pfree((bool *) nulls); pfree((Datum *) datums); } PG_CATCH(); { /* * Try and cleanup as much memory as possible. * * Currently, this code will run in the procedure context, * so whatever leaks here will remain allocated for the duration of the * procedure. If failure is often the part of a loop, the leaks could * be problematic. */ if (rat != NULL) { /* * When rat != NULL, failure occurred after the array * was built, which means it had trouble freeing the resources. * Attempt to free rat, but leave it at that. */ pfree((char *) rat); } else { if (datums != NULL && nulls != NULL) { if (!typinfo->typbyval) { /* * This is a bit different from the non-error case; * rather than pfree'ing everything, we watch for * NULL pointers.. */ for (i = 0; i < nelems; ++i) { char *p = DatumGetPointer(datums[i]); if (nulls[i]) continue; if (PointerIsValid(p)) pfree(p); else break; } } } if (datums != NULL) pfree((Datum *) datums); if (nulls != NULL) pfree((bool *) nulls); } PyErr_SetPgError(false); rat = NULL; } PG_END_TRY(); return((ArrayType *) rat); }
/* * array_element - get an iterator to all the elements in the array * * The short: deconstruct and build a list of element instances. */ static PyObj array_elements(PyObj self) { PyObj element_type; volatile PyObj rob = NULL; PyPgTypeInfo typinfo; element_type = PyPgType_GetElementType(Py_TYPE(self)); typinfo = PyPgTypeInfo(element_type); /* * Multiple dimensions, so get a slice. */ PG_TRY(); { Datum *elements; bool *nulls; int i, nelems; ArrayType *at; at = DatumGetArrayTypeP(PyPgObject_GetDatum(self)); deconstruct_array(at, typinfo->typoid, typinfo->typlen, typinfo->typbyval, typinfo->typalign, &elements, &nulls, &nelems ); rob = PyList_New(nelems); for (i = 0; i < nelems; ++i) { PyObj ob; if (nulls[i]) { ob = Py_None; Py_INCREF(ob); } else ob = PyPgObject_New(element_type, elements[i]); if (ob == NULL) { Py_DECREF(rob); rob = NULL; break; } PyList_SET_ITEM(rob, i, ob); } pfree(elements); pfree(nulls); } PG_CATCH(); { Py_XDECREF(rob); rob = NULL; PyErr_SetPgError(false); return(NULL); } PG_END_TRY(); return(rob); }
static PyObj array_sql_get_element(PyObj self, PyObj indexes_ob) { PyObj tup, element_type, rob = NULL; PyPgTypeInfo atypinfo, typinfo; ArrayType *at; int i, nindexes, indexes[MAXDIM] = {0,}; /* * Convert the dimensions keyword into a tuple and extract the values * into the dims[] array. */ tup = Py_Call((PyObj) &PyTuple_Type, indexes_ob); if (tup == NULL) return(NULL); at = DatumGetArrayTypeP(PyPgObject_GetDatum(self)); Assert(at != NULL); nindexes = (int) PyTuple_GET_SIZE(tup); if (nindexes != ARR_NDIM(at)) { Py_DECREF(tup); Py_INCREF(Py_None); return(Py_None); } for (i = 0; i < nindexes; ++i) { indexes[i] = (int) PyNumber_AsSsize_t(PyTuple_GET_ITEM(tup, i), NULL); if (PyErr_Occurred()) { Py_DECREF(tup); return(NULL); } } Py_DECREF(tup); atypinfo = PyPgTypeInfo(Py_TYPE(self)); element_type = PyPgType_GetElementType(Py_TYPE(self)); typinfo = PyPgTypeInfo(element_type); /* * Single dimenion array? Get an element. */ PG_TRY(); { Datum rd; bool isnull = false; rd = array_ref(at, nindexes, indexes, atypinfo->typlen, typinfo->typlen, typinfo->typbyval, typinfo->typalign, &isnull); if (isnull) { rob = Py_None; Py_INCREF(rob); } else { /* * It points into the array structure, so there's no need to free. */ rob = PyPgObject_New(element_type, rd); } } PG_CATCH(); { PyErr_SetPgError(false); } PG_END_TRY(); return(rob); }
static PyObj obj_new(PyTypeObject *subtype, PyObj args, PyObj kw) { static char *words[] = {"source", "mod", NULL}; PyObj src = NULL, mod = NULL, rob = NULL, typmodin_ob = NULL; Datum d; bool isnull = true; int32 typmod = -1; if (DB_IS_NOT_READY()) return(NULL); /* * The type *must* be a PyPgType instance. * Use CheckExact for speed. * Subclassing PyPgType_Type is not supported. */ if (PyPgObjectType_Require(subtype)) return(NULL); /* * Grab a single "source" argument and an * optional "mod" from the args and kw. */ if (!PyArg_ParseTupleAndKeywords(args, kw, "O|O", words, &src, &mod)) return(NULL); if (mod != NULL && mod != Py_None) { PyObj lo; if (!PyList_CheckExact(mod)) { lo = Py_Call((PyObj) &PyList_Type, mod); if (lo == NULL) { PyErr_SetString(PyExc_ValueError, "'mod' keyword must be a sequence"); return(NULL); } } else { Py_INCREF(mod); lo = mod; } typmodin_ob = Py_Call(PyPg_cstring_Array_Type, lo); Py_DECREF(lo); if (typmodin_ob == NULL) { PyErr_SetString(PyExc_ValueError, "invalid typmod object"); return(NULL); } } else if (Py_TYPE(src) == subtype) { /* * Exact type and no typmod. */ rob = src; Py_INCREF(rob); return(rob); } PG_TRY(); { if (typmodin_ob != NULL) { typmod = PyPgType_modin((PyObj) subtype, typmodin_ob); } if (src == Py_None) { d = 0; isnull = true; } else { PyPgType_DatumNew((PyObj) subtype, src, typmod, &d, &isnull); } if (isnull) { rob = Py_None; Py_INCREF(rob); } else { rob = PyPgObject_New(subtype, d); if (PyPgType_ShouldFree(subtype)) { Datum fd = d; d = 0; isnull = true; /* * If it fails to pfree, don't try it again in * the catch. */ pfree(DatumGetPointer(fd)); } } } PG_CATCH(); { Py_XDECREF(rob); rob = NULL; if (!isnull && PyPgType_ShouldFree(subtype)) pfree(DatumGetPointer(d)); PyErr_SetPgError(false); } PG_END_TRY(); Py_XDECREF(typmodin_ob); return(rob); }
/* * Array.get_element(indexes) - Get an element from the array. * * This uses Python sequence semantics(zero-based indexes, IndexError's). */ static PyObj array_get_element(PyObj self, PyObj indexes_ob) { PyObj tup, element_type, rob = NULL; PyPgTypeInfo atypinfo, typinfo; ArrayType *at; int i, nindexes, indexes[MAXDIM] = {0,}; /* * Convert the indexes_ob into a tuple and extract the values * into the indexes[] array. Do any necessary checks along the way. */ tup = Py_Call((PyObj) &PyTuple_Type, indexes_ob); if (tup == NULL) return(NULL); nindexes = (int) PyTuple_GET_SIZE(tup); if (!(nindexes > 0)) { Py_DECREF(tup); PyErr_SetString(PyExc_ValueError, "empty index tuple"); return(NULL); } at = DatumGetArrayTypeP(PyPgObject_GetDatum(self)); Assert(at != NULL); if (nindexes != ARR_NDIM(at)) { Py_DECREF(tup); if (ARR_NDIM(at) == 0) PyErr_SetString(PyExc_IndexError, "no elements in array"); else PyErr_Format(PyExc_ValueError, "element access requires exactly %d indexes, given %d", ARR_NDIM(at), nindexes); return(NULL); } for (i = 0; i < nindexes; ++i) { int index; index = (int) PyNumber_AsSsize_t(PyTuple_GET_ITEM(tup, i), NULL); if (PyErr_Occurred()) { Py_DECREF(tup); return(NULL); } /* * Adjust for backwards based access. (feature of get_element) */ if (index < 0) indexes[i] = index + ARR_DIMS(at)[i]; else indexes[i] = index; if (indexes[i] >= ARR_DIMS(at)[i] || indexes[i] < 0) { PyErr_Format(PyExc_IndexError, "index %d out of range %d for axis %d", index, ARR_DIMS(at)[0], i); Py_DECREF(tup); return(NULL); } /* * Adjust by the lowerbounds.. */ indexes[i] = indexes[i] + ARR_LBOUND(at)[i]; } Py_DECREF(tup); atypinfo = PyPgTypeInfo(Py_TYPE(self)); element_type = PyPgType_GetElementType(Py_TYPE(self)); typinfo = PyPgTypeInfo(element_type); PG_TRY(); { Datum rd; bool isnull = false; rd = array_ref(at, nindexes, indexes, atypinfo->typlen, typinfo->typlen, typinfo->typbyval, typinfo->typalign, &isnull); if (isnull) { rob = Py_None; Py_INCREF(rob); } else { /* * It points into the array structure, so there's no need to free. */ rob = PyPgObject_New(element_type, rd); } } PG_CATCH(); { PyErr_SetPgError(false); } PG_END_TRY(); return(rob); }
static PyObj tupd_item(PyObj self, Py_ssize_t i) { volatile PyObj rob = NULL; TupleDesc td; HeapTuple ht; Form_pg_attribute att; MemoryContext former = CurrentMemoryContext; td = PyPgTupleDesc_GetTupleDesc(self); if (i >= td->natts || i < 0) { PyErr_Format(PyExc_IndexError, "Postgres.TupleDesc index(%d) is out of range %d", i, td->natts); return(NULL); } i = PyPgTupleDesc_GetAttributeIndex(self, i); att = td->attrs[i]; PG_TRY(); { int initd_natts = 0; Datum pg_att_datums[Natts_pg_attribute]; bool pg_att_nulls[Natts_pg_attribute] = {false,}; #ifdef Anum_pg_attribute_attacl pg_att_nulls[Anum_pg_attribute_attacl-1] = true; #endif #ifdef Anum_pg_attribute_attoptions pg_att_nulls[Anum_pg_attribute_attoptions-1] = true; #endif #ifdef Anum_pg_attribute_attfdwoptions pg_att_nulls[Anum_pg_attribute_attfdwoptions-1] = true; #endif /* * XXX: Need a better way to construct a pg_attribute Datum. */ #define FIELD(NAME, NUM, DATUMIZER) \ pg_att_datums[NUM-1] = DATUMIZER(att->NAME); initd_natts = initd_natts + 1; FormData_pg_attribute_Fields(); #undef FIELD if (initd_natts != Natts_pg_attribute) elog(ERROR, "failed to initialize all pg_attribute fields"); ht = heap_form_tuple(PyPgType_GetTupleDesc(PyPg_pg_attribute_Type), pg_att_datums, pg_att_nulls); MemoryContextSwitchTo(PythonMemoryContext); rob = PyPgObject_FromPyPgTypeAndHeapTuple(PyPg_pg_attribute_Type, ht); MemoryContextSwitchTo(former); heap_freetuple(ht); } PG_CATCH(); { MemoryContextSwitchTo(former); Py_XDECREF(rob); rob = NULL; PyErr_SetPgError(false); } PG_END_TRY(); return(rob); }
static PyObj statement_first(PyObj self, PyObj args, PyObj kw) { MemoryContext former = CurrentMemoryContext; PyObj c, rob = NULL; if (resolve_parameters(self, &args, &kw)) return(NULL); if (DB_IS_NOT_READY()) return(NULL); if (PyPgStatement_ReturnsRows(self)) { c = PyPgCursor_New(self, args, kw, CUR_ROWS(1)); if (c != NULL) { PyObj r; r = PyIter_Next(c); if (!PyErr_Occurred() && r == NULL) { r = Py_None; Py_INCREF(r); } if (PyPgCursor_Close(c)) { Py_DECREF(c); Py_XDECREF(r); return(NULL); } if (r != NULL) { if (r == Py_None) rob = r; else { Py_ssize_t s = PySequence_Size(r); if (s == -1) rob = NULL; else if (s == 1) { rob = PySequence_GetItem(r, 0); Py_DECREF(r); } else { /* * It has multiple columns, so return the first row. */ rob = r; } } } Py_DECREF(c); } } else { SPIPlanPtr plan; PyObj tdo = PyPgStatement_GetInput(self); TupleDesc td = PyPgTupleDesc_GetTupleDesc(tdo); PyObj pargs; plan = PyPgStatement_GetPlan(self); if (plan == NULL) return(NULL); pargs = Py_NormalizeRow( PyPgTupleDesc_GetNatts(tdo), td, PyPgTupleDesc_GetNameMap(tdo), args ); if (pargs == NULL) return(NULL); PG_TRY(); { int r; Datum *datums; bool *nulls; char *cnulls; int *freemap = PyPgTupleDesc_GetFreeMap(tdo); datums = palloc(sizeof(Datum) * td->natts); nulls = palloc(sizeof(bool) * td->natts); cnulls = palloc(sizeof(char) * td->natts); Py_BuildDatumsAndNulls( td, PyPgTupleDesc_GetTypesTuple(tdo), pargs, datums, nulls ); for (r = 0; r < td->natts; ++r) { cnulls[r] = nulls[r] ? 'n' : ' '; } r = SPI_execute_plan(plan, datums, cnulls, PL_FN_READONLY(), 1); if (r < 0) raise_spi_error(r); rob = PyLong_FromUnsignedLong(SPI_processed); FreeDatumsAndNulls(freemap, datums, nulls); pfree(cnulls); } PG_CATCH(); { PyErr_SetPgError(false); Py_XDECREF(rob); rob = NULL; } PG_END_TRY(); Py_DECREF(pargs); } MemoryContextSwitchTo(former); return(rob); }