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:
parent
0a93285d88
commit
28d2420eef
doc/src/sgml/ref
src
backend/parser
include/nodes
test/regress
@ -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.
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
}
|
||||
;
|
||||
|
||||
/*****************************************************************************
|
||||
*
|
||||
|
@ -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)
|
||||
{
|
||||
|
||||
|
@ -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;
|
||||
|
||||
/*
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
|
@ -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'
|
||||
|
@ -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
|
||||
--
|
||||
|
Loading…
x
Reference in New Issue
Block a user