static Datum PLySequence_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence) { Datum result; HeapTuple tuple; Datum *values; bool *nulls; volatile int idx; volatile int i; Assert(PySequence_Check(sequence)); /* * Check that sequence length is exactly same as PG tuple's. We actually * can ignore exceeding items or assume missing ones as null but to avoid * plpython developer's errors we are strict here */ idx = 0; for (i = 0; i < desc->natts; i++) { if (!desc->attrs[i]->attisdropped) idx++; } if (PySequence_Length(sequence) != idx) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("length of returned sequence did not match number of columns in row"))); if (info->is_rowtype == 2) PLy_output_tuple_funcs(info, desc); Assert(info->is_rowtype == 1); /* Build tuple */ values = palloc(sizeof(Datum) * desc->natts); nulls = palloc(sizeof(bool) * desc->natts); idx = 0; for (i = 0; i < desc->natts; ++i) { PyObject *volatile value; PLyObToDatum *att; if (desc->attrs[i]->attisdropped) { values[i] = (Datum) 0; nulls[i] = true; continue; } value = NULL; att = &info->out.r.atts[i]; PG_TRY(); { value = PySequence_GetItem(sequence, idx); Assert(value); if (value == Py_None) { values[i] = (Datum) NULL; nulls[i] = true; } else if (value) { values[i] = (att->func) (att, -1, value); nulls[i] = false; } Py_XDECREF(value); value = NULL; } PG_CATCH(); { Py_XDECREF(value); PG_RE_THROW(); } PG_END_TRY(); idx++; } tuple = heap_form_tuple(desc, values, nulls); result = heap_copy_tuple_as_datum(tuple, desc); heap_freetuple(tuple); pfree(values); pfree(nulls); return result; }
static Datum PLyMapping_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping) { Datum result; HeapTuple tuple; Datum *values; bool *nulls; volatile int i; Assert(PyMapping_Check(mapping)); if (info->is_rowtype == 2) PLy_output_tuple_funcs(info, desc); Assert(info->is_rowtype == 1); /* Build tuple */ values = palloc(sizeof(Datum) * desc->natts); nulls = palloc(sizeof(bool) * desc->natts); for (i = 0; i < desc->natts; ++i) { char *key; PyObject *volatile value; PLyObToDatum *att; if (desc->attrs[i]->attisdropped) { values[i] = (Datum) 0; nulls[i] = true; continue; } key = NameStr(desc->attrs[i]->attname); value = NULL; att = &info->out.r.atts[i]; PG_TRY(); { value = PyMapping_GetItemString(mapping, key); if (value == Py_None) { values[i] = (Datum) NULL; nulls[i] = true; } else if (value) { values[i] = (att->func) (att, -1, value); nulls[i] = false; } else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("key \"%s\" not found in mapping", key), errhint("To return null in a column, " "add the value None to the mapping with the key named after the column."))); Py_XDECREF(value); value = NULL; } PG_CATCH(); { Py_XDECREF(value); PG_RE_THROW(); } PG_END_TRY(); } tuple = heap_form_tuple(desc, values, nulls); result = heap_copy_tuple_as_datum(tuple, desc); heap_freetuple(tuple); pfree(values); pfree(nulls); return result; }
static Datum PLyGenericObject_ToComposite(PLyObToDatum *arg, TupleDesc desc, PyObject *object, bool inarray) { Datum result; HeapTuple tuple; Datum *values; bool *nulls; volatile int i; /* Build tuple */ values = palloc(sizeof(Datum) * desc->natts); nulls = palloc(sizeof(bool) * desc->natts); for (i = 0; i < desc->natts; ++i) { char *key; PyObject *volatile value; PLyObToDatum *att; Form_pg_attribute attr = TupleDescAttr(desc, i); if (attr->attisdropped) { values[i] = (Datum) 0; nulls[i] = true; continue; } key = NameStr(attr->attname); value = NULL; att = &arg->u.tuple.atts[i]; PG_TRY(); { value = PyObject_GetAttrString(object, key); if (!value) { /* * No attribute for this column in the object. * * If we are parsing a composite type in an array, a likely * cause is that the function contained something like "[[123, * 'foo']]". Before PostgreSQL 10, that was interpreted as an * array, with a composite type (123, 'foo') in it. But now * it's interpreted as a two-dimensional array, and we try to * interpret "123" as the composite type. See also similar * heuristic in PLyObject_ToScalar(). */ ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("attribute \"%s\" does not exist in Python object", key), inarray ? errhint("To return a composite type in an array, return the composite type as a Python tuple, e.g., \"[('foo',)]\".") : errhint("To return null in a column, let the returned object have an attribute named after column with value None."))); } values[i] = att->func(att, value, &nulls[i], false); Py_XDECREF(value); value = NULL; } PG_CATCH(); { Py_XDECREF(value); PG_RE_THROW(); } PG_END_TRY(); } tuple = heap_form_tuple(desc, values, nulls); result = heap_copy_tuple_as_datum(tuple, desc); heap_freetuple(tuple); pfree(values); pfree(nulls); return result; }
static Datum PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object) { Datum result; HeapTuple tuple; Datum *values; bool *nulls; volatile int i; if (info->is_rowtype == 2) PLy_output_tuple_funcs(info, desc); Assert(info->is_rowtype == 1); /* Build tuple */ values = palloc(sizeof(Datum) * desc->natts); nulls = palloc(sizeof(bool) * desc->natts); for (i = 0; i < desc->natts; ++i) { char *key; PyObject *volatile value; PLyObToDatum *att; if (desc->attrs[i]->attisdropped) { values[i] = (Datum) 0; nulls[i] = true; continue; } key = NameStr(desc->attrs[i]->attname); value = NULL; att = &info->out.r.atts[i]; PG_TRY(); { value = PyObject_GetAttrString(object, key); if (value == Py_None) { values[i] = (Datum) NULL; nulls[i] = true; } else if (value) { values[i] = (att->func) (att, -1, value); nulls[i] = false; } else ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("attribute \"%s\" does not exist in Python object", key), errhint("To return null in a column, " "let the returned object have an attribute named " "after column with value None."))); Py_XDECREF(value); value = NULL; } PG_CATCH(); { Py_XDECREF(value); PG_RE_THROW(); } PG_END_TRY(); } tuple = heap_form_tuple(desc, values, nulls); result = heap_copy_tuple_as_datum(tuple, desc); heap_freetuple(tuple); pfree(values); pfree(nulls); return result; }
static Datum PLyMapping_ToComposite(PLyObToDatum *arg, TupleDesc desc, PyObject *mapping) { Datum result; HeapTuple tuple; Datum *values; bool *nulls; volatile int i; Assert(PyMapping_Check(mapping)); /* Build tuple */ values = palloc(sizeof(Datum) * desc->natts); nulls = palloc(sizeof(bool) * desc->natts); for (i = 0; i < desc->natts; ++i) { char *key; PyObject *volatile value; PLyObToDatum *att; Form_pg_attribute attr = TupleDescAttr(desc, i); if (attr->attisdropped) { values[i] = (Datum) 0; nulls[i] = true; continue; } key = NameStr(attr->attname); value = NULL; att = &arg->u.tuple.atts[i]; PG_TRY(); { value = PyMapping_GetItemString(mapping, key); if (!value) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_COLUMN), errmsg("key \"%s\" not found in mapping", key), errhint("To return null in a column, " "add the value None to the mapping with the key named after the column."))); values[i] = att->func(att, value, &nulls[i], false); Py_XDECREF(value); value = NULL; } PG_CATCH(); { Py_XDECREF(value); PG_RE_THROW(); } PG_END_TRY(); } tuple = heap_form_tuple(desc, values, nulls); result = heap_copy_tuple_as_datum(tuple, desc); heap_freetuple(tuple); pfree(values); pfree(nulls); return result; }