Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitba3e415

Browse files
committed
PL/Python: Accept strings in functions returning composite types
Before 9.1, PL/Python functions returning composite types could returna string and it would be parsed using record_in. The 9.1 changes madePL/Python only expect dictionaries, tuples, or objects supportinggetattr as output of composite functions, resulting in a regressionand a confusing error message, as the strings were interpreted assequences and the code for transforming lists to database tuples wasused. Fix this by treating strings separately as before, beforechecking for the other types.The reason why it's important to support string to database tupleconversion is that trigger functions on tables with composite columnsget the composite row passed in as a string (from record_out).Without supporting converting this back using record_in, this makes itimpossible to implement pass-through behavior for these columns, asPL/Python no longer accepts strings for composite values.A better solution would be to fix the code that transforms compositeinputs into Python objects to produce dictionaries that would then becorrectly interpreted by the Python->PostgreSQL counterpart code. Butthat would be too invasive to backpatch to 9.1, and it is too late inthe 9.2 cycle to attempt it. It should be revisited in the future,though.Reported as bug #6559 by Kirill Simonov.Jan Urbański
1 parentcc71cea commitba3e415

File tree

7 files changed

+151
-47
lines changed

7 files changed

+151
-47
lines changed

‎src/pl/plpython/expected/plpython_record.out

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ elif typ == 'obj':
3838
type_record.first = first
3939
type_record.second = second
4040
return type_record
41+
elif typ == 'str':
42+
return "('%s',%r)" % (first, second)
4143
$$ LANGUAGE plpythonu;
4244
CREATE FUNCTION test_in_out_params(first in text, second out text) AS $$
4345
return first + '_in_to_out';
@@ -290,6 +292,12 @@ SELECT * FROM test_type_record_as('obj', null, null, true);
290292
|
291293
(1 row)
292294

295+
SELECT * FROM test_type_record_as('str', 'one', 1, false);
296+
first | second
297+
-------+--------
298+
'one' | 1
299+
(1 row)
300+
293301
SELECT * FROM test_in_out_params('test_in');
294302
second
295303
-------------------
@@ -355,3 +363,11 @@ ERROR: attribute "second" does not exist in Python object
355363
HINT: To return null in a column, let the returned object have an attribute named after column with value None.
356364
CONTEXT: while creating return value
357365
PL/Python function "test_type_record_error3"
366+
CREATE FUNCTION test_type_record_error4() RETURNS type_record AS $$
367+
return 'foo'
368+
$$ LANGUAGE plpythonu;
369+
SELECT * FROM test_type_record_error4();
370+
ERROR: malformed record literal: "foo"
371+
DETAIL: Missing left parenthesis.
372+
CONTEXT: while creating return value
373+
PL/Python function "test_type_record_error4"

‎src/pl/plpython/expected/plpython_trigger.out

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,3 +567,40 @@ SELECT * FROM composite_trigger_test;
567567
(3,f) | (7,t)
568568
(1 row)
569569

570+
-- triggers with composite type columns (bug #6559)
571+
CREATE TABLE composite_trigger_noop_test (f1 comp1, f2 comp2);
572+
CREATE FUNCTION composite_trigger_noop_f() RETURNS trigger AS $$
573+
return 'MODIFY'
574+
$$ LANGUAGE plpythonu;
575+
CREATE TRIGGER composite_trigger_noop BEFORE INSERT ON composite_trigger_noop_test
576+
FOR EACH ROW EXECUTE PROCEDURE composite_trigger_noop_f();
577+
INSERT INTO composite_trigger_noop_test VALUES (NULL, NULL);
578+
INSERT INTO composite_trigger_noop_test VALUES (ROW(1, 'f'), NULL);
579+
INSERT INTO composite_trigger_noop_test VALUES (ROW(NULL, 't'), ROW(1, 'f'));
580+
SELECT * FROM composite_trigger_noop_test;
581+
f1 | f2
582+
-------+-------
583+
|
584+
(1,f) |
585+
(,t) | (1,f)
586+
(3 rows)
587+
588+
-- nested composite types
589+
CREATE TYPE comp3 AS (c1 comp1, c2 comp2, m integer);
590+
CREATE TABLE composite_trigger_nested_test(c comp3);
591+
CREATE FUNCTION composite_trigger_nested_f() RETURNS trigger AS $$
592+
return 'MODIFY'
593+
$$ LANGUAGE plpythonu;
594+
CREATE TRIGGER composite_trigger_nested BEFORE INSERT ON composite_trigger_nested_test
595+
FOR EACH ROW EXECUTE PROCEDURE composite_trigger_nested_f();
596+
INSERT INTO composite_trigger_nested_test VALUES (NULL);
597+
INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(1, 'f'), NULL, 3));
598+
INSERT INTO composite_trigger_nested_test VALUES (ROW(ROW(NULL, 't'), ROW(1, 'f'), NULL));
599+
SELECT * FROM composite_trigger_nested_test;
600+
c
601+
-------------------
602+
603+
("(1,f)",,3)
604+
("(,t)","(1,f)",)
605+
(3 rows)
606+

‎src/pl/plpython/plpy_exec.c

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,7 @@ PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc)
180180
}
181181
elseif (proc->result.is_rowtype >=1)
182182
{
183-
TupleDescdesc;
184-
HeapTupletuple=NULL;
183+
TupleDescdesc;
185184

186185
/* make sure it's not an unnamed record */
187186
Assert((proc->result.out.d.typoid==RECORDOID&&
@@ -192,18 +191,8 @@ PLy_exec_function(FunctionCallInfo fcinfo, PLyProcedure *proc)
192191
desc=lookup_rowtype_tupdesc(proc->result.out.d.typoid,
193192
proc->result.out.d.typmod);
194193

195-
tuple=PLyObject_ToTuple(&proc->result,desc,plrv);
196-
197-
if (tuple!=NULL)
198-
{
199-
fcinfo->isnull= false;
200-
rv=HeapTupleGetDatum(tuple);
201-
}
202-
else
203-
{
204-
fcinfo->isnull= true;
205-
rv= (Datum)NULL;
206-
}
194+
rv=PLyObject_ToCompositeDatum(&proc->result,desc,plrv);
195+
fcinfo->isnull= (rv== (Datum)NULL);
207196
}
208197
else
209198
{

‎src/pl/plpython/plpy_typeio.c

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -49,10 +49,11 @@ static Datum PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *pl
4949
staticDatumPLyObject_ToDatum(PLyObToDatum*arg,int32typmod,PyObject*plrv);
5050
staticDatumPLySequence_ToArray(PLyObToDatum*arg,int32typmod,PyObject*plrv);
5151

52-
/* conversion from Python objects to heap tuples (used by triggers and SRFs) */
53-
staticHeapTuplePLyMapping_ToTuple(PLyTypeInfo*info,TupleDescdesc,PyObject*mapping);
54-
staticHeapTuplePLySequence_ToTuple(PLyTypeInfo*info,TupleDescdesc,PyObject*sequence);
55-
staticHeapTuplePLyGenericObject_ToTuple(PLyTypeInfo*info,TupleDescdesc,PyObject*object);
52+
/* conversion from Python objects to composite Datums (used by triggers and SRFs) */
53+
staticDatumPLyString_ToComposite(PLyTypeInfo*info,TupleDescdesc,PyObject*string);
54+
staticDatumPLyMapping_ToComposite(PLyTypeInfo*info,TupleDescdesc,PyObject*mapping);
55+
staticDatumPLySequence_ToComposite(PLyTypeInfo*info,TupleDescdesc,PyObject*sequence);
56+
staticDatumPLyGenericObject_ToComposite(PLyTypeInfo*info,TupleDescdesc,PyObject*object);
5657

5758
/* make allocations in the TopMemoryContext */
5859
staticvoidperm_fmgr_info(OidfunctionId,FmgrInfo*finfo);
@@ -333,26 +334,28 @@ PLyDict_FromTuple(PLyTypeInfo *info, HeapTuple tuple, TupleDesc desc)
333334
}
334335

335336
/*
336-
*Convert a Python object to aPostgreSQL tuple, using all supported
337-
*conversion methods:tuple as asequence, as amapping orasan object that
338-
*has __getattr__ support.
337+
*Convert a Python object to acomposite Datum, using all supported
338+
*conversion methods:composite as astring, as asequence,asa mapping or
339+
*as an object thathas __getattr__ support.
339340
*/
340-
HeapTuple
341-
PLyObject_ToTuple(PLyTypeInfo*info,TupleDescdesc,PyObject*plrv)
341+
Datum
342+
PLyObject_ToCompositeDatum(PLyTypeInfo*info,TupleDescdesc,PyObject*plrv)
342343
{
343-
HeapTupletuple;
344+
Datumdatum;
344345

345-
if (PySequence_Check(plrv))
346+
if (PyString_Check(plrv)||PyUnicode_Check(plrv))
347+
datum=PLyString_ToComposite(info,desc,plrv);
348+
elseif (PySequence_Check(plrv))
346349
/* composite type as sequence (tuple, list etc) */
347-
tuple=PLySequence_ToTuple(info,desc,plrv);
350+
datum=PLySequence_ToComposite(info,desc,plrv);
348351
elseif (PyMapping_Check(plrv))
349352
/* composite type as mapping (currently only dict) */
350-
tuple=PLyMapping_ToTuple(info,desc,plrv);
353+
datum=PLyMapping_ToComposite(info,desc,plrv);
351354
else
352355
/* returned as smth, must provide method __getattr__(name) */
353-
tuple=PLyGenericObject_ToTuple(info,desc,plrv);
356+
datum=PLyGenericObject_ToComposite(info,desc,plrv);
354357

355-
returntuple;
358+
returndatum;
356359
}
357360

358361
staticvoid
@@ -681,7 +684,6 @@ PLyObject_ToBytea(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
681684
staticDatum
682685
PLyObject_ToComposite(PLyObToDatum*arg,int32typmod,PyObject*plrv)
683686
{
684-
HeapTupletuple=NULL;
685687
Datumrv;
686688
PLyTypeInfoinfo;
687689
TupleDescdesc;
@@ -703,15 +705,10 @@ PLyObject_ToComposite(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
703705
* that info instead of looking it up every time a tuple is returned from
704706
* the function.
705707
*/
706-
tuple=PLyObject_ToTuple(&info,desc,plrv);
708+
rv=PLyObject_ToCompositeDatum(&info,desc,plrv);
707709

708710
PLy_typeinfo_dealloc(&info);
709711

710-
if (tuple!=NULL)
711-
rv=HeapTupleGetDatum(tuple);
712-
else
713-
rv= (Datum)NULL;
714-
715712
returnrv;
716713
}
717714

@@ -818,8 +815,27 @@ PLySequence_ToArray(PLyObToDatum *arg, int32 typmod, PyObject *plrv)
818815
returnPointerGetDatum(array);
819816
}
820817

821-
staticHeapTuple
822-
PLyMapping_ToTuple(PLyTypeInfo*info,TupleDescdesc,PyObject*mapping)
818+
819+
staticDatum
820+
PLyString_ToComposite(PLyTypeInfo*info,TupleDescdesc,PyObject*string)
821+
{
822+
HeapTupletypeTup;
823+
824+
typeTup=SearchSysCache1(TYPEOID,ObjectIdGetDatum(desc->tdtypeid));
825+
if (!HeapTupleIsValid(typeTup))
826+
elog(ERROR,"cache lookup failed for type %u",desc->tdtypeid);
827+
828+
PLy_output_datum_func2(&info->out.d,typeTup);
829+
830+
ReleaseSysCache(typeTup);
831+
ReleaseTupleDesc(desc);
832+
833+
returnPLyObject_ToDatum(&info->out.d,info->out.d.typmod,string);
834+
}
835+
836+
837+
staticDatum
838+
PLyMapping_ToComposite(PLyTypeInfo*info,TupleDescdesc,PyObject*mapping)
823839
{
824840
HeapTupletuple;
825841
Datum*values;
@@ -887,12 +903,12 @@ PLyMapping_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *mapping)
887903
pfree(values);
888904
pfree(nulls);
889905

890-
returntuple;
906+
returnHeapTupleGetDatum(tuple);
891907
}
892908

893909

894-
staticHeapTuple
895-
PLySequence_ToTuple(PLyTypeInfo*info,TupleDescdesc,PyObject*sequence)
910+
staticDatum
911+
PLySequence_ToComposite(PLyTypeInfo*info,TupleDescdesc,PyObject*sequence)
896912
{
897913
HeapTupletuple;
898914
Datum*values;
@@ -973,12 +989,12 @@ PLySequence_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *sequence)
973989
pfree(values);
974990
pfree(nulls);
975991

976-
returntuple;
992+
returnHeapTupleGetDatum(tuple);
977993
}
978994

979995

980-
staticHeapTuple
981-
PLyGenericObject_ToTuple(PLyTypeInfo*info,TupleDescdesc,PyObject*object)
996+
staticDatum
997+
PLyGenericObject_ToComposite(PLyTypeInfo*info,TupleDescdesc,PyObject*object)
982998
{
983999
HeapTupletuple;
9841000
Datum*values;
@@ -1045,7 +1061,7 @@ PLyGenericObject_ToTuple(PLyTypeInfo *info, TupleDesc desc, PyObject *object)
10451061
pfree(values);
10461062
pfree(nulls);
10471063

1048-
returntuple;
1064+
returnHeapTupleGetDatum(tuple);
10491065
}
10501066

10511067
/*

‎src/pl/plpython/plpy_typeio.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ extern void PLy_output_tuple_funcs(PLyTypeInfo *arg, TupleDesc desc);
9898

9999
externvoidPLy_output_record_funcs(PLyTypeInfo*arg,TupleDescdesc);
100100

101-
/* conversion from Python objects toheap tuples */
102-
externHeapTuplePLyObject_ToTuple(PLyTypeInfo*info,TupleDescdesc,PyObject*plrv);
101+
/* conversion from Python objects tocomposite Datums */
102+
externDatumPLyObject_ToCompositeDatum(PLyTypeInfo*info,TupleDescdesc,PyObject*plrv);
103103

104104
/* conversion from heap tuples to Python dictionaries */
105105
externPyObject*PLyDict_FromTuple(PLyTypeInfo*info,HeapTupletuple,TupleDescdesc);

‎src/pl/plpython/sql/plpython_record.sql

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ elif typ == 'obj':
4343
type_record.first= first
4444
type_record.second= second
4545
return type_record
46+
elif typ=='str':
47+
return"('%s',%r)" % (first, second)
4648
$$ LANGUAGE plpythonu;
4749

4850
CREATEFUNCTIONtest_in_out_params(firstintext, second outtext)AS $$
@@ -108,6 +110,8 @@ SELECT * FROM test_type_record_as('obj', null, 2, false);
108110
SELECT*FROM test_type_record_as('obj','three',3, false);
109111
SELECT*FROM test_type_record_as('obj',null,null, true);
110112

113+
SELECT*FROM test_type_record_as('str','one',1, false);
114+
111115
SELECT*FROM test_in_out_params('test_in');
112116
SELECT*FROM test_in_out_params_multi('test_in');
113117
SELECT*FROM test_inout_params('test_in');
@@ -151,3 +155,9 @@ CREATE FUNCTION test_type_record_error3() RETURNS type_record AS $$
151155
$$ LANGUAGE plpythonu;
152156

153157
SELECT*FROM test_type_record_error3();
158+
159+
CREATEFUNCTIONtest_type_record_error4() RETURNS type_recordAS $$
160+
return'foo'
161+
$$ LANGUAGE plpythonu;
162+
163+
SELECT*FROM test_type_record_error4();

‎src/pl/plpython/sql/plpython_trigger.sql

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -346,3 +346,39 @@ CREATE TRIGGER composite_trigger BEFORE INSERT ON composite_trigger_test
346346

347347
INSERT INTO composite_trigger_testVALUES (NULL,NULL);
348348
SELECT*FROM composite_trigger_test;
349+
350+
351+
-- triggers with composite type columns (bug #6559)
352+
353+
CREATETABLEcomposite_trigger_noop_test (f1 comp1, f2 comp2);
354+
355+
CREATEFUNCTIONcomposite_trigger_noop_f() RETURNS triggerAS $$
356+
return'MODIFY'
357+
$$ LANGUAGE plpythonu;
358+
359+
CREATETRIGGERcomposite_trigger_noop BEFORE INSERTON composite_trigger_noop_test
360+
FOR EACH ROW EXECUTE PROCEDURE composite_trigger_noop_f();
361+
362+
INSERT INTO composite_trigger_noop_testVALUES (NULL,NULL);
363+
INSERT INTO composite_trigger_noop_testVALUES (ROW(1,'f'),NULL);
364+
INSERT INTO composite_trigger_noop_testVALUES (ROW(NULL,'t'), ROW(1,'f'));
365+
SELECT*FROM composite_trigger_noop_test;
366+
367+
368+
-- nested composite types
369+
370+
CREATETYPEcomp3AS (c1 comp1, c2 comp2, minteger);
371+
372+
CREATETABLEcomposite_trigger_nested_test(c comp3);
373+
374+
CREATEFUNCTIONcomposite_trigger_nested_f() RETURNS triggerAS $$
375+
return'MODIFY'
376+
$$ LANGUAGE plpythonu;
377+
378+
CREATETRIGGERcomposite_trigger_nested BEFORE INSERTON composite_trigger_nested_test
379+
FOR EACH ROW EXECUTE PROCEDURE composite_trigger_nested_f();
380+
381+
INSERT INTO composite_trigger_nested_testVALUES (NULL);
382+
INSERT INTO composite_trigger_nested_testVALUES (ROW(ROW(1,'f'),NULL,3));
383+
INSERT INTO composite_trigger_nested_testVALUES (ROW(ROW(NULL,'t'), ROW(1,'f'),NULL));
384+
SELECT*FROM composite_trigger_nested_test;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp