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_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); }
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); }
/* * fill_element - create all the Datums and NULLs using PyPgType_DatumNew * * This is the nasty routine that navigates through the nested lists coercing * the objects into the array's element type. */ static void fill_elements( PyObj element_type, PyObj listob, int mod, unsigned int nelems, int ndims, int *dims, Datum *datums, bool *nulls) { int elements_per = dims[ndims-1]; int position[MAXDIM] = {0,}; PyObj dstack[MAXDIM] = {listob, NULL,}; unsigned int i = 0; /* current, absolute element position */ int j, axis = 0; /* top */ Assert(PyList_GET_SIZE(listob) == dims[0]); /* * The filling of the Datum array ends when the total number of elements * have been processed. datums and nulls *must* be allocated to fit nelems. */ while (i < nelems) { /* * push until we are at element depth. */ while (axis < ndims - 1) { PyObj pushed; ++axis; /* go deeper */ /* * use the position of the previous axis to identify which list will be used * for this one. */ pushed = dstack[axis] = PyList_GET_ITEM(dstack[axis-1], position[axis-1]); position[axis] = 0; /* just started */ /* just consumed position[axis-1], so increment */ position[axis-1] = position[axis-1] + 1; /* * Check the object. */ if (!PyList_CheckExact(pushed)) { /* * Do *not* be nice and instantiate the list because we don't * want to be holding any references. */ PyErr_Format(PyExc_ValueError, "array boundaries must be list objects not '%s'", Py_TYPE(pushed)->tp_name); PyErr_RelayException(); } /* * Make sure it's consistent with the expectations. * * This check never hits the root list object, but that's fine * because the dims[] is derived from the list lengths. */ if (PyList_GET_SIZE(pushed) != dims[axis]) { PyErr_Format(PyExc_ValueError, "cannot make array from unbalanced lists", dims[axis]); PyErr_RelayException(); } } /* * Build the element datums. */ for (j = 0; j < elements_per; ++j) { PyPgType_DatumNew(element_type, PyList_GET_ITEM(dstack[ndims-1], j), mod, &(datums[i]), &(nulls[i])); ++i; } /* * pop it like it's hot * * No need to DECREF anything as PyList_GET_ITEM borrows. * * Also, the root list is never popped, so stop before zero. */ while (axis > 0) { /* * Stop pop'ing when we identify a position that has more lists to * process. */ --axis; if (position[axis] < dims[axis]) break; } } }