mirror of
https://git.postgresql.org/git/postgresql.git
synced 2025-01-24 18:55:04 +08:00
Implement a psql command "\ef" to edit the definition of a function.
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
This commit is contained in:
parent
2cdcf459ba
commit
2c863ca818
@ -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</>.
|
||||
|
@ -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=>
|
||||
</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>
|
||||
|
@ -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
|
||||
|
@ -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"
|
||||
@ -56,9 +56,12 @@
|
||||
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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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_ ));
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user