This patch cannot be applied.  'active_simple_exprs' is referenced but
not defined.  I think the new variable name is 'simple_eval_estate',
which used to be a structure member of 'active_simple_exprs'.

Would you please update it against current CVS and resubmit?   Thanks.

---------------------------------------------------------------------------

Titus von Boxberg wrote:
> Tom Lane schrieb:
> > "Titus von Boxberg" <[EMAIL PROTECTED]> writes:
> > It works for me if we want to have an "NFIELDS" construct.  Personally
> > I'm still not convinced that we need one --- what's the use-case?
> I have removed the NFIELDS construct
> > 
> > 
> > I'd prefer arbitrary expression, but I suppose there's no harm in doing
> > the simple case first and generalizing if there's demand.
> I took the "no harm" way.
> 
> Attached please find the updated patch.
> The patch is against HEAD of 050721.
> 
> I switched the syntax to your proposal, renamed the functions
> in pl_comp.c and updated the sgml doc source and regression
> test files accordingly.
> 
> Regards
> Titus
> 

> *** ./doc/src/sgml/plpgsql.sgml.orig  Sat Jul  2 08:59:47 2005
> --- ./doc/src/sgml/plpgsql.sgml       Sat Jul 23 17:24:54 2005
> ***************
> *** 867,872 ****
> --- 867,921 ----
>      </para>
>   
>      <para>
> +     To obtain the values of the fields the record is made up of,
> +     the record variable can be qualified with the column or field
> +     name. This can be done either by literally using the column name
> +     or the column name for indexing the record can be taken out of a scalar
> +     variable. The syntax for this notation is 
> Record_variable.(IndexVariable).
> +     To get information about the column field names of the record, 
> +     a
>  special expression exists that returns all column names as an array: 
> +     RecordVariable.(*) .
> +     Thus, the RECORD can be viewed
> +     as an associative array that allows for introspection of it's contents.
> +     This feature is especially useful for writing generic triggers that
> +     operate on records with unknown structure.
> +     Here is an example procedure that shows column names and values
> +     of the predefined record NEW in a trigger procedure:
> + <programlisting>
> + 
> + CREATE OR REPLACE FUNCTION show_associative_records() RETURNS TRIGGER AS $$
> +     DECLARE
> +             colname         TEXT;
> +             colcontent      TEXT;
> +             colnames        TEXT[];
> +             coln            INT4;
> +             coli            INT4;
> +     BEGIN
> + -- obtain an array with all field names of the record
> +             colnames := NEW.(*);
> +             RAISE NOTICE 'All column names of test record: %', colnames;
> + -- show field names and contents of record
> +             coli := 1;
> +             coln := array_upper(colnames,1);
> +             RAISE NOTICE 'Number of columns in NEW: %', coln;
> +             FOR coli IN 1 .. coln LOOP
> +                     colname := colnames[coli];
> +                     colcontent := NEW.(colname);
> +                     RAISE NOTICE 'column % of NEW: %', 
> quote_ident(colname), quote_literal(colcontent);
> +             END LOOP;
> + -- Do it with a fixed field name:
> + -- will have to know the column name
> +             RAISE NOTICE 'column someint of NEW: %', 
> quote_literal(NEW.someint);
> +             RETURN NULL;
> +     END;
> + $$ LANGUAGE plpgsql;
> + --CREATE TABLE test_records (someint INT8, somestring TEXT);
> + --CREATE TRIGGER tr_test_record BEFORE INSERT ON test_records FOR EACH ROW 
> EXECUTE PROCEDURE show_associative_records();
> + 
> + </programlisting>
> +    </para>
> + 
> +    <para>
>       Note that <literal>RECORD</> is not a true data type, only a 
> placeholder.
>       One should also realize that when a <application>PL/pgSQL</application>
>       function is declared to return type <type>record</>, this is not quite 
> the
> *** ./src/pl/plpgsql/src/pl_comp.c.orig       Wed Jul  6 16:42:10 2005
> --- ./src/pl/plpgsql/src/pl_comp.c    Thu Jul 21 21:28:15 2005
> ***************
> *** 995,1001 ****
>   
>                               new = palloc(sizeof(PLpgSQL_recfield));
>                               new->dtype = PLPGSQL_DTYPE_RECFIELD;
> !                             new->fieldname = pstrdup(cp[1]);
>                               new->recparentno = ns->itemno;
>   
>                               plpgsql_adddatum((PLpgSQL_datum *) new);
> --- 995,1002 ----
>   
>                               new = palloc(sizeof(PLpgSQL_recfield));
>                               new->dtype = PLPGSQL_DTYPE_RECFIELD;
> !                             new->fieldindex.fieldname = pstrdup(cp[1]);
> !                             new->fieldindex_flag = RECFIELD_USE_FIELDNAME;
>                               new->recparentno = ns->itemno;
>   
>                               plpgsql_adddatum((PLpgSQL_datum *) new);
> ***************
> *** 1101,1107 ****
>   
>                               new = palloc(sizeof(PLpgSQL_recfield));
>                               new->dtype = PLPGSQL_DTYPE_RECFIELD;
> !                             new->fieldname = pstrdup(cp[2]);
>                               new->recparentno = ns->itemno;
>   
>                               plpgsql_adddatum((PLpgSQL_datum *) new);
> --- 1102,1109 ----
>   
>                               new = palloc(sizeof(PLpgSQL_recfield));
>                               new->dtype = PLPGSQL_DTYPE_RECFIELD;
> !                             new->fieldindex.fieldname = pstrdup(cp[2]);
> !                             new->fieldindex_flag = RECFIELD_USE_FIELDNAME;
>                               new->recparentno = ns->itemno;
>   
>                               plpgsql_adddatum((PLpgSQL_datum *) new);
> ***************
> *** 1550,1555 ****
> --- 1552,1683 ----
>       MemoryContextSwitchTo(oldCxt);
>       return T_DTYPE;
>   }
> + 
> + /* ----------
> +  * plpgsql_parse_recindex
> +  * lookup associative index into record
> +  * ----------
> +  */
> + int
> + plpgsql_parse_recindex(char *word)
> + {
> +     PLpgSQL_nsitem *ns1, *ns2;
> +     char            *cp[2];
> +     int             ret = T_ERROR;
> +     char            *fieldvar;
> +     int             fl;
> + 
> +     /* Do case conversion and word separation */
> +     plpgsql_convert_ident(word, cp, 2);
> +     Assert(cp[1] != NULL);
> + 
> +     /* cleanup the "(identifier)" string to "identifier" */
> +     fieldvar = cp[1];
> +     Assert(*fieldvar == '(');
> +     ++fieldvar;     /* get rid of ( */
> + 
> +     fl = strlen(fieldvar);
> +     Assert(fieldvar[fl-1] == ')');
> +     fieldvar[fl-1] = 0; /* get rid of ) */
> + 
> +     /*
> +      * Lookup the first word
> +      */
> +     ns1 = plpgsql_ns_lookup(cp[0], NULL);
> +     if ( ns1 == NULL )
> +     {
> +             pfree(cp[0]);
> +             pfree(cp[1]);
> +             return T_ERROR;
> +     }
> + 
> +     ns2 = plpgsql_ns_lookup(fieldvar, NULL);
> +     pfree(cp[0]);
> +     pfree(cp[1]);
> +     if ( ns2 == NULL )      /* name lookup failed */
> +             return T_ERROR;
> + 
> +     switch (ns1->itemtype)
> +     {
> +             case PLPGSQL_NSTYPE_REC:
> +                     {
> +                             /*
> +                              * First word is a record name, so second word 
> must be an
> +                              * variable holding the field name in this 
> record.
> +                              */
> +                             if ( ns2->itemtype == PLPGSQL_NSTYPE_VAR ) {
> +                                     PLpgSQL_recfield *new;
> + 
> +                                     new = palloc(sizeof(PLpgSQL_recfield));
> +                                     new->dtype = PLPGSQL_DTYPE_RECFIELD;
> +                                     new->fieldindex.indexvar_no = 
> ns2->itemno;
> +                                     new->fieldindex_flag = 
> RECFIELD_USE_INDEX_VAR;
> +                                     new->recparentno = ns1->itemno;
> + 
> +                                     plpgsql_adddatum((PLpgSQL_datum *) new);
> + 
> +                                     plpgsql_yylval.scalar = (PLpgSQL_datum 
> *) new;
> +                                     ret =  T_SCALAR;
> +                             } 
> +                             break;
> +                     }
> +             default:
> +                     break;
> +     }
> +     return ret;
> + } 
> + 
> + 
> + /* ----------
> +  * plpgsql_parse_recfieldnames
> +  * create fieldnames of a record
> +  * ----------
> +  */
> + int
> + plpgsql_parse_recfieldnames(char *word)
> + {
> +     PLpgSQL_nsitem  *ns1;
> +     char            *cp[2];
> +     int             ret = T_ERROR;
> + 
> +     /* Do case conversion and word separation */
> +     plpgsql_convert_ident(word, cp, 2);
> + 
> +     /*
> +      * Lookup the first word
> +      */
> +     ns1 = plpgsql_ns_lookup(cp[0], NULL);
> +     if ( ns1 == NULL )
> +     {
> +             pfree(cp[0]);
> +             pfree(cp[1]);
> +             return T_ERROR;
> +     }
> + 
> +     pfree(cp[0]);
> +     pfree(cp[1]);
> + 
> +     switch (ns1->itemtype)
> +     {
> +             case PLPGSQL_NSTYPE_REC:
> +                     {
> +                             PLpgSQL_recfieldproperties *new;
> + 
> +                             new = 
> palloc(sizeof(PLpgSQL_recfieldproperties));
> +                             new->dtype = PLPGSQL_DTYPE_RECFIELDNAMES;
> +                             new->recparentno = ns1->itemno;
> +                             new->save_fieldnames = NULL;
> +                             plpgsql_adddatum((PLpgSQL_datum *) new);
> +                             plpgsql_yylval.scalar = (PLpgSQL_datum *) new;
> +                             ret =  T_SCALAR;        /* ??? */
> +                             break;
> +                     }
> +             default:
> +                     break;
> +     }
> +     return ret;
> + }
> + 
>   
>   /*
>    * plpgsql_build_variable - build a datum-array entry of a given
> *** ./src/pl/plpgsql/src/pl_exec.c.orig       Sun Jun 26 22:05:42 2005
> --- ./src/pl/plpgsql/src/pl_exec.c    Sat Jul 23 17:13:32 2005
> ***************
> *** 716,721 ****
> --- 716,722 ----
>               case PLPGSQL_DTYPE_RECFIELD:
>               case PLPGSQL_DTYPE_ARRAYELEM:
>               case PLPGSQL_DTYPE_TRIGARG:
> +             case PLPGSQL_DTYPE_RECFIELDNAMES:
>                       /*
>                        * These datum records are read-only at runtime, so no 
> need
>                        * to copy them
> ***************
> *** 825,830 ****
> --- 826,832 ----
>   
>                       case PLPGSQL_DTYPE_RECFIELD:
>                       case PLPGSQL_DTYPE_ARRAYELEM:
> +                     case PLPGSQL_DTYPE_RECFIELDNAMES:
>                               break;
>   
>                       default:
> ***************
> *** 2146,2151 ****
> --- 2148,2155 ----
>   static void
>   exec_eval_cleanup(PLpgSQL_execstate *estate)
>   {
> +     int             i;
> +     ArrayType       *a;
>       /* Clear result of a full SPI_execute */
>       if (estate->eval_tuptable != NULL)
>               SPI_freetuptable(estate->eval_tuptable);
> ***************
> *** 2154,2159 ****
> --- 2158,2171 ----
>       /* Clear result of exec_eval_simple_expr (but keep the econtext) */
>       if (estate->eval_econtext != NULL)
>               ResetExprContext(estate->eval_econtext);
> +     for ( i = 0; i < estate->ndatums; ++i ) {
> +             if ( estate->datums[i]->dtype == PLPGSQL_DTYPE_RECFIELDNAMES ) {
> +                     a = ((PLpgSQL_recfieldproperties 
> *)(estate->datums[i]))->save_fieldnames;
> +                     if ( a )
> +                             pfree(a);
> +                     ((PLpgSQL_recfieldproperties 
> *)(estate->datums[i]))->save_fieldnames = NULL;
> +             }
> +     }
>   }
>   
>   
> ***************
> *** 3154,3165 ****
>                                * Get the number of the records field to 
> change and the
>                                * number of attributes in the tuple.
>                                */
> !                             fno = SPI_fnumber(rec->tupdesc, 
> recfield->fieldname);
> !                             if (fno == SPI_ERROR_NOATTRIBUTE)
> !                                     ereport(ERROR,
> !                                                     
> (errcode(ERRCODE_UNDEFINED_COLUMN),
> !                                                      errmsg("record \"%s\" 
> has no field \"%s\"",
> !                                                                     
> rec->refname, recfield->fieldname)));
>                               fno--;
>                               natts = rec->tupdesc->natts;
>   
> --- 3166,3200 ----
>                                * Get the number of the records field to 
> change and the
>                                * number of attributes in the tuple.
>                                */
> !                             if ( recfield->fieldindex_flag == 
> RECFIELD_USE_FIELDNAME ) {
> !                                     fno = SPI_fnumber(rec->tupdesc, 
> recfield->fieldindex.fieldname);
> !                                     if (fno == SPI_ERROR_NOATTRIBUTE)
> !                                             ereport(ERROR,
> !                                                             
> (errcode(ERRCODE_UNDEFINED_COLUMN),
> !                                                              errmsg("record 
> \"%s\" has no field \"%s\"",
> !                                                                             
> rec->refname, recfield->fieldindex.fieldname)));
> !                             }
> !                             else if ( recfield->fieldindex_flag == 
> RECFIELD_USE_INDEX_VAR ) {
> !                                     PLpgSQL_var * idxvar = (PLpgSQL_var *) 
> (estate->datums[recfield->fieldindex.indexvar_no]);
> !                                     char * fname = 
> convert_value_to_string(idxvar->value, idxvar->datatype->typoid);
> !                                     if ( fname == NULL )
> !                                             ereport(ERROR,
> !                                                             
> (errcode(ERRCODE_UNDEFINED_COLUMN),
> !                                                             errmsg("record 
> \"%s\": cannot evaluate variable to record index string",
> !                                                                             
> rec->refname)));
> !                                     fno = SPI_fnumber(rec->tupdesc, fname);
> !                                     pfree(fname);
> !                                     if (fno == SPI_ERROR_NOATTRIBUTE)
> !                                             ereport(ERROR,
> !                                                             
> (errcode(ERRCODE_UNDEFINED_COLUMN),
> !                                                              errmsg("record 
> \"%s\" has no field \"%s\"",
> !                                                                             
> rec->refname, fname)));
> !                             }
> !                             else
> !                                     ereport(ERROR,
> !                                             
> (errcode(ERRCODE_UNDEFINED_COLUMN),
> !                                             errmsg("record \"%s\": internal 
> error",
> !                                                                     
> rec->refname)));
>                               fno--;
>                               natts = rec->tupdesc->natts;
>   
> ***************
> *** 3497,3518 ****
>                                          errmsg("record \"%s\" is not 
> assigned yet",
>                                                         rec->refname),
>                                          errdetail("The tuple structure of a 
> not-yet-assigned record is indeterminate.")));
> !                             fno = SPI_fnumber(rec->tupdesc, 
> recfield->fieldname);
> !                             if (fno == SPI_ERROR_NOATTRIBUTE)
> !                                     ereport(ERROR,
> !                                                     
> (errcode(ERRCODE_UNDEFINED_COLUMN),
> !                                                      errmsg("record \"%s\" 
> has no field \"%s\"",
> !                                                                     
> rec->refname, recfield->fieldname)));
> !                             *typeid = SPI_gettypeid(rec->tupdesc, fno);
> !                             *value = SPI_getbinval(rec->tup, rec->tupdesc, 
> fno, isnull);
> !                             if (expectedtypeid != InvalidOid && 
> expectedtypeid != *typeid)
> !                                     ereport(ERROR,
> !                                                     
> (errcode(ERRCODE_DATATYPE_MISMATCH),
> !                                                      errmsg("type of 
> \"%s.%s\" does not match that when preparing the plan",
> !                                                                     
> rec->refname, recfield->fieldname)));
> !                             break;
> !                     }
> ! 
>               case PLPGSQL_DTYPE_TRIGARG:
>                       {
>                               PLpgSQL_trigarg *trigarg = (PLpgSQL_trigarg *) 
> datum;
> --- 3532,3656 ----
>                                          errmsg("record \"%s\" is not 
> assigned yet",
>                                                         rec->refname),
>                                          errdetail("The tuple structure of a 
> not-yet-assigned record is indeterminate.")));
> !                             if ( recfield->fieldindex_flag == 
> RECFIELD_USE_FIELDNAME ) {
> !                                     fno = SPI_fnumber(rec->tupdesc, 
> recfield->fieldindex.fieldname);
> !                                     if (fno == SPI_ERROR_NOATTRIBUTE)
> !                                             ereport(ERROR,
> !                                                             
> (errcode(ERRCODE_UNDEFINED_COLUMN),
> !                                                              errmsg("record 
> \"%s\" has no field \"%s\"",
> !                                                                             
> rec->refname, recfield->fieldindex.fieldname)));
> !                             }
> !                             else if ( recfield->fieldindex_flag == 
> RECFIELD_USE_INDEX_VAR ) {
> !                                     PLpgSQL_var * idxvar = (PLpgSQL_var *) 
> (estate->datums[recfield->fieldindex.indexvar_no]);
> !                                     char * fname = 
> convert_value_to_string(idxvar->value, idxvar->datatype->typoid);
> !                                     if ( fname == NULL )
> !                                             ereport(ERROR,
> !                                                             
> (errcode(ERRCODE_UNDEFINED_COLUMN),
> !                                                             errmsg("record 
> \"%s\": cannot evaluate variable to record index string",
> !                                                                             
> rec->refname)));
> !                                     fno = SPI_fnumber(rec->tupdesc, fname);
> !                                     pfree(fname);
> !                                     if (fno == SPI_ERROR_NOATTRIBUTE)
> !                                             ereport(ERROR,
> !                                                             
> (errcode(ERRCODE_UNDEFINED_COLUMN),
> !                                                              errmsg("record 
> \"%s\" has no field \"%s\"",
> !                                                                             
> rec->refname, fname)));
> !                             }
> !                             else
> !                                     ereport(ERROR,
> !                                             
> (errcode(ERRCODE_UNDEFINED_COLUMN),
> !                                             errmsg("record \"%s\": internal 
> error",
> !                                                             rec->refname)));
> ! 
> !                             /* Do not allow typeids to become "narrowed" by 
> InvalidOids 
> !                             causing specialized typeids from the tuple 
> restricting the destination */
> !                             if ( expectedtypeid != InvalidOid && 
> expectedtypeid != SPI_gettypeid(rec->tupdesc, fno) ) {
> !                                     Datum cval = SPI_getbinval(rec->tup, 
> rec->tupdesc, fno, isnull);
> !                                     cval =   exec_simple_cast_value(cval,
> !                                                                     
> SPI_gettypeid(rec->tupdesc, fno),
> !                                                                     
> expectedtypeid,
> !                                                                     -1,
> !                                                                     isnull);
> ! 
> !                                     *value = cval;
> !                                     *typeid = expectedtypeid;
> !                                     /* ereport(ERROR,
> !                                                     
> (errcode(ERRCODE_DATATYPE_MISMATCH),
> !                                                      errmsg("type of \"%s\" 
> does not match that when preparing the plan",
> !                                                                     
> rec->refname)));
> !                                     */
> !                             } 
> !                             else { /* expected typeid matches */
> !                                     *value = SPI_getbinval(rec->tup, 
> rec->tupdesc, fno, isnull);
> !                                     *typeid = SPI_gettypeid(rec->tupdesc, 
> fno);
> !                             } 
> !                             break;
> !                     }
> ! 
> !             case PLPGSQL_DTYPE_RECFIELDNAMES:
> !                     /* Construct array datum from record field names */
> !                     {
> !                             Oid                     arraytypeid,
> !                                                     arrayelemtypeid = 
> TEXTOID;
> !                             int16                   arraytyplen,
> !                                                     elemtyplen;
> !                             bool                    elemtypbyval;
> !                             char                    elemtypalign;
> !                             ArrayType               *arrayval;
> !                             PLpgSQL_recfieldproperties * recfp = 
> (PLpgSQL_recfieldproperties *) datum;
> !                             PLpgSQL_rec             *rec = (PLpgSQL_rec *) 
> (estate->datums[recfp->recparentno]);
> !                             int                     fc, tfc = 0;
> !                             Datum                   *arrayelems;
> !                             char                    *fieldname;
> ! 
> !                             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.")));
> !                             arrayelems = palloc(sizeof(Datum) * 
> rec->tupdesc->natts);
> !                             arraytypeid = get_array_type(arrayelemtypeid);
> !                             arraytyplen = get_typlen(arraytypeid);
> !                             get_typlenbyvalalign(arrayelemtypeid,
> !                                                  &elemtyplen,
> !                                                  &elemtypbyval,
> !                                                  &elemtypalign);
> ! 
> !                             if ( expectedtypeid != InvalidOid && 
> expectedtypeid != arraytypeid )
> !                                     ereport(ERROR,
> !                                                     
> (errcode(ERRCODE_DATATYPE_MISMATCH),
> !                                                      errmsg("type of \"%s\" 
> does not match array type when preparing the plan",
> !                                                                     
> rec->refname)));
> !                             for ( fc = 0; fc < rec->tupdesc->natts; ++fc ) {
> !                                     fieldname = SPI_fname(rec->tupdesc, 
> fc+1);
> !                                     if ( fieldname ) {
> !                                             arrayelems[fc] = 
> DirectFunctionCall1(textin, CStringGetDatum(fieldname));
> !                                             pfree(fieldname);
> !                                             ++tfc;
> !                                     } 
> !                             } 
> !                             arrayval = construct_array(arrayelems, tfc,
> !                                                      arrayelemtypeid,
> !                                                      elemtyplen,
> !                                                      elemtypbyval,
> !                                                      elemtypalign);
> ! 
> ! 
> !                             /* construct_array copies data; free temp elem 
> array */
> !                             for ( fc = 0; fc < tfc; ++fc )
> !                                     pfree(DatumGetPointer(arrayelems[fc]));
> !                             pfree(arrayelems);
> !                             *value = PointerGetDatum(arrayval);
> !                             *typeid = arraytypeid;
> !                             *isnull = false;
> !                             /* need to save the pointer because otherwise 
> it does not get freed */
> !                             if ( recfp->save_fieldnames )
> !                                     pfree(recfp->save_fieldnames);
> !                             recfp->save_fieldnames = arrayval;
> !                             break;
> !                     }
> ! 
>               case PLPGSQL_DTYPE_TRIGARG:
>                       {
>                               PLpgSQL_trigarg *trigarg = (PLpgSQL_trigarg *) 
> datum;
> ***************
> *** 3610,3616 ****
> --- 3748,3790 ----
>        */
>       if (expr->plan == NULL)
>               exec_prepare_plan(estate, expr);
> +     else {
> +             /*
> +              * check for any subexpressions with varying type in the 
> expression 
> +              * currently (July 05), this is a record field of a record 
> indexed by a variable
> +              */
> +             int                     i;
> +             PLpgSQL_datum           *d;
> +             PLpgSQL_recfield        *rf;
> +             for ( i = 0; i < expr->nparams; ++i ) {
> +                     d = estate->datums[expr->params[i]];
> +                     if ( d->dtype == PLPGSQL_DTYPE_RECFIELD ) {
> +                             rf = (PLpgSQL_recfield *)d;
> +                             if ( rf->fieldindex_flag == 
> RECFIELD_USE_INDEX_VAR )
> +                                     break;
> +                     }
> +             }
> +             if ( i < expr->nparams ) { /* expr may change it's type */
> +                     /* Make sure expr is not in the list of active simple 
> expressions
> +                        because 
> exec_prepare_plan()/exec_simple_check_plan()... 
> +                        will destroy the link to the next simple expression 
> */
> +                     PLpgSQL_expr *e, *enext;
> +                     PLpgSQL_expr **eprevnext = &active_simple_exprs;
> +                     for (e = active_simple_exprs; e; e = enext)
> +                     {
> +                             enext = e->expr_simple_next;
> +                             if ( e == expr ) /* found us in the list */
> +                                     *eprevnext = enext; /* discard us */
> +                             else
> +                                     eprevnext = &(e->expr_simple_next);
> +                     }
>   
> +                     /* now discard the plan and get new one */
> +                     SPI_freeplan(expr->plan);
> +                     expr->plan = NULL;
> +                     exec_prepare_plan(estate, expr);
> +             }
> +     }
>       /*
>        * If this is a simple expression, bypass SPI and use the executor
>        * directly
> *** ./src/pl/plpgsql/src/pl_funcs.c.orig      Wed Jun 22 01:35:02 2005
> --- ./src/pl/plpgsql/src/pl_funcs.c   Thu Jul 21 21:03:40 2005
> ***************
> *** 1066,1074 ****
>                               printf("REC %s\n", ((PLpgSQL_rec *) 
> d)->refname);
>                               break;
>                       case PLPGSQL_DTYPE_RECFIELD:
> !                             printf("RECFIELD %-16s of REC %d\n",
> !                                        ((PLpgSQL_recfield *) d)->fieldname,
> !                                        ((PLpgSQL_recfield *) 
> d)->recparentno);
>                               break;
>                       case PLPGSQL_DTYPE_ARRAYELEM:
>                               printf("ARRAYELEM of VAR %d subscript ",
> --- 1066,1078 ----
>                               printf("REC %s\n", ((PLpgSQL_rec *) 
> d)->refname);
>                               break;
>                       case PLPGSQL_DTYPE_RECFIELD:
> !                             if ( ((PLpgSQL_recfield *) d)->fieldindex_flag 
> == RECFIELD_USE_FIELDNAME )
> !                                     printf("RECFIELD %-16s of REC %d\n",
> !                                                ((PLpgSQL_recfield *) 
> d)->fieldindex.fieldname,
> !                                                ((PLpgSQL_recfield *) 
> d)->recparentno);
> !                             else
> !                                     printf("RECFIELD Variable of REC %d\n",
> !                                                ((PLpgSQL_recfield *) 
> d)->recparentno);
>                               break;
>                       case PLPGSQL_DTYPE_ARRAYELEM:
>                               printf("ARRAYELEM of VAR %d subscript ",
> *** ./src/pl/plpgsql/src/plpgsql.h.orig       Thu Jul 21 21:00:57 2005
> --- ./src/pl/plpgsql/src/plpgsql.h    Thu Jul 21 21:15:42 2005
> ***************
> *** 73,79 ****
>       PLPGSQL_DTYPE_RECFIELD,
>       PLPGSQL_DTYPE_ARRAYELEM,
>       PLPGSQL_DTYPE_EXPR,
> !     PLPGSQL_DTYPE_TRIGARG
>   };
>   
>   /* ----------
> --- 73,80 ----
>       PLPGSQL_DTYPE_RECFIELD,
>       PLPGSQL_DTYPE_ARRAYELEM,
>       PLPGSQL_DTYPE_EXPR,
> !     PLPGSQL_DTYPE_TRIGARG,
> !     PLPGSQL_DTYPE_RECFIELDNAMES
>   };
>   
>   /* ----------
> ***************
> *** 269,278 ****
>   {                                                           /* Field in 
> record */
>       int                     dtype;
>       int                     rfno;
> !     char       *fieldname;
>       int                     recparentno;    /* dno of parent record */
>   } PLpgSQL_recfield;
>   
>   
>   typedef struct
>   {                                                           /* Element of 
> array variable */
> --- 270,294 ----
>   {                                                           /* Field in 
> record */
>       int                     dtype;
>       int                     rfno;
> !     union {
> !             char    *fieldname;
> !             int     indexvar_no;            /* dno of variable holding 
> index string */
> !     } fieldindex;
> !     enum {
> !             RECFIELD_USE_FIELDNAME,
> !             RECFIELD_USE_INDEX_VAR,
> !     }       fieldindex_flag;
>       int                     recparentno;    /* dno of parent record */
>   } PLpgSQL_recfield;
>   
> + typedef struct
> + {                                                           /* Field in 
> record */
> +     int                     dtype;
> +     int                     rfno;
> +     int                     recparentno;                    /* dno of 
> parent record */
> +     ArrayType *             save_fieldnames;
> + } PLpgSQL_recfieldproperties;
> + 
>   
>   typedef struct
>   {                                                           /* Element of 
> array variable */
> ***************
> *** 678,683 ****
> --- 694,701 ----
>   extern int  plpgsql_parse_tripwordtype(char *word);
>   extern int  plpgsql_parse_wordrowtype(char *word);
>   extern int  plpgsql_parse_dblwordrowtype(char *word);
> + extern int  plpgsql_parse_recfieldnames(char *word);
> + extern int  plpgsql_parse_recindex(char *word);
>   extern PLpgSQL_type *plpgsql_parse_datatype(const char *string);
>   extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod);
>   extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int 
> lineno,
> *** ./src/pl/plpgsql/src/scan.l.orig  Sun Jun 26 19:16:07 2005
> --- ./src/pl/plpgsql/src/scan.l       Thu Jul 21 21:28:20 2005
> ***************
> *** 243,248 ****
> --- 243,254 ----
>   {param}{space}*\.{space}*{identifier}{space}*%ROWTYPE       {
>       plpgsql_error_lineno = plpgsql_scanner_lineno();
>       return plpgsql_parse_dblwordrowtype(yytext); }
> + {identifier}{space}*\.\(\*\)                {
> +     plpgsql_error_lineno = plpgsql_scanner_lineno();
> +     return plpgsql_parse_recfieldnames(yytext); }
> + {identifier}{space}*\.\({identifier}\)              {
> +     plpgsql_error_lineno = plpgsql_scanner_lineno();
> +     return plpgsql_parse_recindex(yytext); }
>   
>   {digit}+            { return T_NUMBER;                      }
>   
> *** ./src/test/regress/expected/plpgsql.out.orig      Sat Jul  2 08:59:48 2005
> --- ./src/test/regress/expected/plpgsql.out   Thu Jul 21 22:32:52 2005
> ***************
> *** 2721,2723 ****
> --- 2721,2761 ----
>   $$ language plpgsql;
>   ERROR:  end label "outer_label" specified for unlabelled block
>   CONTEXT:  compile of PL/pgSQL function "end_label4" near line 5
> + -- check introspective records
> + create table ritest (i INT4, t TEXT);
> + insert into ritest (i, t) VALUES (1, 'sometext');
> + create function test_record() returns void as $$
> + declare
> +   cname text;
> +   tval  text;
> +   ival  int4;
> +   tval2 text;
> +   ival2 int4;
> +   columns text[];
> +   r     RECORD;
> + begin
> +   SELECT INTO r * FROM ritest WHERE i = 1;
> +   ival := r.i;
> +   tval := r.t;
> +   RAISE NOTICE 'ival=%, tval=%', ival, tval;
> +   cname := 'i';
> +   ival2 := r.(cname);
> +   cname :='t';
> +   tval2 := r.(cname);
> +   RAISE NOTICE 'ival2=%, tval2=%', ival2, tval2;
> +   columns := r.(*);
> +   RAISE NOTICE 'fieldnames=%', columns;
> +   RETURN;
> + end;
> + $$ language plpgsql;
> + select test_record();
> + NOTICE:  ival=1, tval=sometext
> + NOTICE:  ival2=1, tval2=sometext
> + NOTICE:  fieldnames={i,t}
> +  test_record 
> + -------------
> +  
> +  (1 row)
> + 
> + drop table ritest;
> + drop function test_record();
> *** ./src/test/regress/sql/plpgsql.sql.orig   Thu Jul 21 21:04:43 2005
> --- ./src/test/regress/sql/plpgsql.sql        Thu Jul 21 21:36:11 2005
> ***************
> *** 2280,2282 ****
> --- 2280,2314 ----
>     end loop outer_label;
>   end;
>   $$ language plpgsql;
> + 
> + -- check introspective records
> + create table ritest (i INT4, t TEXT);
> + insert into ritest (i, t) VALUES (1, 'sometext');
> + create function test_record() returns void as $$
> + declare
> +   cname text;
> +   tval  text;
> +   ival  int4;
> +   tval2 text;
> +   ival2 int4;
> +   columns text[];
> +   r     RECORD;
> + begin
> +   SELECT INTO r * FROM ritest WHERE i = 1;
> +   ival := r.i;
> +   tval := r.t;
> +   RAISE NOTICE 'ival=%, tval=%', ival, tval;
> +   cname := 'i';
> +   ival2 := r.(cname);
> +   cname :='t';
> +   tval2 := r.(cname);
> +   RAISE NOTICE 'ival2=%, tval2=%', ival2, tval2;
> +   columns := r.(*);
> +   RAISE NOTICE 'fieldnames=%', columns;
> +   RETURN;
> + end;
> + $$ language plpgsql;
> + select test_record();
> + drop table ritest;
> + drop function test_record();
> + 

> 
> ---------------------------(end of broadcast)---------------------------
> TIP 9: In versions below 8.0, the planner will ignore your desire to
>        choose an index scan if your joining column's datatypes do not
>        match

-- 
  Bruce Momjian   http://candle.pha.pa.us
  EnterpriseDB    http://www.enterprisedb.com

  + If your life is a hard drive, Christ can be your backup. +

---------------------------(end of broadcast)---------------------------
TIP 3: Have you checked our extensive FAQ?

               http://www.postgresql.org/docs/faq

Reply via email to