This patch adds support for %TYPE in CREATE FUNCTION argument and return

types.  This version has an elog() to remind the user the type
resolution is not dynamic.

Ian Lance Taylor
This commit is contained in:
Bruce Momjian 2001-06-04 23:27:23 +00:00
parent 0a93285d88
commit 28d2420eef
9 changed files with 170 additions and 13 deletions

View File

@ -1,5 +1,5 @@
<!--
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_function.sgml,v 1.23 2001/05/19 09:01:10 petere Exp $
$Header: /cvsroot/pgsql/doc/src/sgml/ref/create_function.sgml,v 1.24 2001/06/04 23:27:23 momjian Exp $
-->
<refentry id="SQL-CREATEFUNCTION">
@ -55,10 +55,16 @@ CREATE FUNCTION <replaceable class="parameter">name</replaceable> ( [ <replaceab
<listitem>
<para>
The data type(s) of the function's arguments, if any. The
input types may be base or complex types, or
<literal>opaque</literal>. <literal>Opaque</literal> indicates
input types may be base or complex types,
<literal>opaque</literal>, or the same as the type of an
existing column. <literal>Opaque</literal> indicates
that the function accepts arguments of a non-SQL type such as
<type>char *</type>.
The type of a column is indicated using <replaceable
class="parameter">tablename</replaceable>.<replaceable
class="parameter">columnname</replaceable><literal>%TYPE</literal>;
using this can sometimes help make a function independent from
changes to the definition of a table.
</para>
</listitem>
</varlistentry>
@ -69,8 +75,10 @@ CREATE FUNCTION <replaceable class="parameter">name</replaceable> ( [ <replaceab
<listitem>
<para>
The return data type. The output type may be specified as a
base type, complex type, <literal>setof</literal> type, or
<literal>opaque</literal>. The <literal>setof</literal>
base type, complex type, <literal>setof</literal> type,
<literal>opaque</literal>, or the same as the type of an
existing column.
The <literal>setof</literal>
modifier indicates that the function will return a set of
items, rather than a single item. Functions with a declared
return type of <literal>opaque</literal> do not return a value.

View File

@ -6,7 +6,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.188 2001/06/04 16:17:30 tgl Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.189 2001/06/04 23:27:23 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -29,6 +29,7 @@
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parse_type.h"
#include "parser/parse_expr.h"
#include "rewrite/rewriteManip.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
@ -51,7 +52,10 @@ static Node *transformSetOperationTree(ParseState *pstate, SelectStmt *stmt);
static Query *transformUpdateStmt(ParseState *pstate, UpdateStmt *stmt);
static Query *transformCreateStmt(ParseState *pstate, CreateStmt *stmt);
static Query *transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt);
static Node *transformTypeRefs(ParseState *pstate, Node *stmt);
static void transformTypeRefsList(ParseState *pstate, List *l);
static void transformTypeRef(ParseState *pstate, TypeName *tn);
static List *getSetColTypes(ParseState *pstate, Node *node);
static void transformForUpdate(Query *qry, List *forUpdate);
static void transformFkeyGetPrimaryKey(FkConstraint *fkconstraint, Oid *pktypoid);
@ -232,6 +236,17 @@ transformStmt(ParseState *pstate, Node *parseTree)
(SelectStmt *) parseTree);
break;
/*
* Convert use of %TYPE in statements where it is permitted.
*/
case T_ProcedureStmt:
case T_CommentStmt:
case T_RemoveFuncStmt:
case T_DefineStmt:
result = makeNode(Query);
result->commandType = CMD_UTILITY;
result->utilityStmt = transformTypeRefs(pstate, parseTree);
break;
default:
@ -2701,6 +2716,107 @@ transformAlterTableStmt(ParseState *pstate, AlterTableStmt *stmt)
return qry;
}
/*
* Transform uses of %TYPE in a statement.
*/
static Node *
transformTypeRefs(ParseState *pstate, Node *stmt)
{
switch (nodeTag(stmt))
{
case T_ProcedureStmt:
{
ProcedureStmt *ps = (ProcedureStmt *) stmt;
transformTypeRefsList(pstate, ps->argTypes);
transformTypeRef(pstate, (TypeName *) ps->returnType);
transformTypeRefsList(pstate, ps->withClause);
}
break;
case T_CommentStmt:
{
CommentStmt *cs = (CommentStmt *) stmt;
transformTypeRefsList(pstate, cs->objlist);
}
break;
case T_RemoveFuncStmt:
{
RemoveFuncStmt *rs = (RemoveFuncStmt *) stmt;
transformTypeRefsList(pstate, rs->args);
}
break;
case T_DefineStmt:
{
DefineStmt *ds = (DefineStmt *) stmt;
List *ele;
foreach(ele, ds->definition)
{
DefElem *de = (DefElem *) lfirst(ele);
if (de->arg != NULL
&& IsA(de->arg, TypeName))
{
transformTypeRef(pstate, (TypeName *) de->arg);
}
}
}
break;
default:
elog(ERROR, "Unsupported type %d in transformTypeRefs",
nodeTag(stmt));
break;
}
return stmt;
}
/*
* Transform uses of %TYPE in a list.
*/
static void
transformTypeRefsList(ParseState *pstate, List *l)
{
List *ele;
foreach(ele, l)
{
if (IsA(lfirst(ele), TypeName))
transformTypeRef(pstate, (TypeName *) lfirst(ele));
}
}
/*
* Transform a TypeName to not use %TYPE.
*/
static void
transformTypeRef(ParseState *pstate, TypeName *tn)
{
Attr *att;
Node *n;
Var *v;
char *tyn;
if (tn->attrname == NULL)
return;
att = makeAttr(tn->name, tn->attrname);
n = transformExpr(pstate, (Node *) att, EXPR_COLUMN_FIRST);
if (! IsA(n, Var))
elog(ERROR, "unsupported expression in %%TYPE");
v = (Var *) n;
tyn = typeidTypeName(v->vartype);
elog(NOTICE, "%s.%s%%TYPE converted to %s", tn->name, tn->attrname, tyn);
tn->name = tyn;
tn->typmod = v->vartypmod;
tn->attrname = NULL;
}
/* exported so planner can check again after rewriting, query pullup, etc */
void
CheckSelectForUpdate(Query *qry)

View File

@ -11,7 +11,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.227 2001/05/27 09:59:29 petere Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 2.228 2001/06/04 23:27:23 momjian Exp $
*
* HISTORY
* AUTHOR DATE MAJOR EVENT
@ -192,7 +192,7 @@ static void doNegateFloat(Value *v);
def_list, opt_indirection, group_clause, TriggerFuncArgs,
select_limit, opt_select_limit
%type <typnam> func_arg, func_return, aggr_argtype
%type <typnam> func_arg, func_return, func_type, aggr_argtype
%type <boolean> opt_arg, TriggerForOpt, TriggerForType, OptTemp
@ -2490,7 +2490,7 @@ func_args_list: func_arg
{ $$ = lappend($1, $3); }
;
func_arg: opt_arg Typename
func_arg: opt_arg func_type
{
/* We can catch over-specified arguments here if we want to,
* but for now better to silently swallow typmod, etc.
@ -2498,7 +2498,7 @@ func_arg: opt_arg Typename
*/
$$ = $2;
}
| Typename
| func_type
{
$$ = $1;
}
@ -2526,7 +2526,7 @@ func_as: Sconst
{ $$ = makeList2(makeString($1), makeString($3)); }
;
func_return: Typename
func_return: func_type
{
/* We can catch over-specified arguments here if we want to,
* but for now better to silently swallow typmod, etc.
@ -2536,6 +2536,18 @@ func_return: Typename
}
;
func_type: Typename
{
$$ = $1;
}
| IDENT '.' ColId '%' TYPE_P
{
$$ = makeNode(TypeName);
$$->name = $1;
$$->typmod = -1;
$$->attrname = $3;
}
;
/*****************************************************************************
*

View File

@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.96 2001/05/21 18:42:08 momjian Exp $
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.97 2001/06/04 23:27:23 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -942,6 +942,7 @@ parser_typecast_expression(ParseState *pstate,
char *
TypeNameToInternalName(TypeName *typename)
{
Assert(typename->attrname == NULL);
if (typename->arrayBounds != NIL)
{

View File

@ -7,7 +7,7 @@
* Portions Copyright (c) 1996-2001, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
* $Id: parsenodes.h,v 1.129 2001/05/21 18:42:08 momjian Exp $
* $Id: parsenodes.h,v 1.130 2001/06/04 23:27:23 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@ -951,6 +951,7 @@ typedef struct TypeName
bool setof; /* is a set? */
int32 typmod; /* type modifier */
List *arrayBounds; /* array bounds */
char *attrname; /* field name when using %TYPE */
} TypeName;
/*

View File

@ -13,6 +13,12 @@ CREATE FUNCTION hobby_construct(text, text)
LANGUAGE 'sql';
CREATE FUNCTION hobbies_by_name(hobbies_r.name%TYPE)
RETURNS hobbies_r.person%TYPE
AS 'select person from hobbies_r where name = $1'
LANGUAGE 'sql';
CREATE FUNCTION equipment(hobbies_r)
RETURNS setof equipment_r
AS 'select * from equipment_r where hobby = $1.name'

View File

@ -214,6 +214,7 @@ SELECT user_relns() AS user_relns
--SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer'))) AS equip_name;
SELECT hobbies_by_name('basketball');
--
-- check that old-style C functions work properly with TOASTed values

View File

@ -9,6 +9,12 @@ CREATE FUNCTION hobby_construct(text, text)
RETURNS hobbies_r
AS 'select $1 as name, $2 as hobby'
LANGUAGE 'sql';
CREATE FUNCTION hobbies_by_name(hobbies_r.name%TYPE)
RETURNS hobbies_r.person%TYPE
AS 'select person from hobbies_r where name = $1'
LANGUAGE 'sql';
NOTICE: hobbies_r.name%TYPE converted to text
NOTICE: hobbies_r.person%TYPE converted to text
CREATE FUNCTION equipment(hobbies_r)
RETURNS setof equipment_r
AS 'select * from equipment_r where hobby = $1.name'

View File

@ -656,6 +656,12 @@ SELECT user_relns() AS user_relns
(90 rows)
--SELECT name(equipment(hobby_construct(text 'skywalking', text 'mer'))) AS equip_name;
SELECT hobbies_by_name('basketball');
hobbies_by_name
-----------------
joe
(1 row)
--
-- check that old-style C functions work properly with TOASTed values
--