Fix plpgsql so that variables of composite types (rowtypes) can be
authorTom Lane <tgl@sss.pgh.pa.us>
Sun, 27 Apr 2003 22:21:22 +0000 (22:21 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sun, 27 Apr 2003 22:21:22 +0000 (22:21 +0000)
declared without having to write %ROWTYPE.  If the declared type of
a variable is a composite type, it'll be taken to be a row variable
automatically.

doc/src/sgml/plpgsql.sgml
src/pl/plpgsql/src/gram.y
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/plpgsql.h

index 38306e100b2d15b09d31a1e27418a9066eae7789..2aa0c07ed1bc15a762a1fb07f45fd3ec68cb9541 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$Header: /cvsroot/pgsql/doc/src/sgml/plpgsql.sgml,v 1.17 2003/04/07 01:29:25 petere Exp $
+$Header: /cvsroot/pgsql/doc/src/sgml/plpgsql.sgml,v 1.18 2003/04/27 22:21:22 tgl Exp $
 -->
 
 <chapter id="plpgsql"> 
@@ -541,7 +541,8 @@ user_id users.user_id%TYPE;
      <title>Row Types</title>
 
 <synopsis>
-<replaceable>name</replaceable> <replaceable>tablename</replaceable><literal>%ROWTYPE</literal>;
+<replaceable>name</replaceable> <replaceable>table_name</replaceable><literal>%ROWTYPE</literal>;
+<replaceable>name</replaceable> <replaceable>composite_type_name</replaceable>;
 </synopsis>
 
    <para>
@@ -550,17 +551,20 @@ user_id users.user_id%TYPE;
     can hold a whole row of a <command>SELECT</> or <command>FOR</>
     query result, so long as that query's column set matches the
     declared type of the variable.
-    <replaceable>tablename</replaceable> must be an existing table or
-    view name in the database. The individual fields of the row value
+    The individual fields of the row value
     are accessed using the usual dot notation, for example
     <literal>rowvar.field</literal>.
    </para>
 
    <para>
-    Presently, a row variable can only be declared using the
-    <literal>%ROWTYPE</literal> notation; although one might expect a
-    bare table name to work as a type declaration, it won't be accepted
-    within <application>PL/pgSQL</application> functions.
+    A row variable can be declared to have the same type as the rows of
+    an existing table or view, by using the
+    <replaceable>table_name</replaceable><literal>%ROWTYPE</literal>
+    notation; or it can be declared by giving a composite type's name.
+    (Since every table has an associated datatype of the same name,
+    it actually does not matter in <productname>PostgreSQL</> whether you
+    write <literal>%ROWTYPE</literal> or not.  But the form with
+    <literal>%ROWTYPE</literal> is more portable.)
    </para>
 
    <para>
index 23dc8940290e40dd043140be675403314b0d30d7..dd15cf80a2f83f6dd5a944940b54a163c686fa84 100644 (file)
@@ -4,7 +4,7 @@
  *                       procedural language
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.41 2003/03/25 03:16:40 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v 1.42 2003/04/27 22:21:22 tgl Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -307,36 +307,64 @@ decl_stmt     : '<' '<' opt_lblname '>' '>'
 
 decl_statement : decl_varname decl_const decl_datatype decl_notnull decl_defval
                    {
-                       PLpgSQL_var     *new;
+                       if (!OidIsValid($3->typrelid))
+                       {
+                           /* Ordinary scalar datatype */
+                           PLpgSQL_var     *var;
 
-                       new = malloc(sizeof(PLpgSQL_var));
-                       memset(new, 0, sizeof(PLpgSQL_var));
+                           var = malloc(sizeof(PLpgSQL_var));
+                           memset(var, 0, sizeof(PLpgSQL_var));
 
-                       new->dtype      = PLPGSQL_DTYPE_VAR;
-                       new->refname    = $1.name;
-                       new->lineno     = $1.lineno;
+                           var->dtype      = PLPGSQL_DTYPE_VAR;
+                           var->refname    = $1.name;
+                           var->lineno     = $1.lineno;
 
-                       new->datatype   = $3;
-                       new->isconst    = $2;
-                       new->notnull    = $4;
-                       new->default_val = $5;
+                           var->datatype   = $3;
+                           var->isconst    = $2;
+                           var->notnull    = $4;
+                           var->default_val = $5;
 
-                       plpgsql_adddatum((PLpgSQL_datum *)new);
-                       plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR, new->varno,
-                                               $1.name);
+                           plpgsql_adddatum((PLpgSQL_datum *)var);
+                           plpgsql_ns_additem(PLPGSQL_NSTYPE_VAR,
+                                              var->varno,
+                                              $1.name);
+                       }
+                       else
+                       {
+                           /* Composite type --- treat as rowtype */
+                           PLpgSQL_row    *row;
+
+                           row = build_rowtype($3->typrelid);
+                           row->dtype      = PLPGSQL_DTYPE_ROW;
+                           row->refname    = $1.name;
+                           row->lineno     = $1.lineno;
+
+                           if ($2)
+                               elog(ERROR, "Rowtype variable cannot be CONSTANT");
+                           if ($4)
+                               elog(ERROR, "Rowtype variable cannot be NOT NULL");
+                           if ($5 != NULL)
+                               elog(ERROR, "Default value for rowtype variable is not supported");
+
+                           plpgsql_adddatum((PLpgSQL_datum *)row);
+                           plpgsql_ns_additem(PLPGSQL_NSTYPE_ROW,
+                                              row->rowno,
+                                              $1.name);
+
+                       }
                    }
                | decl_varname K_RECORD ';'
                    {
-                       PLpgSQL_rec     *new;
+                       PLpgSQL_rec     *var;
 
-                       new = malloc(sizeof(PLpgSQL_rec));
+                       var = malloc(sizeof(PLpgSQL_rec));
 
-                       new->dtype      = PLPGSQL_DTYPE_REC;
-                       new->refname    = $1.name;
-                       new->lineno     = $1.lineno;
+                       var->dtype      = PLPGSQL_DTYPE_REC;
+                       var->refname    = $1.name;
+                       var->lineno     = $1.lineno;
 
-                       plpgsql_adddatum((PLpgSQL_datum *)new);
-                       plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, new->recno,
+                       plpgsql_adddatum((PLpgSQL_datum *)var);
+                       plpgsql_ns_additem(PLPGSQL_NSTYPE_REC, var->recno,
                                                $1.name);
                    }
                | decl_varname decl_rowtype ';'
index feb80f94ae78d146334dfbe90fb010330254f6bb..5c88761e05dd11fa0c69fe2d5539f809ed9c8eec 100644 (file)
@@ -3,7 +3,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.56 2003/04/24 21:16:44 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.57 2003/04/27 22:21:22 tgl Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -81,7 +81,7 @@ PLpgSQL_function *plpgsql_curr_compile;
 
 
 static void plpgsql_compile_error_callback(void *arg);
-static PLpgSQL_row *build_rowtype(Oid classOid);
+static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);
 
 
 /*
@@ -275,19 +275,11 @@ plpgsql_compile(Oid fn_oid, int functype)
                     */
                    var = malloc(sizeof(PLpgSQL_var));
                    memset(var, 0, sizeof(PLpgSQL_var));
-                   var->datatype = malloc(sizeof(PLpgSQL_type));
-                   memset(var->datatype, 0, sizeof(PLpgSQL_type));
 
                    var->dtype = PLPGSQL_DTYPE_VAR;
                    var->refname = strdup(buf);
                    var->lineno = 0;
-                   var->datatype->typname = strdup(NameStr(typeStruct->typname));
-                   var->datatype->typoid = procStruct->proargtypes[i];
-                   perm_fmgr_info(typeStruct->typinput, &(var->datatype->typinput));
-                   var->datatype->typelem = typeStruct->typelem;
-                   var->datatype->typbyval = typeStruct->typbyval;
-                   var->datatype->typlen = typeStruct->typlen;
-                   var->datatype->atttypmod = -1;
+                   var->datatype = build_datatype(typeTup, -1);
                    var->isconst = true;
                    var->notnull = false;
                    var->default_val = NULL;
@@ -908,7 +900,6 @@ plpgsql_parse_wordtype(char *word)
        if (HeapTupleIsValid(typeTup))
        {
            Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
-           PLpgSQL_type *typ;
 
            if (!typeStruct->typisdefined ||
                typeStruct->typrelid != InvalidOid)
@@ -918,17 +909,7 @@ plpgsql_parse_wordtype(char *word)
                return T_ERROR;
            }
 
-           typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type));
-
-           typ->typname = strdup(NameStr(typeStruct->typname));
-           typ->typoid = typeOid;
-           perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
-           typ->typelem = typeStruct->typelem;
-           typ->typbyval = typeStruct->typbyval;
-           typ->typlen = typeStruct->typlen;
-           typ->atttypmod = -1;
-
-           plpgsql_yylval.dtype = typ;
+           plpgsql_yylval.dtype = build_datatype(typeTup, -1);
 
            ReleaseSysCache(typeTup);
            pfree(cp[0]);
@@ -960,8 +941,6 @@ plpgsql_parse_dblwordtype(char *word)
    HeapTuple   attrtup;
    Form_pg_attribute attrStruct;
    HeapTuple   typetup;
-   Form_pg_type typeStruct;
-   PLpgSQL_type *typ;
    char       *cp[3];
    int         i;
 
@@ -1067,22 +1046,11 @@ plpgsql_parse_dblwordtype(char *word)
    if (!HeapTupleIsValid(typetup))
        elog(ERROR, "cache lookup for type %u of %s.%s failed",
             attrStruct->atttypid, cp[0], cp[1]);
-   typeStruct = (Form_pg_type) GETSTRUCT(typetup);
 
    /*
     * Found that - build a compiler type struct and return it
     */
-   typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type));
-
-   typ->typname = strdup(NameStr(typeStruct->typname));
-   typ->typoid = attrStruct->atttypid;
-   perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
-   typ->typelem = typeStruct->typelem;
-   typ->typbyval = typeStruct->typbyval;
-   typ->typlen = typeStruct->typlen;
-   typ->atttypmod = attrStruct->atttypmod;
-
-   plpgsql_yylval.dtype = typ;
+   plpgsql_yylval.dtype = build_datatype(typetup, attrStruct->atttypmod);
 
    ReleaseSysCache(classtup);
    ReleaseSysCache(attrtup);
@@ -1107,8 +1075,6 @@ plpgsql_parse_tripwordtype(char *word)
    HeapTuple   attrtup;
    Form_pg_attribute attrStruct;
    HeapTuple   typetup;
-   Form_pg_type typeStruct;
-   PLpgSQL_type *typ;
    char       *cp[2];
    char       *colname[1];
    int         qualified_att_len;
@@ -1192,22 +1158,11 @@ plpgsql_parse_tripwordtype(char *word)
    if (!HeapTupleIsValid(typetup))
        elog(ERROR, "cache lookup for type %u of %s.%s failed",
             attrStruct->atttypid, cp[0], cp[1]);
-   typeStruct = (Form_pg_type) GETSTRUCT(typetup);
 
    /*
     * Found that - build a compiler type struct and return it
     */
-   typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type));
-
-   typ->typname = strdup(NameStr(typeStruct->typname));
-   typ->typoid = attrStruct->atttypid;
-   perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
-   typ->typelem = typeStruct->typelem;
-   typ->typbyval = typeStruct->typbyval;
-   typ->typlen = typeStruct->typlen;
-   typ->atttypmod = attrStruct->atttypmod;
-
-   plpgsql_yylval.dtype = typ;
+   plpgsql_yylval.dtype = build_datatype(typetup, attrStruct->atttypmod);
 
    ReleaseSysCache(classtup);
    ReleaseSysCache(attrtup);
@@ -1296,7 +1251,7 @@ plpgsql_parse_dblwordrowtype(char *word)
 /*
  * Build a rowtype data structure given the pg_class OID.
  */
-static PLpgSQL_row *
+PLpgSQL_row *
 build_rowtype(Oid classOid)
 {
    PLpgSQL_row *row;
@@ -1341,7 +1296,6 @@ build_rowtype(Oid classOid)
        HeapTuple   attrtup;
        Form_pg_attribute attrStruct;
        HeapTuple   typetup;
-       Form_pg_type typeStruct;
        const char *attname;
        PLpgSQL_var *var;
 
@@ -1365,7 +1319,6 @@ build_rowtype(Oid classOid)
        if (!HeapTupleIsValid(typetup))
            elog(ERROR, "cache lookup for type %u of %s.%s failed",
                 attrStruct->atttypid, relname, attname);
-       typeStruct = (Form_pg_type) GETSTRUCT(typetup);
 
        /*
         * Create the internal variable
@@ -1384,14 +1337,7 @@ build_rowtype(Oid classOid)
        strcpy(var->refname, relname);
        strcat(var->refname, ".");
        strcat(var->refname, attname);
-       var->datatype = malloc(sizeof(PLpgSQL_type));
-       var->datatype->typname = strdup(NameStr(typeStruct->typname));
-       var->datatype->typoid = attrStruct->atttypid;
-       perm_fmgr_info(typeStruct->typinput, &(var->datatype->typinput));
-       var->datatype->typelem = typeStruct->typelem;
-       var->datatype->typbyval = typeStruct->typbyval;
-       var->datatype->typlen = typeStruct->typlen;
-       var->datatype->atttypmod = attrStruct->atttypmod;
+       var->datatype = build_datatype(typetup, attrStruct->atttypmod);
        var->isconst = false;
        var->notnull = false;
        var->default_val = NULL;
@@ -1428,7 +1374,6 @@ plpgsql_parse_datatype(char *string)
    Oid         type_id;
    int32       typmod;
    HeapTuple   typeTup;
-   Form_pg_type typeStruct;
    PLpgSQL_type *typ;
 
    /* Let the main parser try to parse it under standard SQL rules */
@@ -1440,20 +1385,34 @@ plpgsql_parse_datatype(char *string)
                             0, 0, 0);
    if (!HeapTupleIsValid(typeTup))
        elog(ERROR, "cache lookup failed for type %u", type_id);
-   typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
+
+   typ = build_datatype(typeTup, typmod);
+
+   ReleaseSysCache(typeTup);
+
+   return typ;
+}
+
+/*
+ * Utility subroutine to make a PLpgSQL_type struct given a pg_type entry
+ */
+static PLpgSQL_type *
+build_datatype(HeapTuple typeTup, int32 typmod)
+{
+   Form_pg_type typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
+   PLpgSQL_type *typ;
 
    typ = (PLpgSQL_type *) malloc(sizeof(PLpgSQL_type));
 
    typ->typname = strdup(NameStr(typeStruct->typname));
-   typ->typoid = type_id;
-   perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
-   typ->typelem = typeStruct->typelem;
-   typ->typbyval = typeStruct->typbyval;
+   typ->typoid = HeapTupleGetOid(typeTup);
    typ->typlen = typeStruct->typlen;
+   typ->typbyval = typeStruct->typbyval;
+   typ->typrelid = typeStruct->typrelid;
+   typ->typelem = typeStruct->typelem;
+   perm_fmgr_info(typeStruct->typinput, &(typ->typinput));
    typ->atttypmod = typmod;
 
-   ReleaseSysCache(typeTup);
-
    return typ;
 }
 
index 3756d94c911de447049ac7410c683846ab030dc7..1140cebddccf44cb911db2011ff540f0d42539f0 100644 (file)
@@ -3,7 +3,7 @@
  *           procedural language
  *
  * IDENTIFICATION
- *   $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.34 2003/04/24 21:16:44 tgl Exp $
+ *   $Header: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.35 2003/04/27 22:21:22 tgl Exp $
  *
  *   This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -142,14 +142,15 @@ typedef struct
 
 
 typedef struct
-{                              /* Postgres base data type      */
+{                              /* Postgres data type       */
    char       *typname;
-   Oid         typoid;
-   FmgrInfo    typinput;
-   Oid         typelem;
-   int16       typlen;
+   Oid         typoid;         /* OID of the data type */
+   int16       typlen;         /* stuff copied from its pg_type entry */
    bool        typbyval;
-   int32       atttypmod;
+   Oid         typrelid;
+   Oid         typelem;
+   FmgrInfo    typinput;       /* lookup info for typinput function */
+   int32       atttypmod;      /* typmod (taken from someplace else) */
 }  PLpgSQL_type;
 
 
@@ -600,6 +601,7 @@ extern int  plpgsql_parse_tripwordtype(char *word);
 extern int plpgsql_parse_wordrowtype(char *word);
 extern int plpgsql_parse_dblwordrowtype(char *word);
 extern PLpgSQL_type *plpgsql_parse_datatype(char *string);
+extern PLpgSQL_row *build_rowtype(Oid classOid);
 extern void plpgsql_adddatum(PLpgSQL_datum * new);
 extern int plpgsql_add_initdatums(int **varnos);
 extern void plpgsql_yyerror(const char *s);