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); }
/* * Very similar to type_get_Element. */ static PyObj array_get_Element_type(PyObj self, void *closure) { PyObj rob; PyPgTypeInfo typinfo = PyPgTypeInfo(Py_TYPE(self)); rob = typinfo->array.x_yes.typelem_Type; /* * The array type shouldn't exist without having an element type. */ Assert(rob != NULL); Py_INCREF(rob); return(rob); }
PyObj PyPgObject_Initialize(PyObj self, Datum d) { PyPgTypeInfo typinfo = PyPgTypeInfo(Py_TYPE(self)); MemoryContext former = CurrentMemoryContext; MemoryContextSwitchTo(PythonMemoryContext); d = Py_datumCopy(d, typinfo->typbyval, typinfo->typlen); MemoryContextSwitchTo(former); if (!typinfo->typbyval && !PointerIsValid(DatumGetPointer(d))) { Py_DECREF(self); return(NULL); } PyPgObject_SetDatum(self, d); return(self); }
static long obj_hash(PyObj self) { PyPgTypeInfo typinfo; Datum ob_datum = PyPgObject_GetDatum(self); long rv = 0; typinfo = PyPgTypeInfo(Py_TYPE(self)); if (typinfo->typbyval) { rv = ((long) ob_datum); } else if (typinfo->typlen > -1 && typinfo->typlen <= sizeof(long)) { rv = (*((long *) DatumGetPointer(ob_datum))); } else { int len; switch(typinfo->typlen) { case -2: len = strlen((char *) DatumGetPointer(ob_datum)); break; case -1: len = VARSIZE(ob_datum) - VARHDRSZ; ob_datum = PointerGetDatum(VARDATA(ob_datum)); break; default: len = (int) typinfo->typlen; break; } rv = hash_any((unsigned char *) DatumGetPointer(ob_datum), len); } return(rv); }
/* * 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); }
/* * 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 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); }
/* * 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_from_py_list - given an element type and a list(), build an array */ static ArrayType * array_from_py_list(PyObj element_type, PyObj listob, int elemmod) { PyPgTypeInfo typinfo = PyPgTypeInfo(element_type); Datum * volatile datums = NULL; bool * volatile nulls = NULL; unsigned int nelems; int i, ndims; int dims[MAXDIM]; int lbs[MAXDIM]; ArrayType *rat = NULL; ndims = py_list_dimensions(listob, dims); Assert(ndims <= MAXDIM); /* * From the dimensions, calculate the expected number of elements. * At this point it is not known if the array is balanced */ nelems = dims[ndims-1]; for (i = ndims-2; i > -1; --i) { unsigned int n = nelems * dims[i]; if (n < nelems) { elog(ERROR, "too many elements for array"); } nelems = n; } if (nelems == 0 && ndims > 1) elog(ERROR, "malformed nesting of list objects"); PG_TRY(); { /* * palloc0 as cleanup in the PG_CATCH() will depend on this. */ nulls = palloc0(sizeof(bool) * nelems); datums = palloc0(sizeof(Datum) * nelems); /* * elog's on failure, this will validate the balance/sizes of the * dimensions. */ fill_elements(element_type, listob, elemmod, nelems, ndims, dims, (Datum *) datums, (bool *) nulls); /* * Arrays built from lists don't support custom lower bounds, * so initialize it to the default '1'. */ for (i = 0; i < ndims; ++i) lbs[i] = 1; /* * 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 (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(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); } PG_RE_THROW(); } PG_END_TRY(); return(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); }