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

Commit17dee32

Browse files
committed
Fix plpython's handling of functions used as triggers on multiple tables.
plpython tried to use a single cache entry for a trigger function, but itneeds a separate cache entry for each table the trigger is applied to,because there is table-dependent data in there. This was done correctlybefore 9.1, but commit46211da broke itby simplifying the lookup key from "function OID and triggered table OID"to "function OID and is-trigger boolean". Go back to using both OIDsas the lookup key. Per bug report from Sandro Santilli.Andres Freund
1 parent812451d commit17dee32

File tree

3 files changed

+100
-42
lines changed

3 files changed

+100
-42
lines changed

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

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -604,3 +604,27 @@ SELECT * FROM composite_trigger_nested_test;
604604
("(,t)","(1,f)",)
605605
(3 rows)
606606

607+
-- check that using a function as a trigger over two tables works correctly
608+
CREATE FUNCTION trig1234() RETURNS trigger LANGUAGE plpythonu AS $$
609+
TD["new"]["data"] = '1234'
610+
return 'MODIFY'
611+
$$;
612+
CREATE TABLE a(data text);
613+
CREATE TABLE b(data int); -- different type conversion
614+
CREATE TRIGGER a_t BEFORE INSERT ON a FOR EACH ROW EXECUTE PROCEDURE trig1234();
615+
CREATE TRIGGER b_t BEFORE INSERT ON b FOR EACH ROW EXECUTE PROCEDURE trig1234();
616+
INSERT INTO a DEFAULT VALUES;
617+
SELECT * FROM a;
618+
data
619+
------
620+
1234
621+
(1 row)
622+
623+
DROP TABLE a;
624+
INSERT INTO b DEFAULT VALUES;
625+
SELECT * FROM b;
626+
data
627+
------
628+
1234
629+
(1 row)
630+

‎src/pl/plpython/plpython.c

Lines changed: 58 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -228,10 +228,17 @@ typedef struct PLyProcedure
228228
}PLyProcedure;
229229

230230

231+
/* the procedure cache key */
232+
typedefstructPLyProcedureKey
233+
{
234+
Oidfn_oid;/* function OID */
235+
Oidfn_rel;/* triggered-on relation or InvalidOid */
236+
}PLyProcedureKey;
237+
231238
/* the procedure cache entry */
232239
typedefstructPLyProcedureEntry
233240
{
234-
Oidfn_oid;/* hash key */
241+
PLyProcedureKeykey;/* hash key */
235242
PLyProcedure*proc;
236243
}PLyProcedureEntry;
237244

@@ -371,7 +378,7 @@ static HeapTuple PLy_modify_tuple(PLyProcedure *, PyObject *,
371378

372379
staticPyObject*PLy_procedure_call(PLyProcedure*,char*,PyObject*);
373380

374-
staticPLyProcedure*PLy_procedure_get(Oidfn_oid,boolis_trigger);
381+
staticPLyProcedure*PLy_procedure_get(Oidfn_oid,Oidfn_rel,boolis_trigger);
375382

376383
staticPLyProcedure*PLy_procedure_create(HeapTupleprocTup,
377384
Oidfn_oid,boolis_trigger);
@@ -427,7 +434,6 @@ static List *explicit_subtransactions = NIL;
427434
staticPyObject*PLy_interp_globals=NULL;
428435
staticPyObject*PLy_interp_safe_globals=NULL;
429436
staticHTAB*PLy_procedure_cache=NULL;
430-
staticHTAB*PLy_trigger_cache=NULL;
431437

432438
/* Python exceptions */
433439
staticPyObject*PLy_exc_error=NULL;
@@ -528,7 +534,8 @@ plpython_validator(PG_FUNCTION_ARGS)
528534

529535
ReleaseSysCache(tuple);
530536

531-
PLy_procedure_get(funcoid,is_trigger);
537+
/* We can't validate triggers against any particular table ... */
538+
PLy_procedure_get(funcoid,InvalidOid,is_trigger);
532539

533540
PG_RETURN_VOID();
534541
}
@@ -554,20 +561,22 @@ plpython_call_handler(PG_FUNCTION_ARGS)
554561

555562
PG_TRY();
556563
{
564+
Oidfuncoid=fcinfo->flinfo->fn_oid;
557565
PLyProcedure*proc;
558566

559567
if (CALLED_AS_TRIGGER(fcinfo))
560568
{
569+
Relationtgrel= ((TriggerData*)fcinfo->context)->tg_relation;
561570
HeapTupletrv;
562571

563-
proc=PLy_procedure_get(fcinfo->flinfo->fn_oid, true);
572+
proc=PLy_procedure_get(funcoid,RelationGetRelid(tgrel), true);
564573
PLy_curr_procedure=proc;
565574
trv=PLy_trigger_handler(fcinfo,proc);
566575
retval=PointerGetDatum(trv);
567576
}
568577
else
569578
{
570-
proc=PLy_procedure_get(fcinfo->flinfo->fn_oid, false);
579+
proc=PLy_procedure_get(funcoid,InvalidOid, false);
571580
PLy_curr_procedure=proc;
572581
retval=PLy_function_handler(fcinfo,proc);
573582
}
@@ -1516,62 +1525,76 @@ PLy_procedure_valid(PLyProcedure *proc, HeapTuple procTup)
15161525
* PLyProcedure functions
15171526
*/
15181527

1519-
/* PLy_procedure_get: returns a cached PLyProcedure, or creates, stores and
1520-
* returns a new PLyProcedure.fcinfo is the call info, tgreloid is the
1521-
* relation OID when calling a trigger, or InvalidOid (zero) for ordinary
1522-
* function calls.
1528+
/*
1529+
* PLy_procedure_get: returns a cached PLyProcedure, or creates, stores and
1530+
* returns a new PLyProcedure.
1531+
*
1532+
* fn_oid is the OID of the function requested
1533+
* fn_rel is InvalidOid or the relation this function triggers on
1534+
* is_trigger denotes whether the function is a trigger function
1535+
*
1536+
* The reason that both fn_rel and is_trigger need to be passed is that when
1537+
* trigger functions get validated we don't know which relation(s) they'll
1538+
* be used with, so no sensible fn_rel can be passed.
15231539
*/
15241540
staticPLyProcedure*
1525-
PLy_procedure_get(Oidfn_oid,boolis_trigger)
1541+
PLy_procedure_get(Oidfn_oid,Oidfn_rel,boolis_trigger)
15261542
{
1543+
booluse_cache= !(is_trigger&&fn_rel==InvalidOid);
15271544
HeapTupleprocTup;
1528-
PLyProcedureEntry*volatileentry;
1529-
boolfound;
1545+
PLyProcedureKeykey;
1546+
PLyProcedureEntry*volatileentry=NULL;
1547+
PLyProcedure*volatileproc=NULL;
1548+
boolfound= false;
15301549

15311550
procTup=SearchSysCache1(PROCOID,ObjectIdGetDatum(fn_oid));
15321551
if (!HeapTupleIsValid(procTup))
15331552
elog(ERROR,"cache lookup failed for function %u",fn_oid);
15341553

1535-
/* Look for the function in the corresponding cache */
1536-
if (is_trigger)
1537-
entry=hash_search(PLy_trigger_cache,
1538-
&fn_oid,HASH_ENTER,&found);
1539-
else
1540-
entry=hash_search(PLy_procedure_cache,
1541-
&fn_oid,HASH_ENTER,&found);
1554+
/*
1555+
* Look for the function in the cache, unless we don't have the necessary
1556+
* information (e.g. during validation). In that case we just don't cache
1557+
* anything.
1558+
*/
1559+
if (use_cache)
1560+
{
1561+
key.fn_oid=fn_oid;
1562+
key.fn_rel=fn_rel;
1563+
entry=hash_search(PLy_procedure_cache,&key,HASH_ENTER,&found);
1564+
proc=entry->proc;
1565+
}
15421566

15431567
PG_TRY();
15441568
{
15451569
if (!found)
15461570
{
1547-
/* Haven't found it, create a new cache entry */
1548-
entry->proc=PLy_procedure_create(procTup,fn_oid,is_trigger);
1571+
/* Haven't found it, create a new procedure */
1572+
proc=PLy_procedure_create(procTup,fn_oid,is_trigger);
1573+
if (use_cache)
1574+
entry->proc=proc;
15491575
}
1550-
elseif (!PLy_procedure_valid(entry->proc,procTup))
1576+
elseif (!PLy_procedure_valid(proc,procTup))
15511577
{
15521578
/* Found it, but it's invalid, free and reuse the cache entry */
1553-
PLy_procedure_delete(entry->proc);
1554-
PLy_free(entry->proc);
1555-
entry->proc=PLy_procedure_create(procTup,fn_oid,is_trigger);
1579+
PLy_procedure_delete(proc);
1580+
PLy_free(proc);
1581+
proc=PLy_procedure_create(procTup,fn_oid,is_trigger);
1582+
entry->proc=proc;
15561583
}
15571584
/* Found it and it's valid, it's fine to use it */
15581585
}
15591586
PG_CATCH();
15601587
{
15611588
/* Do not leave an uninitialised entry in the cache */
1562-
if (is_trigger)
1563-
hash_search(PLy_trigger_cache,
1564-
&fn_oid,HASH_REMOVE,NULL);
1565-
else
1566-
hash_search(PLy_procedure_cache,
1567-
&fn_oid,HASH_REMOVE,NULL);
1589+
if (use_cache)
1590+
hash_search(PLy_procedure_cache,&key,HASH_REMOVE,NULL);
15681591
PG_RE_THROW();
15691592
}
15701593
PG_END_TRY();
15711594

15721595
ReleaseSysCache(procTup);
15731596

1574-
returnentry->proc;
1597+
returnproc;
15751598
}
15761599

15771600
/*
@@ -4115,19 +4138,12 @@ _PG_init(void)
41154138
PLy_elog(FATAL,"untrapped error in initialization");
41164139

41174140
memset(&hash_ctl,0,sizeof(hash_ctl));
4118-
hash_ctl.keysize=sizeof(Oid);
4141+
hash_ctl.keysize=sizeof(PLyProcedureKey);
41194142
hash_ctl.entrysize=sizeof(PLyProcedureEntry);
4120-
hash_ctl.hash=oid_hash;
4143+
hash_ctl.hash=tag_hash;
41214144
PLy_procedure_cache=hash_create("PL/Python procedures",32,&hash_ctl,
41224145
HASH_ELEM |HASH_FUNCTION);
41234146

4124-
memset(&hash_ctl,0,sizeof(hash_ctl));
4125-
hash_ctl.keysize=sizeof(Oid);
4126-
hash_ctl.entrysize=sizeof(PLyProcedureEntry);
4127-
hash_ctl.hash=oid_hash;
4128-
PLy_trigger_cache=hash_create("PL/Python triggers",32,&hash_ctl,
4129-
HASH_ELEM |HASH_FUNCTION);
4130-
41314147
explicit_subtransactions=NIL;
41324148

41334149
inited= true;

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -382,3 +382,21 @@ INSERT INTO composite_trigger_nested_test VALUES (NULL);
382382
INSERT INTO composite_trigger_nested_testVALUES (ROW(ROW(1,'f'),NULL,3));
383383
INSERT INTO composite_trigger_nested_testVALUES (ROW(ROW(NULL,'t'), ROW(1,'f'),NULL));
384384
SELECT*FROM composite_trigger_nested_test;
385+
386+
-- check that using a function as a trigger over two tables works correctly
387+
CREATEFUNCTIONtrig1234() RETURNS trigger LANGUAGE plpythonuAS $$
388+
TD["new"]["data"]='1234'
389+
return'MODIFY'
390+
$$;
391+
392+
CREATETABLEa(datatext);
393+
CREATETABLEb(dataint);-- different type conversion
394+
395+
CREATETRIGGERa_t BEFORE INSERTON a FOR EACH ROW EXECUTE PROCEDURE trig1234();
396+
CREATETRIGGERb_t BEFORE INSERTON b FOR EACH ROW EXECUTE PROCEDURE trig1234();
397+
398+
INSERT INTO a DEFAULTVALUES;
399+
SELECT*FROM a;
400+
DROPTABLE a;
401+
INSERT INTO b DEFAULTVALUES;
402+
SELECT*FROM b;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp