From 4089d2517560ec2265c42fcf7a786e04207f2787 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Sun, 27 Apr 2003 22:21:22 +0000 Subject: [PATCH] Fix plpgsql so that variables of composite types (rowtypes) can be 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 | 20 +++++--- src/pl/plpgsql/src/gram.y | 70 +++++++++++++++++-------- src/pl/plpgsql/src/pl_comp.c | 99 +++++++++++------------------------- src/pl/plpgsql/src/plpgsql.h | 16 +++--- 4 files changed, 99 insertions(+), 106 deletions(-) diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index 38306e100b..2aa0c07ed1 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,5 +1,5 @@ @@ -541,7 +541,8 @@ user_id users.user_id%TYPE; Row Types -name tablename%ROWTYPE; +name table_name%ROWTYPE; +name composite_type_name; @@ -550,17 +551,20 @@ user_id users.user_id%TYPE; can hold a whole row of a SELECT or FOR query result, so long as that query's column set matches the declared type of the variable. - tablename 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 rowvar.field. - Presently, a row variable can only be declared using the - %ROWTYPE notation; although one might expect a - bare table name to work as a type declaration, it won't be accepted - within PL/pgSQL functions. + A row variable can be declared to have the same type as the rows of + an existing table or view, by using the + table_name%ROWTYPE + 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 PostgreSQL whether you + write %ROWTYPE or not. But the form with + %ROWTYPE is more portable.) diff --git a/src/pl/plpgsql/src/gram.y b/src/pl/plpgsql/src/gram.y index 23dc894029..dd15cf80a2 100644 --- a/src/pl/plpgsql/src/gram.y +++ b/src/pl/plpgsql/src/gram.y @@ -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 ';' diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index feb80f94ae..5c88761e05 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -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; } diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index 3756d94c91..1140cebddc 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -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); -- GitLab