Skip to content

Commit 7eb2ff7

Browse files
committed
Support assignment to whole-row variables in plpgsql; also fix glitch
with using a trigger's NEW or OLD record as a whole-row variable in an expression. Fixes several long-standing complaints.
1 parent 0f059e1 commit 7eb2ff7

File tree

2 files changed

+160
-42
lines changed

2 files changed

+160
-42
lines changed

src/pl/plpgsql/src/gram.y

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* procedural language
55
*
66
* IDENTIFICATION
7-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.55 2004/06/04 00:07:52 tgl Exp $
7+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.56 2004/06/04 02:37:06 tgl Exp $
88
*
99
* This software is copyrighted by Jan Wieck - Hamburg.
1010
*
@@ -744,6 +744,16 @@ assign_var : T_SCALAR
744744
check_assignable(yylval.scalar);
745745
$$ = yylval.scalar->dno;
746746
}
747+
| T_ROW
748+
{
749+
check_assignable((PLpgSQL_datum *) yylval.row);
750+
$$ = yylval.row->rowno;
751+
}
752+
| T_RECORD
753+
{
754+
check_assignable((PLpgSQL_datum *) yylval.rec);
755+
$$ = yylval.rec->recno;
756+
}
747757
| assign_var '[' expr_until_rightbracket
748758
{
749759
PLpgSQL_arrayelem *new;
@@ -1966,6 +1976,12 @@ check_assignable(PLpgSQL_datum *datum)
19661976
((PLpgSQL_var *) datum)->refname)));
19671977
}
19681978
break;
1979+
case PLPGSQL_DTYPE_ROW:
1980+
/* always assignable? */
1981+
break;
1982+
case PLPGSQL_DTYPE_REC:
1983+
/* always assignable? What about NEW/OLD? */
1984+
break;
19691985
case PLPGSQL_DTYPE_RECFIELD:
19701986
/* always assignable? */
19711987
break;

src/pl/plpgsql/src/pl_exec.c

Lines changed: 143 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
* procedural language
44
*
55
* IDENTIFICATION
6-
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.103 2004/06/04 00:07:52 tgl Exp $
6+
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.104 2004/06/04 02:37:06 tgl Exp $
77
*
88
* This software is copyrighted by Jan Wieck - Hamburg.
99
*
@@ -2664,49 +2664,15 @@ exec_assign_value(PLpgSQL_execstate * estate,
26642664
PLpgSQL_datum * target,
26652665
Datum value, Oid valtype, bool *isNull)
26662666
{
2667-
PLpgSQL_var *var;
2668-
PLpgSQL_rec *rec;
2669-
PLpgSQL_recfield *recfield;
2670-
int fno;
2671-
int i;
2672-
int natts;
2673-
Datum *values;
2674-
char *nulls;
2675-
void *mustfree;
2676-
Datum newvalue;
2677-
bool attisnull;
2678-
Oid atttype;
2679-
int32 atttypmod;
2680-
int nsubscripts;
2681-
PLpgSQL_expr *subscripts[MAXDIM];
2682-
int subscriptvals[MAXDIM];
2683-
bool havenullsubscript,
2684-
oldarrayisnull;
2685-
Oid arraytypeid,
2686-
arrayelemtypeid,
2687-
arrayInputFn;
2688-
int16 elemtyplen;
2689-
bool elemtypbyval;
2690-
char elemtypalign;
2691-
Datum oldarrayval,
2692-
coerced_value;
2693-
ArrayType *newarrayval;
2694-
HeapTuple newtup;
2695-
26962667
switch (target->dtype)
26972668
{
26982669
case PLPGSQL_DTYPE_VAR:
2699-
2670+
{
27002671
/*
27012672
* Target is a variable
27022673
*/
2703-
var = (PLpgSQL_var *) target;
2704-
2705-
if (var->freeval)
2706-
{
2707-
pfree(DatumGetPointer(var->value));
2708-
var->freeval = false;
2709-
}
2674+
PLpgSQL_var *var = (PLpgSQL_var *) target;
2675+
Datum newvalue;
27102676

27112677
newvalue = exec_cast_value(value, valtype, var->datatype->typoid,
27122678
&(var->datatype->typinput),
@@ -2720,6 +2686,12 @@ exec_assign_value(PLpgSQL_execstate * estate,
27202686
errmsg("NULL cannot be assigned to variable \"%s\" declared NOT NULL",
27212687
var->refname)));
27222688

2689+
if (var->freeval)
2690+
{
2691+
pfree(DatumGetPointer(var->value));
2692+
var->freeval = false;
2693+
}
2694+
27232695
/*
27242696
* If type is by-reference, make sure we have a freshly
27252697
* palloc'd copy; the originally passed value may not live as
@@ -2741,13 +2713,110 @@ exec_assign_value(PLpgSQL_execstate * estate,
27412713
var->value = newvalue;
27422714
var->isnull = *isNull;
27432715
break;
2716+
}
27442717

2745-
case PLPGSQL_DTYPE_RECFIELD:
2718+
case PLPGSQL_DTYPE_ROW:
2719+
{
2720+
/*
2721+
* Target is a row variable
2722+
*/
2723+
PLpgSQL_row *row = (PLpgSQL_row *) target;
2724+
2725+
/* Source must be of RECORD or composite type */
2726+
if (!(valtype == RECORDOID ||
2727+
get_typtype(valtype) == 'c'))
2728+
ereport(ERROR,
2729+
(errcode(ERRCODE_DATATYPE_MISMATCH),
2730+
errmsg("cannot assign non-composite value to a row variable")));
2731+
if (*isNull)
2732+
{
2733+
/* If source is null, just assign nulls to the row */
2734+
exec_move_row(estate, NULL, row, NULL, NULL);
2735+
}
2736+
else
2737+
{
2738+
HeapTupleHeader td;
2739+
Oid tupType;
2740+
int32 tupTypmod;
2741+
TupleDesc tupdesc;
2742+
HeapTupleData tmptup;
2743+
2744+
/* Else source is a tuple Datum, safe to do this: */
2745+
td = DatumGetHeapTupleHeader(value);
2746+
/* Extract rowtype info and find a tupdesc */
2747+
tupType = HeapTupleHeaderGetTypeId(td);
2748+
tupTypmod = HeapTupleHeaderGetTypMod(td);
2749+
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
2750+
/* Build a temporary HeapTuple control structure */
2751+
tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
2752+
ItemPointerSetInvalid(&(tmptup.t_self));
2753+
tmptup.t_tableOid = InvalidOid;
2754+
tmptup.t_data = td;
2755+
exec_move_row(estate, NULL, row, &tmptup, tupdesc);
2756+
}
2757+
break;
2758+
}
27462759

2760+
case PLPGSQL_DTYPE_REC:
2761+
{
2762+
/*
2763+
* Target is a record variable
2764+
*/
2765+
PLpgSQL_rec *rec = (PLpgSQL_rec *) target;
2766+
2767+
/* Source must be of RECORD or composite type */
2768+
if (!(valtype == RECORDOID ||
2769+
get_typtype(valtype) == 'c'))
2770+
ereport(ERROR,
2771+
(errcode(ERRCODE_DATATYPE_MISMATCH),
2772+
errmsg("cannot assign non-composite value to a record variable")));
2773+
if (*isNull)
2774+
{
2775+
/* If source is null, just assign nulls to the record */
2776+
exec_move_row(estate, rec, NULL, NULL, NULL);
2777+
}
2778+
else
2779+
{
2780+
HeapTupleHeader td;
2781+
Oid tupType;
2782+
int32 tupTypmod;
2783+
TupleDesc tupdesc;
2784+
HeapTupleData tmptup;
2785+
2786+
/* Else source is a tuple Datum, safe to do this: */
2787+
td = DatumGetHeapTupleHeader(value);
2788+
/* Extract rowtype info and find a tupdesc */
2789+
tupType = HeapTupleHeaderGetTypeId(td);
2790+
tupTypmod = HeapTupleHeaderGetTypMod(td);
2791+
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
2792+
/* Build a temporary HeapTuple control structure */
2793+
tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
2794+
ItemPointerSetInvalid(&(tmptup.t_self));
2795+
tmptup.t_tableOid = InvalidOid;
2796+
tmptup.t_data = td;
2797+
exec_move_row(estate, rec, NULL, &tmptup, tupdesc);
2798+
}
2799+
break;
2800+
}
2801+
2802+
case PLPGSQL_DTYPE_RECFIELD:
2803+
{
27472804
/*
27482805
* Target is a field of a record
27492806
*/
2750-
recfield = (PLpgSQL_recfield *) target;
2807+
PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) target;
2808+
PLpgSQL_rec *rec;
2809+
int fno;
2810+
HeapTuple newtup;
2811+
int natts;
2812+
int i;
2813+
Datum *values;
2814+
char *nulls;
2815+
void *mustfree;
2816+
bool attisnull;
2817+
Oid atttype;
2818+
int32 atttypmod;
2819+
27512820
rec = (PLpgSQL_rec *) (estate->datums[recfield->recparentno]);
27522821

27532822
/*
@@ -2839,8 +2908,25 @@ exec_assign_value(PLpgSQL_execstate * estate,
28392908
pfree(mustfree);
28402909

28412910
break;
2911+
}
28422912

28432913
case PLPGSQL_DTYPE_ARRAYELEM:
2914+
{
2915+
int nsubscripts;
2916+
int i;
2917+
PLpgSQL_expr *subscripts[MAXDIM];
2918+
int subscriptvals[MAXDIM];
2919+
bool havenullsubscript,
2920+
oldarrayisnull;
2921+
Oid arraytypeid,
2922+
arrayelemtypeid,
2923+
arrayInputFn;
2924+
int16 elemtyplen;
2925+
bool elemtypbyval;
2926+
char elemtypalign;
2927+
Datum oldarrayval,
2928+
coerced_value;
2929+
ArrayType *newarrayval;
28442930

28452931
/*
28462932
* Target is an element of an array
@@ -2942,6 +3028,7 @@ exec_assign_value(PLpgSQL_execstate * estate,
29423028
*/
29433029
pfree(newarrayval);
29443030
break;
3031+
}
29453032

29463033
default:
29473034
elog(ERROR, "unrecognized dtype: %d", target->dtype);
@@ -2993,6 +3080,8 @@ exec_eval_datum(PLpgSQL_execstate * estate,
29933080

29943081
if (!row->rowtupdesc) /* should not happen */
29953082
elog(ERROR, "row variable has no tupdesc");
3083+
/* Make sure we have a valid type/typmod setting */
3084+
BlessTupleDesc(row->rowtupdesc);
29963085
tup = make_tuple_from_row(estate, row, row->rowtupdesc);
29973086
if (tup == NULL) /* should not happen */
29983087
elog(ERROR, "row not compatible with its own tupdesc");
@@ -3010,15 +3099,28 @@ exec_eval_datum(PLpgSQL_execstate * estate,
30103099
case PLPGSQL_DTYPE_REC:
30113100
{
30123101
PLpgSQL_rec *rec = (PLpgSQL_rec *) datum;
3102+
HeapTupleData worktup;
30133103

30143104
if (!HeapTupleIsValid(rec->tup))
30153105
ereport(ERROR,
30163106
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
30173107
errmsg("record \"%s\" is not assigned yet",
30183108
rec->refname),
30193109
errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
3110+
Assert(rec->tupdesc != NULL);
3111+
/* Make sure we have a valid type/typmod setting */
3112+
BlessTupleDesc(rec->tupdesc);
3113+
/*
3114+
* In a trigger, the NEW and OLD parameters are likely to be
3115+
* on-disk tuples that don't have the desired Datum fields.
3116+
* Copy the tuple body and insert the right values.
3117+
*/
3118+
heap_copytuple_with_tuple(rec->tup, &worktup);
3119+
HeapTupleHeaderSetDatumLength(worktup.t_data, worktup.t_len);
3120+
HeapTupleHeaderSetTypeId(worktup.t_data, rec->tupdesc->tdtypeid);
3121+
HeapTupleHeaderSetTypMod(worktup.t_data, rec->tupdesc->tdtypmod);
30203122
*typeid = rec->tupdesc->tdtypeid;
3021-
*value = HeapTupleGetDatum(rec->tup);
3123+
*value = HeapTupleGetDatum(&worktup);
30223124
*isnull = false;
30233125
if (expectedtypeid != InvalidOid && expectedtypeid != *typeid)
30243126
ereport(ERROR,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy