Implement a psql command "\ef" to edit the definition of a function.
authorTom Lane <tgl@sss.pgh.pa.us>
Sat, 6 Sep 2008 00:01:25 +0000 (00:01 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Sat, 6 Sep 2008 00:01:25 +0000 (00:01 +0000)
In support of that, create a backend function pg_get_functiondef().
The psql command is functional but maybe a bit rough around the edges...

Abhijit Menon-Sen

doc/src/sgml/func.sgml
doc/src/sgml/ref/psql-ref.sgml
src/backend/utils/adt/ruleutils.c
src/bin/psql/command.c
src/include/catalog/catversion.h
src/include/catalog/pg_proc.h
src/include/utils/builtins.h

index 15162ed5ed31033d44d1b4201e228c5e4a31c4bc..7d8608d01e595c636b4dd01ba1cbd5200557c62f 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.443 2008/07/21 04:47:00 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.444 2008/09/06 00:01:21 tgl Exp $ -->
 
  <chapter id="functions">
   <title>Functions and Operators</title>
@@ -11562,6 +11562,10 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
     <primary>pg_get_ruledef</primary>
    </indexterm>
 
+   <indexterm>
+    <primary>pg_get_functiondef</primary>
+   </indexterm>
+
    <indexterm>
     <primary>pg_get_function_arguments</primary>
    </indexterm>
@@ -11643,6 +11647,11 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
        <entry>decompile internal form of an expression, assuming that any Vars
        in it refer to the relation indicated by the second parameter</entry>
       </row>
+      <row>
+       <entry><literal><function>pg_get_functiondef</function>(<parameter>func_oid</parameter>)</literal></entry>
+       <entry><type>text</type></entry>
+       <entry>get definition of a function</entry>
+      </row>
       <row>
        <entry><literal><function>pg_get_function_arguments</function>(<parameter>func_oid</parameter>)</literal></entry>
        <entry><type>text</type></entry>
@@ -11756,6 +11765,8 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
   </para>
 
   <para>
+   <function>pg_get_functiondef</> returns a complete
+   <command>CREATE OR REPLACE FUNCTION</> statement for a function.
    <function>pg_get_function_arguments</function> returns the argument list
    of a function, in the form it would need to appear in within
    <command>CREATE FUNCTION</>.
index eca303857489742394357896c07f8b13914f4548..2eedbb54b4698c5b56579afa2ce889b0eed2309c 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.209 2008/07/03 03:37:16 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.210 2008/09/06 00:01:21 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -1195,6 +1195,29 @@ testdb=&gt;
       </varlistentry>
 
 
+      <varlistentry>
+        <term><literal>\ef <replaceable class="parameter">function_description</replaceable> </literal></term>
+
+        <listitem>
+        <para>
+         This command fetches and edits the definition of the named function,
+         in the form of a <command>CREATE OR REPLACE FUNCTION</> command.
+         Editing is done in the same way as for <literal>\e</>.
+         After the editor exits, the updated command waits in the query buffer;
+         type semicolon or <literal>\g</> to send it, or <literal>\r</>
+         to cancel.
+        </para>
+
+        <para>
+         The target function can be specified by name alone, or by name
+         and arguments, for example <literal>foo(integer, text)</>.
+         The argument types must be given if there is more
+         than one function of the same name.
+        </para>
+        </listitem>
+      </varlistentry>
+
+
       <varlistentry>
         <term><literal>\echo <replaceable class="parameter">text</replaceable> [ ... ]</literal></term>
         <listitem>
index 66bbaaaa68dd935f92f2670bc127fb8171e55ee4..07c39fefdad572e747b30d60b2beb6f7c9231168 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.282 2008/09/01 20:42:45 tgl Exp $
+ *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.283 2008/09/06 00:01:21 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -137,6 +137,7 @@ static char *pg_get_expr_worker(text *expr, Oid relid, char *relname,
                                   int prettyFlags);
 static int print_function_arguments(StringInfo buf, HeapTuple proctup,
                                                 bool print_table_args);
+static void print_function_rettype(StringInfo buf, HeapTuple proctup);
 static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
                         int prettyFlags);
 static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
@@ -180,6 +181,7 @@ static void get_coercion_expr(Node *arg, deparse_context *context,
                                  Node *parentNode);
 static void get_const_expr(Const *constval, deparse_context *context,
                           int showtype);
+static void simple_quote_literal(StringInfo buf, const char *val);
 static void get_sublink_expr(SubLink *sublink, deparse_context *context);
 static void get_from_clause(Query *query, const char *prefix,
                                deparse_context *context);
@@ -553,23 +555,10 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
                {
                        if (i > 0)
                                appendStringInfo(&buf, ", ");
-
-                       /*
-                        * We form the string literal according to the prevailing setting
-                        * of standard_conforming_strings; we never use E''. User is
-                        * responsible for making sure result is used correctly.
-                        */
-                       appendStringInfoChar(&buf, '\'');
-                       while (*p)
-                       {
-                               char            ch = *p++;
-
-                               if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
-                                       appendStringInfoChar(&buf, ch);
-                               appendStringInfoChar(&buf, ch);
-                       }
-                       appendStringInfoChar(&buf, '\'');
+                       simple_quote_literal(&buf, p);
                        /* advance p to next string embedded in tgargs */
+                       while (*p)
+                               p++;
                        p++;
                }
        }
@@ -1397,6 +1386,182 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS)
 }
 
 
+/*
+ * pg_get_functiondef
+ *             Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
+ *             the specified function.
+ */
+Datum
+pg_get_functiondef(PG_FUNCTION_ARGS)
+{
+       Oid                     funcid = PG_GETARG_OID(0);
+       StringInfoData buf;
+       StringInfoData dq;
+       HeapTuple       proctup;
+       HeapTuple       langtup;
+       Form_pg_proc proc;
+       Form_pg_language lang;
+       Datum           tmp;
+       bool            isnull;
+       const char *prosrc;
+       const char *name;
+       const char *nsp;
+       float4          procost;
+       int                     oldlen;
+
+       initStringInfo(&buf);
+
+       /* Look up the function */
+       proctup = SearchSysCache(PROCOID,
+                                                        ObjectIdGetDatum(funcid),
+                                                        0, 0, 0);
+       if (!HeapTupleIsValid(proctup))
+               elog(ERROR, "cache lookup failed for function %u", funcid);
+       proc = (Form_pg_proc) GETSTRUCT(proctup);
+       name = NameStr(proc->proname);
+
+       if (proc->proisagg)
+               ereport(ERROR,
+                               (errcode(ERRCODE_WRONG_OBJECT_TYPE),
+                                errmsg("\"%s\" is an aggregate function", name)));
+
+       /* Need its pg_language tuple for the language name */
+       langtup = SearchSysCache(LANGOID,
+                                                        ObjectIdGetDatum(proc->prolang),
+                                                        0, 0, 0);
+       if (!HeapTupleIsValid(langtup))
+               elog(ERROR, "cache lookup failed for language %u", proc->prolang);
+       lang = (Form_pg_language) GETSTRUCT(langtup);
+
+       /*
+        * We always qualify the function name, to ensure the right function
+        * gets replaced.
+        */
+       nsp = get_namespace_name(proc->pronamespace);
+       appendStringInfo(&buf, "CREATE OR REPLACE FUNCTION %s(",
+                                        quote_qualified_identifier(nsp, name));
+       (void) print_function_arguments(&buf, proctup, false);
+       appendStringInfoString(&buf, ")\n RETURNS ");
+       print_function_rettype(&buf, proctup);
+       appendStringInfo(&buf, "\n LANGUAGE %s\n",
+                                        quote_identifier(NameStr(lang->lanname)));
+
+       /* Emit some miscellaneous options on one line */
+       oldlen = buf.len;
+
+       switch (proc->provolatile)
+       {
+               case PROVOLATILE_IMMUTABLE:
+                       appendStringInfoString(&buf, " IMMUTABLE");
+                       break;
+               case PROVOLATILE_STABLE:
+                       appendStringInfoString(&buf, " STABLE");
+                       break;
+               case PROVOLATILE_VOLATILE:
+                       break;
+       }
+       if (proc->proisstrict)
+               appendStringInfoString(&buf, " STRICT");
+       if (proc->prosecdef)
+               appendStringInfoString(&buf, " SECURITY DEFINER");
+
+       /* This code for the default cost and rows should match functioncmds.c */
+       if (proc->prolang == INTERNALlanguageId ||
+               proc->prolang == ClanguageId)
+               procost = 1;
+       else
+               procost = 100;
+       if (proc->procost != procost)
+               appendStringInfo(&buf, " COST %g", proc->procost);
+
+       if (proc->prorows > 0 && proc->prorows != 1000)
+               appendStringInfo(&buf, " ROWS %g", proc->prorows);
+
+       if (oldlen != buf.len)
+               appendStringInfoChar(&buf, '\n');
+
+       /* Emit any proconfig options, one per line */
+       tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proconfig, &isnull);
+       if (!isnull)
+       {
+               ArrayType       *a = DatumGetArrayTypeP(tmp);
+               int                     i;
+
+               Assert(ARR_ELEMTYPE(a) == TEXTOID);
+               Assert(ARR_NDIM(a) == 1);
+               Assert(ARR_LBOUND(a)[0] == 1);
+
+               for (i = 1; i <= ARR_DIMS(a)[0]; i++)
+               {
+                       Datum   d;
+
+                       d = array_ref(a, 1, &i,
+                                                 -1 /* varlenarray */ ,
+                                                 -1 /* TEXT's typlen */ ,
+                                                 false /* TEXT's typbyval */ ,
+                                                 'i' /* TEXT's typalign */ ,
+                                                 &isnull);
+                       if (!isnull)
+                       {
+                               char       *configitem = TextDatumGetCString(d);
+                               char       *pos;
+
+                               pos = strchr(configitem, '=');
+                               if (pos == NULL)
+                                       continue;
+                               *pos++ = '\0';
+
+                               appendStringInfo(&buf, " SET %s TO ",
+                                                                quote_identifier(configitem));
+
+                               /*
+                                * Some GUC variable names are 'LIST' type and hence must not
+                                * be quoted.
+                                */
+                               if (pg_strcasecmp(configitem, "DateStyle") == 0
+                                       || pg_strcasecmp(configitem, "search_path") == 0)
+                                       appendStringInfoString(&buf, pos);
+                               else
+                                       simple_quote_literal(&buf, pos);
+                               appendStringInfoChar(&buf, '\n');
+                       }
+               }
+       }
+
+       /* And finally the function definition ... */
+       appendStringInfoString(&buf, "AS ");
+
+       tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_probin, &isnull);
+       if (!isnull)
+       {
+               simple_quote_literal(&buf, TextDatumGetCString(tmp));
+               appendStringInfoString(&buf, ", ");             /* assume prosrc isn't null */
+       }
+
+       tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosrc, &isnull);
+       if (isnull)
+               elog(ERROR, "null prosrc");
+       prosrc = TextDatumGetCString(tmp);
+
+       /* We always use dollar quoting.  Figure out a suitable delimiter. */
+       initStringInfo(&dq);
+       appendStringInfoChar(&dq, '$');
+       while (strstr(prosrc, dq.data) != NULL)
+               appendStringInfoChar(&dq, 'x');
+       appendStringInfoChar(&dq, '$');
+
+       appendStringInfoString(&buf, dq.data);
+       appendStringInfoString(&buf, prosrc);
+       appendStringInfoString(&buf, dq.data);
+
+       appendStringInfoString(&buf, "\n");
+
+       ReleaseSysCache(langtup);
+       ReleaseSysCache(proctup);
+
+       PG_RETURN_TEXT_P(string_to_text(buf.data));
+}
+
 /*
  * pg_get_function_arguments
  *             Get a nicely-formatted list of arguments for a function.
@@ -1436,8 +1601,6 @@ pg_get_function_result(PG_FUNCTION_ARGS)
        Oid                     funcid = PG_GETARG_OID(0);
        StringInfoData buf;
        HeapTuple       proctup;
-       Form_pg_proc procform;
-       int                     ntabargs = 0;
 
        initStringInfo(&buf);
 
@@ -1446,30 +1609,47 @@ pg_get_function_result(PG_FUNCTION_ARGS)
                                                         0, 0, 0);
        if (!HeapTupleIsValid(proctup))
                elog(ERROR, "cache lookup failed for function %u", funcid);
-       procform = (Form_pg_proc) GETSTRUCT(proctup);
 
-       if (procform->proretset)
+       print_function_rettype(&buf, proctup);
+
+       ReleaseSysCache(proctup);
+
+       PG_RETURN_TEXT_P(string_to_text(buf.data));
+}
+
+/*
+ * Guts of pg_get_function_result: append the function's return type
+ * to the specified buffer.
+ */
+static void
+print_function_rettype(StringInfo buf, HeapTuple proctup)
+{
+       Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
+       int                     ntabargs = 0;
+       StringInfoData rbuf;
+
+       initStringInfo(&rbuf);
+
+       if (proc->proretset)
        {
                /* It might be a table function; try to print the arguments */
-               appendStringInfoString(&buf, "TABLE(");
-               ntabargs = print_function_arguments(&buf, proctup, true);
+               appendStringInfoString(&rbuf, "TABLE(");
+               ntabargs = print_function_arguments(&rbuf, proctup, true);
                if (ntabargs > 0)
-                       appendStringInfoString(&buf, ")");
+                       appendStringInfoString(&rbuf, ")");
                else
-                       resetStringInfo(&buf);
+                       resetStringInfo(&rbuf);
        }
 
        if (ntabargs == 0)
        {
                /* Not a table function, so do the normal thing */
-               if (procform->proretset)
-                       appendStringInfoString(&buf, "SETOF ");
-               appendStringInfoString(&buf, format_type_be(procform->prorettype));
+               if (proc->proretset)
+                       appendStringInfoString(&rbuf, "SETOF ");
+               appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
        }
 
-       ReleaseSysCache(proctup);
-
-       PG_RETURN_TEXT_P(string_to_text(buf.data));
+       appendStringInfoString(buf, rbuf.data);
 }
 
 /*
@@ -4597,7 +4777,6 @@ get_const_expr(Const *constval, deparse_context *context, int showtype)
        Oid                     typoutput;
        bool            typIsVarlena;
        char       *extval;
-       char       *valptr;
        bool            isfloat = false;
        bool            needlabel;
 
@@ -4672,22 +4851,7 @@ get_const_expr(Const *constval, deparse_context *context, int showtype)
                        break;
 
                default:
-
-                       /*
-                        * We form the string literal according to the prevailing setting
-                        * of standard_conforming_strings; we never use E''. User is
-                        * responsible for making sure result is used correctly.
-                        */
-                       appendStringInfoChar(buf, '\'');
-                       for (valptr = extval; *valptr; valptr++)
-                       {
-                               char            ch = *valptr;
-
-                               if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
-                                       appendStringInfoChar(buf, ch);
-                               appendStringInfoChar(buf, ch);
-                       }
-                       appendStringInfoChar(buf, '\'');
+                       simple_quote_literal(buf, extval);
                        break;
        }
 
@@ -4729,6 +4893,31 @@ get_const_expr(Const *constval, deparse_context *context, int showtype)
                                                                                                  constval->consttypmod));
 }
 
+/*
+ * simple_quote_literal - Format a string as a SQL literal, append to buf
+ */
+static void
+simple_quote_literal(StringInfo buf, const char *val)
+{
+       const char *valptr;
+
+       /*
+        * We form the string literal according to the prevailing setting
+        * of standard_conforming_strings; we never use E''. User is
+        * responsible for making sure result is used correctly.
+        */
+       appendStringInfoChar(buf, '\'');
+       for (valptr = val; *valptr; valptr++)
+       {
+               char            ch = *valptr;
+
+               if (SQL_STR_DOUBLE(ch, !standard_conforming_strings))
+                       appendStringInfoChar(buf, ch);
+               appendStringInfoChar(buf, ch);
+       }
+       appendStringInfoChar(buf, '\'');
+}
+
 
 /* ----------
  * get_sublink_expr                    - Parse back a sublink
index 958e134a309639be972cf4b83d78554a4a076104..e1887a2472d402b2ab909569f9004ead447e471f 100644 (file)
@@ -3,7 +3,7 @@
  *
  * Copyright (c) 2000-2008, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.193 2008/08/16 00:16:56 momjian Exp $
+ * $PostgreSQL: pgsql/src/bin/psql/command.c,v 1.194 2008/09/06 00:01:24 tgl Exp $
  */
 #include "postgres_fe.h"
 #include "command.h"
 static backslashResult exec_command(const char *cmd,
                         PsqlScanState scan_state,
                         PQExpBuffer query_buf);
-static bool do_edit(const char *filename_arg, PQExpBuffer query_buf);
+static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
+                                       bool *edited);
 static bool do_connect(char *dbname, char *user, char *host, char *port);
 static bool do_shell(const char *command);
+static bool lookup_function_oid(PGconn *conn, const char *desc, Oid *foid);
+static bool get_create_function_cmd(PGconn *conn, Oid oid, PQExpBuffer buf);
 
 #ifdef USE_SSL
 static void printSSLInfo(void);
@@ -444,11 +447,64 @@ exec_command(const char *cmd,
                        expand_tilde(&fname);
                        if (fname)
                                canonicalize_path(fname);
-                       status = do_edit(fname, query_buf) ? PSQL_CMD_NEWEDIT : PSQL_CMD_ERROR;
+                       if (do_edit(fname, query_buf, NULL))
+                               status = PSQL_CMD_NEWEDIT;
+                       else
+                               status = PSQL_CMD_ERROR;
                        free(fname);
                }
        }
 
+       /*
+        * \ef -- edit the named function in $EDITOR.
+        */
+       else if (strcmp(cmd, "ef") == 0)
+       {
+               char   *func;
+               Oid             foid;
+
+               func = psql_scan_slash_option(scan_state, OT_WHOLE_LINE, NULL, true);
+               if (!func)
+               {
+                       psql_error("no function name specified\n");
+                       status = PSQL_CMD_ERROR;
+               }
+               else if (!lookup_function_oid(pset.db, func, &foid))
+               {
+                       psql_error(PQerrorMessage(pset.db));
+                       status = PSQL_CMD_ERROR;
+               }
+               else if (!query_buf)
+               {
+                       psql_error("no query buffer\n");
+                       status = PSQL_CMD_ERROR;
+               }
+               else if (!get_create_function_cmd(pset.db, foid, query_buf))
+               {
+                       psql_error(PQerrorMessage(pset.db));
+                       status = PSQL_CMD_ERROR;
+               }
+               else
+               {
+                       bool edited = false;
+
+                       if (!do_edit(0, query_buf, &edited))
+                       {
+                               status = PSQL_CMD_ERROR;
+                       }
+                       else if (!edited)
+                       {
+                               printf("No changes\n");
+                       }
+                       else
+                       {
+                               status = PSQL_CMD_NEWEDIT;
+                       }
+               }
+               if (func)
+                       free(func);
+       }
+
        /* \echo and \qecho */
        else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0)
        {
@@ -1410,7 +1466,7 @@ editFile(const char *fname)
 
 /* call this one */
 static bool
-do_edit(const char *filename_arg, PQExpBuffer query_buf)
+do_edit(const char *filename_arg, PQExpBuffer query_buf, bool *edited)
 {
        char            fnametmp[MAXPGPATH];
        FILE       *stream = NULL;
@@ -1532,10 +1588,13 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf)
                                psql_error("%s: %s\n", fname, strerror(errno));
                                error = true;
                        }
+                       else if (edited)
+                       {
+                               *edited = true;
+                       }
 
                        fclose(stream);
                }
-
        }
 
        /* remove temp file */
@@ -1912,3 +1971,66 @@ do_shell(const char *command)
        }
        return true;
 }
+
+/*
+ * This function takes a function description, e.g. "x" or "x(int)", and
+ * issues a query on the given connection to retrieve the function's OID
+ * using a cast to regproc or regprocedure (as appropriate). The result,
+ * if there is one, is returned at *foid.  Note that we'll fail if the
+ * function doesn't exist OR if there are multiple matching candidates
+ * OR if there's something syntactically wrong with the function description;
+ * unfortunately it can be hard to tell the difference.
+ */
+static bool
+lookup_function_oid(PGconn *conn, const char *desc, Oid *foid)
+{
+       bool            result = true;
+       PQExpBuffer query;
+       PGresult *res;
+
+       query = createPQExpBuffer();
+       printfPQExpBuffer(query, "SELECT ");
+       appendStringLiteralConn(query, desc, conn);
+       appendPQExpBuffer(query, "::pg_catalog.%s::pg_catalog.oid",
+                                         strchr(desc, '(') ? "regprocedure" : "regproc");
+
+       res = PQexec(conn, query->data);
+       if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1)
+               *foid = atooid(PQgetvalue(res, 0, 0));
+       else
+               result = false;
+
+       PQclear(res);
+       destroyPQExpBuffer(query);
+
+       return result;
+}
+
+/*
+ * Fetches the "CREATE OR REPLACE FUNCTION ..." command that describes the
+ * function with the given OID.  If successful, the result is stored in buf.
+ */
+static bool
+get_create_function_cmd(PGconn *conn, Oid oid, PQExpBuffer buf)
+{
+       bool            result = true;
+       PQExpBuffer query;
+       PGresult *res;
+
+       query = createPQExpBuffer();
+       printfPQExpBuffer(query, "SELECT pg_catalog.pg_get_functiondef(%u)", oid);
+
+       res = PQexec(conn, query->data);
+       if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1)
+       {
+               resetPQExpBuffer(buf);
+               appendPQExpBufferStr(buf, PQgetvalue(res, 0, 0));
+       }
+       else
+               result = false;
+
+       PQclear(res);
+       destroyPQExpBuffer(query);
+
+       return result;
+}
index ecf100483847f95518593085e97bd68f7f954d8e..fd596bd52b863339d394181147f667100854525f 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.482 2008/09/01 20:42:45 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.483 2008/09/06 00:01:24 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                                                     yyyymmddN */
-#define CATALOG_VERSION_NO     200808311
+#define CATALOG_VERSION_NO     200809051
 
 #endif
index 721ba9d0c02d5281f469ad0315cf549f082c13eb..cf946508aebf48d121aece1a2bd35f3526a01982 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.512 2008/08/25 11:18:43 mha Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.513 2008/09/06 00:01:24 tgl Exp $
  *
  * NOTES
  *       The script catalog/genbki.sh reads this file and generates .bki
@@ -2292,6 +2292,8 @@ DATA(insert OID = 1716 (  pg_get_expr                PGNSP PGUID 12 1 0 0 f f t f s 2 25 "2
 DESCR("deparse an encoded expression");
 DATA(insert OID = 1665 (  pg_get_serial_sequence       PGNSP PGUID 12 1 0 0 f f t f s 2 25 "25 25" _null_ _null_ _null_        pg_get_serial_sequence _null_ _null_ _null_ ));
 DESCR("name of sequence for a serial column");
+DATA(insert OID = 2098 (  pg_get_functiondef   PGNSP PGUID 12 1 0 0 f f t f s 1 25 "26" _null_ _null_ _null_   pg_get_functiondef _null_ _null_ _null_ ));
+DESCR("definition of a function");
 DATA(insert OID = 2162 (  pg_get_function_arguments       PGNSP PGUID 12 1 0 0 f f t f s 1 25 "26" _null_ _null_ _null_        pg_get_function_arguments _null_ _null_ _null_ ));
 DESCR("argument list of a function");
 DATA(insert OID = 2165 (  pg_get_function_result          PGNSP PGUID 12 1 0 0 f f t f s 1 25 "26" _null_ _null_ _null_        pg_get_function_result _null_ _null_ _null_ ));
index fbe5a119cb9eb5ecc7a3dcad0b238c2c9e5cc00e..09d38e9c1c35376ac08a8a2091877eb8ede23b0f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.319 2008/07/18 03:32:53 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.320 2008/09/06 00:01:25 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -551,6 +551,7 @@ extern Datum pg_get_expr(PG_FUNCTION_ARGS);
 extern Datum pg_get_expr_ext(PG_FUNCTION_ARGS);
 extern Datum pg_get_userbyid(PG_FUNCTION_ARGS);
 extern Datum pg_get_serial_sequence(PG_FUNCTION_ARGS);
+extern Datum pg_get_functiondef(PG_FUNCTION_ARGS);
 extern Datum pg_get_function_arguments(PG_FUNCTION_ARGS);
 extern Datum pg_get_function_result(PG_FUNCTION_ARGS);
 extern char *deparse_expression(Node *expr, List *dpcontext,