static void tupd_dealloc(PyObj self) { MemoryContext former = CurrentMemoryContext; Py_XDECREF(PyPgTupleDesc_GetNames(self)); PyPgTupleDesc_SetNames(self, NULL); Py_XDECREF(PyPgTupleDesc_GetNameMap(self)); PyPgTupleDesc_SetNameMap(self, NULL); Py_XDECREF(PyPgTupleDesc_GetTypesTuple(self)); PyPgTupleDesc_SetTypesTuple(self, NULL); PG_TRY(); { TupleDesc td; int *map; td = PyPgTupleDesc_GetTupleDesc(self); PyPgTupleDesc_SetTupleDesc(self, NULL); if (td != NULL) FreeTupleDesc(td); map = PyPgTupleDesc_GetIndexMap(self); PyPgTupleDesc_SetIndexMap(self, NULL); if (map != NULL) pfree(map); map = PyPgTupleDesc_GetFreeMap(self); PyPgTupleDesc_SetFreeMap(self, NULL); if (map != NULL) pfree(map); /* * When PLPY_STRANGE_THINGS is defined. */ RaiseAStrangeError } PG_CATCH(); { PyErr_EmitPgErrorAsWarning("could not deallocate Postgres.TupleDesc fields"); } PG_END_TRY(); /* * Normally, this doesn't matter, but it is possible for the context * to be switched by the above code.. */ MemoryContextSwitchTo(former); Py_TYPE(self)->tp_free(self); }
/* * The TypesTuple includes dropped attributes, if there are dropped attributes * create and return a tuple of types without dropped attribute types. */ PyObj PyPgTupleDesc_GetTypes(PyObj tdo) { Py_ssize_t i, j, l; PyObj rob, types; types = PyPgTupleDesc_GetTypesTuple(tdo); if (PyTuple_GET_SIZE(types) == PyPgTupleDesc_GetNatts(tdo)) { /* No dropped attributes */ Py_INCREF(types); return(types); } rob = PyTuple_New(PyPgTupleDesc_GetNatts(tdo)); if (rob == NULL) return(NULL); /* * After this point, [python] failure isn't really possible... */ l = PyTuple_GET_SIZE(types); /* * Fill the return tuple with !None objects */ for (i = 0, j = 0; i < l; ++i) { PyObj t = PyTuple_GET_ITEM(types, i); if (t != Py_None) { PyTuple_SET_ITEM(rob, j, t); Py_INCREF(t); ++j; /* * Reached the end of the !dropped attributes? Break out. */ if (j == PyTuple_GET_SIZE(rob)) break; } } Assert(j == PyTuple_GET_SIZE(rob)); return(rob); }
/* * PyPgTupleDesc_IsCurrent - determine if all the column types are current */ bool PyPgTupleDesc_IsCurrent(PyObj tdo) { PyObj types = PyPgTupleDesc_GetTypesTuple(tdo); Py_ssize_t i; for (i = 0; i < PyTuple_GET_SIZE(types); ++i) { PyObj t; t = PyTuple_GET_ITEM(types, i); if (t == Py_None) continue; if (!PyPgType_IsCurrent(t)) return(false); } return(true); }
PyObj PyPgTupleDesc_GetTypeOids(PyObj tdo) { Py_ssize_t i, j, l; PyObj rob, types; types = PyPgTupleDesc_GetTypesTuple(tdo); rob = PyTuple_New(PyPgTupleDesc_GetNatts(tdo)); if (rob == NULL) return(NULL); /* * After this point, [python] failure isn't really possible... */ l = PyTuple_GET_SIZE(types); /* * Fill the return tuple with !None objects */ for (i = 0, j = 0; i < l; ++i) { PyObj t = PyTuple_GET_ITEM(types, i); if (t != Py_None) { PyObj typoid_ob = PyPgType_GetOid_PyLong(t); Py_INCREF(typoid_ob); PyTuple_SET_ITEM(rob, j, typoid_ob); ++j; if (j == PyTuple_GET_SIZE(rob)) break; } } 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); }
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 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); }