type_record.first = first
type_record.second = second
return type_record
+elif typ == 'str':
+ return "('%s',%r)" % (first, second)
$$ LANGUAGE plpythonu;
CREATE FUNCTION test_in_out_params(first in text, second out text) AS $$
return first + '_in_to_out';
|
(1 row)
+SELECT * FROM test_type_record_as('str', 'one', 1, false);
+ first | second
+-------+--------
+ 'one' | 1
+(1 row)
+
SELECT * FROM test_in_out_params('test_in');
second
-------------------
HINT: To return null in a column, let the returned object have an attribute named after column with value None.
CONTEXT: while creating return value
PL/Python function "test_type_record_error3"
+CREATE FUNCTION test_type_record_error4() RETURNS type_record AS $$
+ return 'foo'
+$$ LANGUAGE plpythonu;
+SELECT * FROM test_type_record_error4();
+ERROR: malformed record literal: "foo"
+DETAIL: Missing left parenthesis.
+CONTEXT: while creating return value
+PL/Python function "test_type_record_error4"
(3,f) | (7,t)
(1 row)
+-- triggers with composite type columns (bug #6559)
+CREATE TABLE composite_trigger_noop_test (f1 comp1, f2 comp2);
+CREATE FUNCTION composite_trigger_noop_f() RETURNS trigger AS $$
+ return 'MODIFY'
+$$ LANGUAGE plpythonu;
+CREATE TRIGGER composite_trigger_noop BEFORE INSERT ON composite_trigger_noop_test
+ FOR EACH ROW EXECUTE PROCEDURE composite_trigger_noop_f();
+INSERT INTO composite_trigger_noop_test VALUES (NULL, NULL);
+INSERT INTO composite_trigger_noop_test VALUES (ROW(1, 'f'), NULL);
+INSERT INTO composite_trigger_noop_test VALUES (ROW(NULL, 't'), ROW(1, 'f'));
+SELECT * FROM composite_trigger_noop_test;
+ f1 | f2
+-------+-------
+ |
+ (1,f) |
+ (,t) | (1,f)
+(3 rows)
+
+-- nested composite types
+CREATE TYPE comp3 AS (c1 comp1, c2 comp2, m integer);
+CREATE TABLE composite_trigger_nested_test(c comp3);
+CREATE FUNCTION composite_trigger_nested_f() RETURNS trigger AS $$
+ return 'MODIFY'
+$$ LANGUAGE plpythonu;
+CREATE TRIGGER composite_trigger_nested BEFORE INSERT ON composite_trigger_nested_test
+ FOR EACH ROW EXECUTE PROCEDURE composite_trigger_nested_f();
+INSERT INTO composite_trigger_nested_test VALUES (NULL);
+INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(1, 'f'), NULL, 3));
+INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(NULL, 't'), ROW(1, 'f'), NULL));
+SELECT * FROM composite_trigger_nested_test;
+ c
+-------------------
+
+ ("(1,f)",,3)
+ ("(,t)","(1,f)",)
+(3 rows)
+
}
else if (proc->result.is_rowtype >= 1)
{
- TupleDesc desc;
- HeapTuple tuple = NULL;
+ TupleDesc desc;
/* make sure it's not an unnamed record */
Assert((proc->result.out.d.typoid == RECORDOID &&
desc = lookup_rowtype_tupdesc(proc->result.out.d.typoid,
proc->result.out.d.typmod);
- tuple = PLyObject_ToTuple(&proc->result, desc, plrv);
-
- if (tuple != NULL)
- {
- fcinfo->isnull = false;
- rv = HeapTupleGetDatum(tuple);
- }
- else
- {
- fcinfo->isnull = true;
- rv = (Datum) NULL;
- }
+ rv = PLyObject_ToCompositeDatum(&proc->result, desc, plrv);
+ fcinfo->isnull = (rv == (Datum) NULL);
}
else
{
static Datum PLyObject_ToDatum(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
static Datum PLySequence_ToArray(PLyObToDatum *arg, int32 typmod, PyObject *plrv);
-/* conversion from Python objects to heap tuples (used by triggers and SRFs) */
-static HeapTuple PLyMapping_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping);
-static HeapTuple PLySequence_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence);
-static HeapTuple PLyGenericObject_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *object);
+/* conversion from Python objects to composite Datums (used by triggers and SRFs) */
+static Datum PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string);
+static Datum PLyMapping_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping);
+static Datum PLySequence_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence);
+static Datum PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object);
/* make allocations in the TopMemoryContext */
static void perm_fmgr_info(Oid functionId, FmgrInfo *finfo);
}
/*
- * Convert a Python object to a PostgreSQL tuple, using all supported
- * conversion methods: tuple as a sequence, as a mapping or as an object that
- * has __getattr__ support.
+ * Convert a Python object to a composite Datum, using all supported
+ * conversion methods: composite as a string, as a sequence, as a mapping or
+ * as an object that has __getattr__ support.
*/
-HeapTuple
-PLyObject_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv)
+Datum
+PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv)
{
- HeapTuple tuple;
+ Datum datum;
- if (PySequence_Check(plrv))
+ if (PyString_Check(plrv) || PyUnicode_Check(plrv))
+ datum = PLyString_ToComposite(info, desc, plrv);
+ else if (PySequence_Check(plrv))
/* composite type as sequence (tuple, list etc) */
- tuple = PLySequence_ToTuple(info, desc, plrv);
+ datum = PLySequence_ToComposite(info, desc, plrv);
else if (PyMapping_Check(plrv))
/* composite type as mapping (currently only dict) */
- tuple = PLyMapping_ToTuple(info, desc, plrv);
+ datum = PLyMapping_ToComposite(info, desc, plrv);
else
/* returned as smth, must provide method __getattr__(name) */
- tuple = PLyGenericObject_ToTuple(info, desc, plrv);
+ datum = PLyGenericObject_ToComposite(info, desc, plrv);
- return tuple;
+ return datum;
}
static void
static Datum
PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
{
- HeapTuple tuple = NULL;
Datum rv;
PLyTypeInfo info;
TupleDesc desc;
* that info instead of looking it up every time a tuple is returned from
* the function.
*/
- tuple = PLyObject_ToTuple(&info, desc, plrv);
+ rv = PLyObject_ToCompositeDatum(&info, desc, plrv);
PLy_typeinfo_dealloc(&info);
- if (tuple != NULL)
- rv = HeapTupleGetDatum(tuple);
- else
- rv = (Datum) NULL;
-
return rv;
}
return PointerGetDatum(array);
}
-static HeapTuple
-PLyMapping_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping)
+
+static Datum
+PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string)
+{
+ HeapTuple typeTup;
+
+ typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(desc->tdtypeid));
+ if (!HeapTupleIsValid(typeTup))
+ elog(ERROR, "cache lookup failed for type %u", desc->tdtypeid);
+
+ PLy_output_datum_func2(&info->out.d, typeTup);
+
+ ReleaseSysCache(typeTup);
+ ReleaseTupleDesc(desc);
+
+ return PLyObject_ToDatum(&info->out.d, info->out.d.typmod, string);
+}
+
+
+static Datum
+PLyMapping_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping)
{
HeapTuple tuple;
Datum *values;
pfree(values);
pfree(nulls);
- return tuple;
+ return HeapTupleGetDatum(tuple);
}
-static HeapTuple
-PLySequence_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence)
+static Datum
+PLySequence_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence)
{
HeapTuple tuple;
Datum *values;
pfree(values);
pfree(nulls);
- return tuple;
+ return HeapTupleGetDatum(tuple);
}
-static HeapTuple
-PLyGenericObject_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *object)
+static Datum
+PLyGenericObject_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *object)
{
HeapTuple tuple;
Datum *values;
pfree(values);
pfree(nulls);
- return tuple;
+ return HeapTupleGetDatum(tuple);
}
/*
extern void PLy_output_record_funcs(PLyTypeInfo *arg, TupleDesc desc);
-/* conversion from Python objects to heap tuples */
-extern HeapTuple PLyObject_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv);
+/* conversion from Python objects to composite Datums */
+extern Datum PLyObject_ToCompositeDatum(PLyTypeInfo *info, TupleDesc desc, PyObject *plrv);
/* conversion from heap tuples to Python dictionaries */
extern PyObject *PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc);
type_record.first = first
type_record.second = second
return type_record
+elif typ == 'str':
+ return "('%s',%r)" % (first, second)
$$ LANGUAGE plpythonu;
CREATE FUNCTION test_in_out_params(first in text, second out text) AS $$
SELECT * FROM test_type_record_as('obj', 'three', 3, false);
SELECT * FROM test_type_record_as('obj', null, null, true);
+SELECT * FROM test_type_record_as('str', 'one', 1, false);
+
SELECT * FROM test_in_out_params('test_in');
SELECT * FROM test_in_out_params_multi('test_in');
SELECT * FROM test_inout_params('test_in');
$$ LANGUAGE plpythonu;
SELECT * FROM test_type_record_error3();
+
+CREATE FUNCTION test_type_record_error4() RETURNS type_record AS $$
+ return 'foo'
+$$ LANGUAGE plpythonu;
+
+SELECT * FROM test_type_record_error4();
INSERT INTO composite_trigger_test VALUES (NULL, NULL);
SELECT * FROM composite_trigger_test;
+
+
+-- triggers with composite type columns (bug #6559)
+
+CREATE TABLE composite_trigger_noop_test (f1 comp1, f2 comp2);
+
+CREATE FUNCTION composite_trigger_noop_f() RETURNS trigger AS $$
+ return 'MODIFY'
+$$ LANGUAGE plpythonu;
+
+CREATE TRIGGER composite_trigger_noop BEFORE INSERT ON composite_trigger_noop_test
+ FOR EACH ROW EXECUTE PROCEDURE composite_trigger_noop_f();
+
+INSERT INTO composite_trigger_noop_test VALUES (NULL, NULL);
+INSERT INTO composite_trigger_noop_test VALUES (ROW(1, 'f'), NULL);
+INSERT INTO composite_trigger_noop_test VALUES (ROW(NULL, 't'), ROW(1, 'f'));
+SELECT * FROM composite_trigger_noop_test;
+
+
+-- nested composite types
+
+CREATE TYPE comp3 AS (c1 comp1, c2 comp2, m integer);
+
+CREATE TABLE composite_trigger_nested_test(c comp3);
+
+CREATE FUNCTION composite_trigger_nested_f() RETURNS trigger AS $$
+ return 'MODIFY'
+$$ LANGUAGE plpythonu;
+
+CREATE TRIGGER composite_trigger_nested BEFORE INSERT ON composite_trigger_nested_test
+ FOR EACH ROW EXECUTE PROCEDURE composite_trigger_nested_f();
+
+INSERT INTO composite_trigger_nested_test VALUES (NULL);
+INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(1, 'f'), NULL, 3));
+INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(NULL, 't'), ROW(1, 'f'), NULL));
+SELECT * FROM composite_trigger_nested_test;