aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/pl/plpgsql/src/gram.y18
-rw-r--r--src/pl/plpgsql/src/pl_exec.c184
2 files changed, 160 insertions, 42 deletions
diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y
index 7abc1e19b83..826e1539d1e 100644
--- a/src/pl/plpgsql/src/gram.y
+++ b/src/pl/plpgsql/src/gram.y
@@ -4,7 +4,7 @@
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.55 2004/06/04 00:07:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.56 2004/06/04 02:37:06 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -744,6 +744,16 @@ assign_var : T_SCALAR
check_assignable(yylval.scalar);
$$ = yylval.scalar->dno;
}
+ | T_ROW
+ {
+ check_assignable((PLpgSQL_datum *) yylval.row);
+ $$ = yylval.row->rowno;
+ }
+ | T_RECORD
+ {
+ check_assignable((PLpgSQL_datum *) yylval.rec);
+ $$ = yylval.rec->recno;
+ }
| assign_var '[' expr_until_rightbracket
{
PLpgSQL_arrayelem *new;
@@ -1966,6 +1976,12 @@ check_assignable(PLpgSQL_datum *datum)
((PLpgSQL_var *) datum)->refname)));
}
break;
+ case PLPGSQL_DTYPE_ROW:
+ /* always assignable? */
+ break;
+ case PLPGSQL_DTYPE_REC:
+ /* always assignable? What about NEW/OLD? */
+ break;
case PLPGSQL_DTYPE_RECFIELD:
/* always assignable? */
break;
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index 149c96ec7ba..5a3d77a07c6 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -3,7 +3,7 @@
* procedural language
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.103 2004/06/04 00:07:52 tgl Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.104 2004/06/04 02:37:06 tgl Exp $
*
* This software is copyrighted by Jan Wieck - Hamburg.
*
@@ -2664,49 +2664,15 @@ exec_assign_value(PLpgSQL_execstate * estate,
PLpgSQL_datum * target,
Datum value, Oid valtype, bool *isNull)
{
- PLpgSQL_var *var;
- PLpgSQL_rec *rec;
- PLpgSQL_recfield *recfield;
- int fno;
- int i;
- int natts;
- Datum *values;
- char *nulls;
- void *mustfree;
- Datum newvalue;
- bool attisnull;
- Oid atttype;
- int32 atttypmod;
- int nsubscripts;
- PLpgSQL_expr *subscripts[MAXDIM];
- int subscriptvals[MAXDIM];
- bool havenullsubscript,
- oldarrayisnull;
- Oid arraytypeid,
- arrayelemtypeid,
- arrayInputFn;
- int16 elemtyplen;
- bool elemtypbyval;
- char elemtypalign;
- Datum oldarrayval,
- coerced_value;
- ArrayType *newarrayval;
- HeapTuple newtup;
-
switch (target->dtype)
{
case PLPGSQL_DTYPE_VAR:
-
+ {
/*
* Target is a variable
*/
- var = (PLpgSQL_var *) target;
-
- if (var->freeval)
- {
- pfree(DatumGetPointer(var->value));
- var->freeval = false;
- }
+ PLpgSQL_var *var = (PLpgSQL_var *) target;
+ Datum newvalue;
newvalue = exec_cast_value(value, valtype, var->datatype->typoid,
&(var->datatype->typinput),
@@ -2720,6 +2686,12 @@ exec_assign_value(PLpgSQL_execstate * estate,
errmsg("NULL cannot be assigned to variable \"%s\" declared NOT NULL",
var->refname)));
+ if (var->freeval)
+ {
+ pfree(DatumGetPointer(var->value));
+ var->freeval = false;
+ }
+
/*
* If type is by-reference, make sure we have a freshly
* palloc'd copy; the originally passed value may not live as
@@ -2741,13 +2713,110 @@ exec_assign_value(PLpgSQL_execstate * estate,
var->value = newvalue;
var->isnull = *isNull;
break;
+ }
- case PLPGSQL_DTYPE_RECFIELD:
+ case PLPGSQL_DTYPE_ROW:
+ {
+ /*
+ * Target is a row variable
+ */
+ PLpgSQL_row *row = (PLpgSQL_row *) target;
+
+ /* Source must be of RECORD or composite type */
+ if (!(valtype == RECORDOID ||
+ get_typtype(valtype) == 'c'))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot assign non-composite value to a row variable")));
+ if (*isNull)
+ {
+ /* If source is null, just assign nulls to the row */
+ exec_move_row(estate, NULL, row, NULL, NULL);
+ }
+ else
+ {
+ HeapTupleHeader td;
+ Oid tupType;
+ int32 tupTypmod;
+ TupleDesc tupdesc;
+ HeapTupleData tmptup;
+
+ /* Else source is a tuple Datum, safe to do this: */
+ td = DatumGetHeapTupleHeader(value);
+ /* Extract rowtype info and find a tupdesc */
+ tupType = HeapTupleHeaderGetTypeId(td);
+ tupTypmod = HeapTupleHeaderGetTypMod(td);
+ tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+ /* Build a temporary HeapTuple control structure */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+ ItemPointerSetInvalid(&(tmptup.t_self));
+ tmptup.t_tableOid = InvalidOid;
+ tmptup.t_data = td;
+ exec_move_row(estate, NULL, row, &tmptup, tupdesc);
+ }
+ break;
+ }
+ case PLPGSQL_DTYPE_REC:
+ {
+ /*
+ * Target is a record variable
+ */
+ PLpgSQL_rec *rec = (PLpgSQL_rec *) target;
+
+ /* Source must be of RECORD or composite type */
+ if (!(valtype == RECORDOID ||
+ get_typtype(valtype) == 'c'))
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("cannot assign non-composite value to a record variable")));
+ if (*isNull)
+ {
+ /* If source is null, just assign nulls to the record */
+ exec_move_row(estate, rec, NULL, NULL, NULL);
+ }
+ else
+ {
+ HeapTupleHeader td;
+ Oid tupType;
+ int32 tupTypmod;
+ TupleDesc tupdesc;
+ HeapTupleData tmptup;
+
+ /* Else source is a tuple Datum, safe to do this: */
+ td = DatumGetHeapTupleHeader(value);
+ /* Extract rowtype info and find a tupdesc */
+ tupType = HeapTupleHeaderGetTypeId(td);
+ tupTypmod = HeapTupleHeaderGetTypMod(td);
+ tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
+ /* Build a temporary HeapTuple control structure */
+ tmptup.t_len = HeapTupleHeaderGetDatumLength(td);
+ ItemPointerSetInvalid(&(tmptup.t_self));
+ tmptup.t_tableOid = InvalidOid;
+ tmptup.t_data = td;
+ exec_move_row(estate, rec, NULL, &tmptup, tupdesc);
+ }
+ break;
+ }
+
+ case PLPGSQL_DTYPE_RECFIELD:
+ {
/*
* Target is a field of a record
*/
- recfield = (PLpgSQL_recfield *) target;
+ PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) target;
+ PLpgSQL_rec *rec;
+ int fno;
+ HeapTuple newtup;
+ int natts;
+ int i;
+ Datum *values;
+ char *nulls;
+ void *mustfree;
+ bool attisnull;
+ Oid atttype;
+ int32 atttypmod;
+
rec = (PLpgSQL_rec *) (estate->datums[recfield->recparentno]);
/*
@@ -2839,8 +2908,25 @@ exec_assign_value(PLpgSQL_execstate * estate,
pfree(mustfree);
break;
+ }
case PLPGSQL_DTYPE_ARRAYELEM:
+ {
+ int nsubscripts;
+ int i;
+ PLpgSQL_expr *subscripts[MAXDIM];
+ int subscriptvals[MAXDIM];
+ bool havenullsubscript,
+ oldarrayisnull;
+ Oid arraytypeid,
+ arrayelemtypeid,
+ arrayInputFn;
+ int16 elemtyplen;
+ bool elemtypbyval;
+ char elemtypalign;
+ Datum oldarrayval,
+ coerced_value;
+ ArrayType *newarrayval;
/*
* Target is an element of an array
@@ -2942,6 +3028,7 @@ exec_assign_value(PLpgSQL_execstate * estate,
*/
pfree(newarrayval);
break;
+ }
default:
elog(ERROR, "unrecognized dtype: %d", target->dtype);
@@ -2993,6 +3080,8 @@ exec_eval_datum(PLpgSQL_execstate * estate,
if (!row->rowtupdesc) /* should not happen */
elog(ERROR, "row variable has no tupdesc");
+ /* Make sure we have a valid type/typmod setting */
+ BlessTupleDesc(row->rowtupdesc);
tup = make_tuple_from_row(estate, row, row->rowtupdesc);
if (tup == NULL) /* should not happen */
elog(ERROR, "row not compatible with its own tupdesc");
@@ -3010,6 +3099,7 @@ exec_eval_datum(PLpgSQL_execstate * estate,
case PLPGSQL_DTYPE_REC:
{
PLpgSQL_rec *rec = (PLpgSQL_rec *) datum;
+ HeapTupleData worktup;
if (!HeapTupleIsValid(rec->tup))
ereport(ERROR,
@@ -3017,8 +3107,20 @@ exec_eval_datum(PLpgSQL_execstate * estate,
errmsg("record \"%s\" is not assigned yet",
rec->refname),
errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
+ Assert(rec->tupdesc != NULL);
+ /* Make sure we have a valid type/typmod setting */
+ BlessTupleDesc(rec->tupdesc);
+ /*
+ * In a trigger, the NEW and OLD parameters are likely to be
+ * on-disk tuples that don't have the desired Datum fields.
+ * Copy the tuple body and insert the right values.
+ */
+ heap_copytuple_with_tuple(rec->tup, &worktup);
+ HeapTupleHeaderSetDatumLength(worktup.t_data, worktup.t_len);
+ HeapTupleHeaderSetTypeId(worktup.t_data, rec->tupdesc->tdtypeid);
+ HeapTupleHeaderSetTypMod(worktup.t_data, rec->tupdesc->tdtypmod);
*typeid = rec->tupdesc->tdtypeid;
- *value = HeapTupleGetDatum(rec->tup);
+ *value = HeapTupleGetDatum(&worktup);
*isnull = false;
if (expectedtypeid != InvalidOid && expectedtypeid != *typeid)
ereport(ERROR,