Commit f469f634 authored by Tom Lane's avatar Tom Lane

Fix plpython crash when returning string representation of a RECORD result.

PLyString_ToComposite() blithely overwrote proc->result.out.d, even though
for a composite result type the other union variant proc->result.out.r is
the one that should be valid.  This could result in a crash if out.r had
in fact been filled in (proc->result.is_rowtype == 1) and then somebody
later attempted to use that data; as per bug #13579 from Paweł Michalak.

Just to add insult to injury, it didn't work for RECORD results anyway,
because record_in() would refuse the case.

Fix by doing the I/O function lookup in a local PLyTypeInfo variable,
as we were doing already in PLyObject_ToComposite().  This is not a great
technique because any fn_extra data allocated by the input function will
be leaked permanently (thanks to using TopMemoryContext as fn_mcxt).
But that's a pre-existing issue that is much less serious than a crash,
so leave it to be fixed separately.

This bug would be a potential security issue, except that plpython is
only available to superusers and the crash requires coding the function
in a way that didn't work before today's patches.

Add regression test cases covering all the supported methods of converting
composite results.

Back-patch to 9.1 where the faulty coding was introduced.
parent 09b3d272
...@@ -65,6 +65,8 @@ elif typ == 'obj': ...@@ -65,6 +65,8 @@ elif typ == 'obj':
type_record.first = first type_record.first = first
type_record.second = second type_record.second = second
return type_record return type_record
elif typ == 'str':
return "('%s',%r)" % (first, second)
$$ LANGUAGE plpythonu; $$ LANGUAGE plpythonu;
SELECT * FROM multiout_record_as('dict', 'foo', 1, 'f'); SELECT * FROM multiout_record_as('dict', 'foo', 1, 'f');
first | second first | second
...@@ -78,6 +80,138 @@ SELECT multiout_record_as('dict', 'foo', 1, 'f'); ...@@ -78,6 +80,138 @@ SELECT multiout_record_as('dict', 'foo', 1, 'f');
(foo,1) (foo,1)
(1 row) (1 row)
SELECT * FROM multiout_record_as('dict', null, null, false);
first | second
-------+--------
|
(1 row)
SELECT * FROM multiout_record_as('dict', 'one', null, false);
first | second
-------+--------
one |
(1 row)
SELECT * FROM multiout_record_as('dict', null, 2, false);
first | second
-------+--------
| 2
(1 row)
SELECT * FROM multiout_record_as('dict', 'three', 3, false);
first | second
-------+--------
three | 3
(1 row)
SELECT * FROM multiout_record_as('dict', null, null, true);
first | second
-------+--------
|
(1 row)
SELECT * FROM multiout_record_as('tuple', null, null, false);
first | second
-------+--------
|
(1 row)
SELECT * FROM multiout_record_as('tuple', 'one', null, false);
first | second
-------+--------
one |
(1 row)
SELECT * FROM multiout_record_as('tuple', null, 2, false);
first | second
-------+--------
| 2
(1 row)
SELECT * FROM multiout_record_as('tuple', 'three', 3, false);
first | second
-------+--------
three | 3
(1 row)
SELECT * FROM multiout_record_as('tuple', null, null, true);
first | second
-------+--------
|
(1 row)
SELECT * FROM multiout_record_as('list', null, null, false);
first | second
-------+--------
|
(1 row)
SELECT * FROM multiout_record_as('list', 'one', null, false);
first | second
-------+--------
one |
(1 row)
SELECT * FROM multiout_record_as('list', null, 2, false);
first | second
-------+--------
| 2
(1 row)
SELECT * FROM multiout_record_as('list', 'three', 3, false);
first | second
-------+--------
three | 3
(1 row)
SELECT * FROM multiout_record_as('list', null, null, true);
first | second
-------+--------
|
(1 row)
SELECT * FROM multiout_record_as('obj', null, null, false);
first | second
-------+--------
|
(1 row)
SELECT * FROM multiout_record_as('obj', 'one', null, false);
first | second
-------+--------
one |
(1 row)
SELECT * FROM multiout_record_as('obj', null, 2, false);
first | second
-------+--------
| 2
(1 row)
SELECT * FROM multiout_record_as('obj', 'three', 3, false);
first | second
-------+--------
three | 3
(1 row)
SELECT * FROM multiout_record_as('obj', null, null, true);
first | second
-------+--------
|
(1 row)
SELECT * FROM multiout_record_as('str', 'one', 1, false);
first | second
-------+--------
'one' | 1
(1 row)
SELECT * FROM multiout_record_as('str', 'one', 2, false);
first | second
-------+--------
'one' | 2
(1 row)
SELECT *, s IS NULL AS snull FROM multiout_record_as('tuple', 'xxx', NULL, 'f') AS f(f, s); SELECT *, s IS NULL AS snull FROM multiout_record_as('tuple', 'xxx', NULL, 'f') AS f(f, s);
f | s | snull f | s | snull
-----+---+------- -----+---+-------
...@@ -179,10 +313,14 @@ elif typ == 'dict': ...@@ -179,10 +313,14 @@ elif typ == 'dict':
d = {'first': n * 2, 'second': n * 3, 'extra': 'not important'} d = {'first': n * 2, 'second': n * 3, 'extra': 'not important'}
elif typ == 'tuple': elif typ == 'tuple':
d = (n * 2, n * 3) d = (n * 2, n * 3)
elif typ == 'list':
d = [ n * 2, n * 3 ]
elif typ == 'obj': elif typ == 'obj':
class d: pass class d: pass
d.first = n * 2 d.first = n * 2
d.second = n * 3 d.second = n * 3
elif typ == 'str':
d = "(%r,%r)" % (n * 2, n * 3)
for i in range(n): for i in range(n):
yield (i, d) yield (i, d)
$$ LANGUAGE plpythonu; $$ LANGUAGE plpythonu;
...@@ -200,6 +338,18 @@ SELECT * FROM multiout_table_type_setof('dict', 'f', 3); ...@@ -200,6 +338,18 @@ SELECT * FROM multiout_table_type_setof('dict', 'f', 3);
2 | (6,9) 2 | (6,9)
(3 rows) (3 rows)
SELECT * FROM multiout_table_type_setof('dict', 'f', 7);
n | column2
---+---------
0 | (14,21)
1 | (14,21)
2 | (14,21)
3 | (14,21)
4 | (14,21)
5 | (14,21)
6 | (14,21)
(7 rows)
SELECT * FROM multiout_table_type_setof('tuple', 'f', 2); SELECT * FROM multiout_table_type_setof('tuple', 'f', 2);
n | column2 n | column2
---+--------- ---+---------
...@@ -207,6 +357,29 @@ SELECT * FROM multiout_table_type_setof('tuple', 'f', 2); ...@@ -207,6 +357,29 @@ SELECT * FROM multiout_table_type_setof('tuple', 'f', 2);
1 | (4,6) 1 | (4,6)
(2 rows) (2 rows)
SELECT * FROM multiout_table_type_setof('tuple', 'f', 3);
n | column2
---+---------
0 | (6,9)
1 | (6,9)
2 | (6,9)
(3 rows)
SELECT * FROM multiout_table_type_setof('list', 'f', 2);
n | column2
---+---------
0 | (4,6)
1 | (4,6)
(2 rows)
SELECT * FROM multiout_table_type_setof('list', 'f', 3);
n | column2
---+---------
0 | (6,9)
1 | (6,9)
2 | (6,9)
(3 rows)
SELECT * FROM multiout_table_type_setof('obj', 'f', 4); SELECT * FROM multiout_table_type_setof('obj', 'f', 4);
n | column2 n | column2
---+--------- ---+---------
...@@ -216,6 +389,39 @@ SELECT * FROM multiout_table_type_setof('obj', 'f', 4); ...@@ -216,6 +389,39 @@ SELECT * FROM multiout_table_type_setof('obj', 'f', 4);
3 | (8,12) 3 | (8,12)
(4 rows) (4 rows)
SELECT * FROM multiout_table_type_setof('obj', 'f', 5);
n | column2
---+---------
0 | (10,15)
1 | (10,15)
2 | (10,15)
3 | (10,15)
4 | (10,15)
(5 rows)
SELECT * FROM multiout_table_type_setof('str', 'f', 6);
n | column2
---+---------
0 | (12,18)
1 | (12,18)
2 | (12,18)
3 | (12,18)
4 | (12,18)
5 | (12,18)
(6 rows)
SELECT * FROM multiout_table_type_setof('str', 'f', 7);
n | column2
---+---------
0 | (14,21)
1 | (14,21)
2 | (14,21)
3 | (14,21)
4 | (14,21)
5 | (14,21)
6 | (14,21)
(7 rows)
SELECT * FROM multiout_table_type_setof('dict', 't', 3); SELECT * FROM multiout_table_type_setof('dict', 't', 3);
n | column2 n | column2
---+--------- ---+---------
......
...@@ -912,20 +912,30 @@ PLySequence_ToArray(PLyObToDatum *arg, int32 typmod, PyObject *plrv) ...@@ -912,20 +912,30 @@ PLySequence_ToArray(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
static Datum static Datum
PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string) PLyString_ToComposite(PLyTypeInfo *info, TupleDesc desc, PyObject *string)
{ {
Datum result;
HeapTuple typeTup; HeapTuple typeTup;
PLyTypeInfo locinfo;
PLyExecutionContext *exec_ctx = PLy_current_execution_context(); PLyExecutionContext *exec_ctx = PLy_current_execution_context();
/* Create a dummy PLyTypeInfo */
MemSet(&locinfo, 0, sizeof(PLyTypeInfo));
PLy_typeinfo_init(&locinfo);
typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(desc->tdtypeid)); typeTup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(desc->tdtypeid));
if (!HeapTupleIsValid(typeTup)) if (!HeapTupleIsValid(typeTup))
elog(ERROR, "cache lookup failed for type %u", desc->tdtypeid); elog(ERROR, "cache lookup failed for type %u", desc->tdtypeid);
PLy_output_datum_func2(&info->out.d, typeTup, PLy_output_datum_func2(&locinfo.out.d, typeTup,
exec_ctx->curr_proc->langid, exec_ctx->curr_proc->langid,
exec_ctx->curr_proc->trftypes); exec_ctx->curr_proc->trftypes);
ReleaseSysCache(typeTup); ReleaseSysCache(typeTup);
return PLyObject_ToDatum(&info->out.d, info->out.d.typmod, string); result = PLyObject_ToDatum(&locinfo.out.d, desc->tdtypmod, string);
PLy_typeinfo_dealloc(&locinfo);
return result;
} }
......
...@@ -32,10 +32,40 @@ elif typ == 'obj': ...@@ -32,10 +32,40 @@ elif typ == 'obj':
type_record.first = first type_record.first = first
type_record.second = second type_record.second = second
return type_record return type_record
elif typ == 'str':
return "('%s',%r)" % (first, second)
$$ LANGUAGE plpythonu; $$ LANGUAGE plpythonu;
SELECT * FROM multiout_record_as('dict', 'foo', 1, 'f'); SELECT * FROM multiout_record_as('dict', 'foo', 1, 'f');
SELECT multiout_record_as('dict', 'foo', 1, 'f'); SELECT multiout_record_as('dict', 'foo', 1, 'f');
SELECT * FROM multiout_record_as('dict', null, null, false);
SELECT * FROM multiout_record_as('dict', 'one', null, false);
SELECT * FROM multiout_record_as('dict', null, 2, false);
SELECT * FROM multiout_record_as('dict', 'three', 3, false);
SELECT * FROM multiout_record_as('dict', null, null, true);
SELECT * FROM multiout_record_as('tuple', null, null, false);
SELECT * FROM multiout_record_as('tuple', 'one', null, false);
SELECT * FROM multiout_record_as('tuple', null, 2, false);
SELECT * FROM multiout_record_as('tuple', 'three', 3, false);
SELECT * FROM multiout_record_as('tuple', null, null, true);
SELECT * FROM multiout_record_as('list', null, null, false);
SELECT * FROM multiout_record_as('list', 'one', null, false);
SELECT * FROM multiout_record_as('list', null, 2, false);
SELECT * FROM multiout_record_as('list', 'three', 3, false);
SELECT * FROM multiout_record_as('list', null, null, true);
SELECT * FROM multiout_record_as('obj', null, null, false);
SELECT * FROM multiout_record_as('obj', 'one', null, false);
SELECT * FROM multiout_record_as('obj', null, 2, false);
SELECT * FROM multiout_record_as('obj', 'three', 3, false);
SELECT * FROM multiout_record_as('obj', null, null, true);
SELECT * FROM multiout_record_as('str', 'one', 1, false);
SELECT * FROM multiout_record_as('str', 'one', 2, false);
SELECT *, s IS NULL AS snull FROM multiout_record_as('tuple', 'xxx', NULL, 'f') AS f(f, s); SELECT *, s IS NULL AS snull FROM multiout_record_as('tuple', 'xxx', NULL, 'f') AS f(f, s);
SELECT *, f IS NULL AS fnull, s IS NULL AS snull FROM multiout_record_as('tuple', 'xxx', 1, 't') AS f(f, s); SELECT *, f IS NULL AS fnull, s IS NULL AS snull FROM multiout_record_as('tuple', 'xxx', 1, 't') AS f(f, s);
SELECT * FROM multiout_record_as('obj', NULL, 10, 'f'); SELECT * FROM multiout_record_as('obj', NULL, 10, 'f');
...@@ -92,18 +122,29 @@ elif typ == 'dict': ...@@ -92,18 +122,29 @@ elif typ == 'dict':
d = {'first': n * 2, 'second': n * 3, 'extra': 'not important'} d = {'first': n * 2, 'second': n * 3, 'extra': 'not important'}
elif typ == 'tuple': elif typ == 'tuple':
d = (n * 2, n * 3) d = (n * 2, n * 3)
elif typ == 'list':
d = [ n * 2, n * 3 ]
elif typ == 'obj': elif typ == 'obj':
class d: pass class d: pass
d.first = n * 2 d.first = n * 2
d.second = n * 3 d.second = n * 3
elif typ == 'str':
d = "(%r,%r)" % (n * 2, n * 3)
for i in range(n): for i in range(n):
yield (i, d) yield (i, d)
$$ LANGUAGE plpythonu; $$ LANGUAGE plpythonu;
SELECT * FROM multiout_composite(2); SELECT * FROM multiout_composite(2);
SELECT * FROM multiout_table_type_setof('dict', 'f', 3); SELECT * FROM multiout_table_type_setof('dict', 'f', 3);
SELECT * FROM multiout_table_type_setof('dict', 'f', 7);
SELECT * FROM multiout_table_type_setof('tuple', 'f', 2); SELECT * FROM multiout_table_type_setof('tuple', 'f', 2);
SELECT * FROM multiout_table_type_setof('tuple', 'f', 3);
SELECT * FROM multiout_table_type_setof('list', 'f', 2);
SELECT * FROM multiout_table_type_setof('list', 'f', 3);
SELECT * FROM multiout_table_type_setof('obj', 'f', 4); SELECT * FROM multiout_table_type_setof('obj', 'f', 4);
SELECT * FROM multiout_table_type_setof('obj', 'f', 5);
SELECT * FROM multiout_table_type_setof('str', 'f', 6);
SELECT * FROM multiout_table_type_setof('str', 'f', 7);
SELECT * FROM multiout_table_type_setof('dict', 't', 3); SELECT * FROM multiout_table_type_setof('dict', 't', 3);
-- check what happens if a type changes under us -- check what happens if a type changes under us
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment