mirror of
https://github.com/postgres/postgres.git
synced 2025-06-30 21:42:05 +03: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:
@ -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">
|
<chapter id="functions">
|
||||||
<title>Functions and Operators</title>
|
<title>Functions and Operators</title>
|
||||||
@ -11562,6 +11562,10 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
|
|||||||
<primary>pg_get_ruledef</primary>
|
<primary>pg_get_ruledef</primary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
|
|
||||||
|
<indexterm>
|
||||||
|
<primary>pg_get_functiondef</primary>
|
||||||
|
</indexterm>
|
||||||
|
|
||||||
<indexterm>
|
<indexterm>
|
||||||
<primary>pg_get_function_arguments</primary>
|
<primary>pg_get_function_arguments</primary>
|
||||||
</indexterm>
|
</indexterm>
|
||||||
@ -11643,6 +11647,11 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
|
|||||||
<entry>decompile internal form of an expression, assuming that any Vars
|
<entry>decompile internal form of an expression, assuming that any Vars
|
||||||
in it refer to the relation indicated by the second parameter</entry>
|
in it refer to the relation indicated by the second parameter</entry>
|
||||||
</row>
|
</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>
|
<row>
|
||||||
<entry><literal><function>pg_get_function_arguments</function>(<parameter>func_oid</parameter>)</literal></entry>
|
<entry><literal><function>pg_get_function_arguments</function>(<parameter>func_oid</parameter>)</literal></entry>
|
||||||
<entry><type>text</type></entry>
|
<entry><type>text</type></entry>
|
||||||
@ -11756,6 +11765,8 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<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
|
<function>pg_get_function_arguments</function> returns the argument list
|
||||||
of a function, in the form it would need to appear in within
|
of a function, in the form it would need to appear in within
|
||||||
<command>CREATE FUNCTION</>.
|
<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
|
PostgreSQL documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -1195,6 +1195,29 @@ testdb=>
|
|||||||
</varlistentry>
|
</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>
|
<varlistentry>
|
||||||
<term><literal>\echo <replaceable class="parameter">text</replaceable> [ ... ]</literal></term>
|
<term><literal>\echo <replaceable class="parameter">text</replaceable> [ ... ]</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
|
@ -9,7 +9,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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);
|
int prettyFlags);
|
||||||
static int print_function_arguments(StringInfo buf, HeapTuple proctup,
|
static int print_function_arguments(StringInfo buf, HeapTuple proctup,
|
||||||
bool print_table_args);
|
bool print_table_args);
|
||||||
|
static void print_function_rettype(StringInfo buf, HeapTuple proctup);
|
||||||
static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
|
static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
|
||||||
int prettyFlags);
|
int prettyFlags);
|
||||||
static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
|
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);
|
Node *parentNode);
|
||||||
static void get_const_expr(Const *constval, deparse_context *context,
|
static void get_const_expr(Const *constval, deparse_context *context,
|
||||||
int showtype);
|
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_sublink_expr(SubLink *sublink, deparse_context *context);
|
||||||
static void get_from_clause(Query *query, const char *prefix,
|
static void get_from_clause(Query *query, const char *prefix,
|
||||||
deparse_context *context);
|
deparse_context *context);
|
||||||
@ -553,23 +555,10 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
|
|||||||
{
|
{
|
||||||
if (i > 0)
|
if (i > 0)
|
||||||
appendStringInfo(&buf, ", ");
|
appendStringInfo(&buf, ", ");
|
||||||
|
simple_quote_literal(&buf, p);
|
||||||
/*
|
|
||||||
* 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, '\'');
|
|
||||||
/* advance p to next string embedded in tgargs */
|
/* advance p to next string embedded in tgargs */
|
||||||
|
while (*p)
|
||||||
|
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
|
* pg_get_function_arguments
|
||||||
* Get a nicely-formatted list of arguments for a function.
|
* 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);
|
Oid funcid = PG_GETARG_OID(0);
|
||||||
StringInfoData buf;
|
StringInfoData buf;
|
||||||
HeapTuple proctup;
|
HeapTuple proctup;
|
||||||
Form_pg_proc procform;
|
|
||||||
int ntabargs = 0;
|
|
||||||
|
|
||||||
initStringInfo(&buf);
|
initStringInfo(&buf);
|
||||||
|
|
||||||
@ -1446,30 +1609,47 @@ pg_get_function_result(PG_FUNCTION_ARGS)
|
|||||||
0, 0, 0);
|
0, 0, 0);
|
||||||
if (!HeapTupleIsValid(proctup))
|
if (!HeapTupleIsValid(proctup))
|
||||||
elog(ERROR, "cache lookup failed for function %u", funcid);
|
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 */
|
/* It might be a table function; try to print the arguments */
|
||||||
appendStringInfoString(&buf, "TABLE(");
|
appendStringInfoString(&rbuf, "TABLE(");
|
||||||
ntabargs = print_function_arguments(&buf, proctup, true);
|
ntabargs = print_function_arguments(&rbuf, proctup, true);
|
||||||
if (ntabargs > 0)
|
if (ntabargs > 0)
|
||||||
appendStringInfoString(&buf, ")");
|
appendStringInfoString(&rbuf, ")");
|
||||||
else
|
else
|
||||||
resetStringInfo(&buf);
|
resetStringInfo(&rbuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ntabargs == 0)
|
if (ntabargs == 0)
|
||||||
{
|
{
|
||||||
/* Not a table function, so do the normal thing */
|
/* Not a table function, so do the normal thing */
|
||||||
if (procform->proretset)
|
if (proc->proretset)
|
||||||
appendStringInfoString(&buf, "SETOF ");
|
appendStringInfoString(&rbuf, "SETOF ");
|
||||||
appendStringInfoString(&buf, format_type_be(procform->prorettype));
|
appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
|
||||||
}
|
}
|
||||||
|
|
||||||
ReleaseSysCache(proctup);
|
appendStringInfoString(buf, rbuf.data);
|
||||||
|
|
||||||
PG_RETURN_TEXT_P(string_to_text(buf.data));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -4597,7 +4777,6 @@ get_const_expr(Const *constval, deparse_context *context, int showtype)
|
|||||||
Oid typoutput;
|
Oid typoutput;
|
||||||
bool typIsVarlena;
|
bool typIsVarlena;
|
||||||
char *extval;
|
char *extval;
|
||||||
char *valptr;
|
|
||||||
bool isfloat = false;
|
bool isfloat = false;
|
||||||
bool needlabel;
|
bool needlabel;
|
||||||
|
|
||||||
@ -4672,22 +4851,7 @@ get_const_expr(Const *constval, deparse_context *context, int showtype)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
simple_quote_literal(buf, extval);
|
||||||
/*
|
|
||||||
* 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, '\'');
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -4729,6 +4893,31 @@ get_const_expr(Const *constval, deparse_context *context, int showtype)
|
|||||||
constval->consttypmod));
|
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
|
* get_sublink_expr - Parse back a sublink
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 2000-2008, PostgreSQL Global Development Group
|
* 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 "postgres_fe.h"
|
||||||
#include "command.h"
|
#include "command.h"
|
||||||
@ -56,9 +56,12 @@
|
|||||||
static backslashResult exec_command(const char *cmd,
|
static backslashResult exec_command(const char *cmd,
|
||||||
PsqlScanState scan_state,
|
PsqlScanState scan_state,
|
||||||
PQExpBuffer query_buf);
|
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_connect(char *dbname, char *user, char *host, char *port);
|
||||||
static bool do_shell(const char *command);
|
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
|
#ifdef USE_SSL
|
||||||
static void printSSLInfo(void);
|
static void printSSLInfo(void);
|
||||||
@ -444,11 +447,64 @@ exec_command(const char *cmd,
|
|||||||
expand_tilde(&fname);
|
expand_tilde(&fname);
|
||||||
if (fname)
|
if (fname)
|
||||||
canonicalize_path(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);
|
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 */
|
/* \echo and \qecho */
|
||||||
else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0)
|
else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho") == 0)
|
||||||
{
|
{
|
||||||
@ -1410,7 +1466,7 @@ editFile(const char *fname)
|
|||||||
|
|
||||||
/* call this one */
|
/* call this one */
|
||||||
static bool
|
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];
|
char fnametmp[MAXPGPATH];
|
||||||
FILE *stream = NULL;
|
FILE *stream = NULL;
|
||||||
@ -1532,10 +1588,13 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf)
|
|||||||
psql_error("%s: %s\n", fname, strerror(errno));
|
psql_error("%s: %s\n", fname, strerror(errno));
|
||||||
error = true;
|
error = true;
|
||||||
}
|
}
|
||||||
|
else if (edited)
|
||||||
|
{
|
||||||
|
*edited = true;
|
||||||
|
}
|
||||||
|
|
||||||
fclose(stream);
|
fclose(stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* remove temp file */
|
/* remove temp file */
|
||||||
@ -1912,3 +1971,66 @@ do_shell(const char *command)
|
|||||||
}
|
}
|
||||||
return true;
|
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) 1996-2008, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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 */
|
/* yyyymmddN */
|
||||||
#define CATALOG_VERSION_NO 200808311
|
#define CATALOG_VERSION_NO 200809051
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
* Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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
|
* NOTES
|
||||||
* The script catalog/genbki.sh reads this file and generates .bki
|
* 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");
|
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_ ));
|
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");
|
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_ ));
|
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");
|
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_ ));
|
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) 1996-2008, PostgreSQL Global Development Group
|
||||||
* Portions Copyright (c) 1994, Regents of the University of California
|
* 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_expr_ext(PG_FUNCTION_ARGS);
|
||||||
extern Datum pg_get_userbyid(PG_FUNCTION_ARGS);
|
extern Datum pg_get_userbyid(PG_FUNCTION_ARGS);
|
||||||
extern Datum pg_get_serial_sequence(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_arguments(PG_FUNCTION_ARGS);
|
||||||
extern Datum pg_get_function_result(PG_FUNCTION_ARGS);
|
extern Datum pg_get_function_result(PG_FUNCTION_ARGS);
|
||||||
extern char *deparse_expression(Node *expr, List *dpcontext,
|
extern char *deparse_expression(Node *expr, List *dpcontext,
|
||||||
|
Reference in New Issue
Block a user