diff --git a/doc/src/sgml/ref/create_function.sgml b/doc/src/sgml/ref/create_function.sgml index b95c2fd24e60ec3343170b4d6118569c5e22ced4..2a28925dff5c8f79d0848143c21190a0c8e33399 100644 --- a/doc/src/sgml/ref/create_function.sgml +++ b/doc/src/sgml/ref/create_function.sgml @@ -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. diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index 2bef065b11aff35da922d2225c6d7ba28cae8d12..c6f21e77b03be983c556726fa3c271b407c4b9e4 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -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) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 37c28495e546f0115282421c32d8e69df8151bdb..6cf2adcb5ba852e8366ecbf5a620a406b2b5398e 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -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; + } + ; /***************************************************************************** * diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 4cbbc1c980c805001fe2b682d44dd016633fa10d..a196779f44c190f474d51ef91b7a00781f4106fb 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -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) { diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index ff4cc278312204af65879b3a96d7320785df2dc6..31aadb449d598251dfe95fb64ed3849f66b4dc18 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -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; /* diff --git a/src/test/regress/input/create_function_2.source b/src/test/regress/input/create_function_2.source index b1c0eab138608bf17232ed586a0b40a3a458a721..4bcf24c6011d70f4c8dca76fe6e4f91810590610 100644 --- a/src/test/regress/input/create_function_2.source +++ b/src/test/regress/input/create_function_2.source @@ -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' diff --git a/src/test/regress/input/misc.source b/src/test/regress/input/misc.source index dbb8df847090ba1185eba934c84fe9fd610546c4..0fa63839e6051ef1e4e141b97a069503a4a66093 100644 --- a/src/test/regress/input/misc.source +++ b/src/test/regress/input/misc.source @@ -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 diff --git a/src/test/regress/output/create_function_2.source b/src/test/regress/output/create_function_2.source index a5f39a00bb00f2f7ae6174dc2977305181008cea..137242da70573cd0634aaf5ba5e11fe3685841f4 100644 --- a/src/test/regress/output/create_function_2.source +++ b/src/test/regress/output/create_function_2.source @@ -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' diff --git a/src/test/regress/output/misc.source b/src/test/regress/output/misc.source index 768dba5c3c7789af4804bbdfffe292f9d16fa9bc..57dc0fb4a4284ee26c945d55bec2c325aa3fda68 100644 --- a/src/test/regress/output/misc.source +++ b/src/test/regress/output/misc.source @@ -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 --