diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 7abc1e19b8..826e1539d1 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 149c96ec7b..5a3d77a07c 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_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,