From 0f059e1d131584cec380962d3166eab78da1b35c Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Fri, 4 Jun 2004 00:07:52 +0000 Subject: [PATCH] Allow plpgsql to pass composite-type arguments (ie, whole-row variables) into SQL expressions. At present this only works usefully for variables of named rowtypes, not RECORD variables, since the SQL parser can't infer anything about datatypes from a RECORD Param. Still, it's a step forward. --- src/pl/plpgsql/src/gram.y | 30 +++++++--------- src/pl/plpgsql/src/pl_exec.c | 69 ++++++++++++++++++++++++++++++------ 2 files changed, 71 insertions(+), 28 deletions(-) diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 36427e2cea..7abc1e19b8 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.54 2004/06/03 22:56:43 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.55 2004/06/04 00:07:52 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -1589,17 +1589,15 @@ read_sql_construct(int until, break; case T_ROW: - /* XXX make this work someday */ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("passing a whole row variable into a SQL command is not implemented"))); + params[nparams] = yylval.row->rowno; + snprintf(buf, sizeof(buf), " $%d ", ++nparams); + plpgsql_dstring_append(&ds, buf); break; case T_RECORD: - /* XXX make this work someday */ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("passing a whole record variable into a SQL command is not implemented"))); + params[nparams] = yylval.rec->recno; + snprintf(buf, sizeof(buf), " $%d ", ++nparams); + plpgsql_dstring_append(&ds, buf); break; default: @@ -1810,17 +1808,15 @@ make_select_stmt(void) break; case T_ROW: - /* XXX make this work someday */ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("passing a whole row variable into a SQL command is not implemented"))); + params[nparams] = yylval.row->rowno; + snprintf(buf, sizeof(buf), " $%d ", ++nparams); + plpgsql_dstring_append(&ds, buf); break; case T_RECORD: - /* XXX make this work someday */ - ereport(ERROR, - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), - errmsg("passing a whole record variable into a SQL command is not implemented"))); + params[nparams] = yylval.rec->recno; + snprintf(buf, sizeof(buf), " $%d ", ++nparams); + plpgsql_dstring_append(&ds, buf); break; default: diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 7fa1eecaca..149c96ec7b 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.102 2004/05/30 23:40:41 neilc Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.103 2004/06/04 00:07:52 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -2969,17 +2969,12 @@ exec_eval_datum(PLpgSQL_execstate * estate, Datum *value, bool *isnull) { - PLpgSQL_var *var; - PLpgSQL_rec *rec; - PLpgSQL_recfield *recfield; - PLpgSQL_trigarg *trigarg; - int tgargno; - int fno; - switch (datum->dtype) { case PLPGSQL_DTYPE_VAR: - var = (PLpgSQL_var *) datum; + { + PLpgSQL_var *var = (PLpgSQL_var *) datum; + *typeid = var->datatype->typoid; *value = var->value; *isnull = var->isnull; @@ -2989,9 +2984,56 @@ exec_eval_datum(PLpgSQL_execstate * estate, errmsg("type of \"%s\" does not match that when preparing the plan", var->refname))); break; + } + + case PLPGSQL_DTYPE_ROW: + { + PLpgSQL_row *row = (PLpgSQL_row *) datum; + HeapTuple tup; + + if (!row->rowtupdesc) /* should not happen */ + elog(ERROR, "row variable has no tupdesc"); + tup = make_tuple_from_row(estate, row, row->rowtupdesc); + if (tup == NULL) /* should not happen */ + elog(ERROR, "row not compatible with its own tupdesc"); + *typeid = row->rowtupdesc->tdtypeid; + *value = HeapTupleGetDatum(tup); + *isnull = false; + if (expectedtypeid != InvalidOid && expectedtypeid != *typeid) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("type of \"%s\" does not match that when preparing the plan", + row->refname))); + break; + } + + case PLPGSQL_DTYPE_REC: + { + PLpgSQL_rec *rec = (PLpgSQL_rec *) datum; + + if (!HeapTupleIsValid(rec->tup)) + ereport(ERROR, + (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE), + errmsg("record \"%s\" is not assigned yet", + rec->refname), + errdetail("The tuple structure of a not-yet-assigned record is indeterminate."))); + *typeid = rec->tupdesc->tdtypeid; + *value = HeapTupleGetDatum(rec->tup); + *isnull = false; + if (expectedtypeid != InvalidOid && expectedtypeid != *typeid) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("type of \"%s\" does not match that when preparing the plan", + rec->refname))); + break; + } case PLPGSQL_DTYPE_RECFIELD: - recfield = (PLpgSQL_recfield *) datum; + { + PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) datum; + PLpgSQL_rec *rec; + int fno; + rec = (PLpgSQL_rec *) (estate->datums[recfield->recparentno]); if (!HeapTupleIsValid(rec->tup)) ereport(ERROR, @@ -3013,9 +3055,13 @@ exec_eval_datum(PLpgSQL_execstate * estate, errmsg("type of \"%s.%s\" does not match that when preparing the plan", rec->refname, recfield->fieldname))); break; + } case PLPGSQL_DTYPE_TRIGARG: - trigarg = (PLpgSQL_trigarg *) datum; + { + PLpgSQL_trigarg *trigarg = (PLpgSQL_trigarg *) datum; + int tgargno; + *typeid = TEXTOID; tgargno = exec_eval_integer(estate, trigarg->argnum, isnull); if (*isnull || tgargno < 0 || tgargno >= estate->trig_nargs) @@ -3034,6 +3080,7 @@ exec_eval_datum(PLpgSQL_execstate * estate, errmsg("type of tgargv[%d] does not match that when preparing the plan", tgargno))); break; + } default: elog(ERROR, "unrecognized dtype: %d", datum->dtype); -- 2.40.0