static void array_new_datum(PyObj subtype, PyObj ob, int32 mod, Datum *rdatum, bool *isnull) { ArrayType *at; if (!PyList_CheckExact(ob)) { /* * If it's a string object, it should never get here. */ PyErr_SetString(PyExc_TypeError, "array constructor requires a list or string object"); PyErr_RelayException(); } else { at = array_from_py_list(PyPgType_GetElementType(subtype), ob, mod); *rdatum = PointerGetDatum(at); *isnull = false; } }
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; } } }
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); }