mirror of
https://github.com/postgres/postgres.git
synced 2025-06-10 09:21:54 +03:00
Add psql \ev and \sv commands for editing and showing view definitions.
These are basically just like the \ef and \sf commands for functions. Petr Korobeinikov, reviewed by Jeevan Chalke, some changes by me
This commit is contained in:
parent
1bc90f7a7b
commit
8eb6407aae
@ -1650,6 +1650,32 @@ Tue Oct 26 21:40:57 CEST 1999
|
|||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>\ev <optional> <replaceable class="parameter">view_name</> <optional> <replaceable class="parameter">line_number</> </optional> </optional> </literal></term>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
This command fetches and edits the definition of the named view,
|
||||||
|
in the form of a <command>CREATE OR REPLACE VIEW</> command.
|
||||||
|
Editing is done in the same way as for <literal>\edit</>.
|
||||||
|
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>
|
||||||
|
If no view is specified, a blank <command>CREATE VIEW</>
|
||||||
|
template is presented for editing.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
If a line number is specified, <application>psql</application> will
|
||||||
|
position the cursor on the specified line of the view definition.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>\encoding [ <replaceable class="parameter">encoding</replaceable> ]</literal></term>
|
<term><literal>\encoding [ <replaceable class="parameter">encoding</replaceable> ]</literal></term>
|
||||||
|
|
||||||
@ -2521,6 +2547,25 @@ testdb=> <userinput>\setenv LESS -imx4F</userinput>
|
|||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
|
<varlistentry>
|
||||||
|
<term><literal>\sv[+] <replaceable class="parameter">view_name</> </literal></term>
|
||||||
|
|
||||||
|
<listitem>
|
||||||
|
<para>
|
||||||
|
This command fetches and shows the definition of the named view,
|
||||||
|
in the form of a <command>CREATE OR REPLACE VIEW</> command.
|
||||||
|
The definition is printed to the current query output channel,
|
||||||
|
as set by <command>\o</command>.
|
||||||
|
</para>
|
||||||
|
|
||||||
|
<para>
|
||||||
|
If <literal>+</literal> is appended to the command name, then the
|
||||||
|
output lines are numbered from 1.
|
||||||
|
</para>
|
||||||
|
</listitem>
|
||||||
|
</varlistentry>
|
||||||
|
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>\t</literal></term>
|
<term><literal>\t</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
@ -3554,9 +3599,10 @@ $endif
|
|||||||
|
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Editor used by the <command>\e</command> and
|
Editor used by the <command>\e</command>, <command>\ef</command>,
|
||||||
<command>\ef</command> commands. The variables are examined in
|
and <command>\ev</command> commands.
|
||||||
the order listed; the first that is set is used.
|
These variables are examined in the order listed;
|
||||||
|
the first that is set is used.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -3571,7 +3617,8 @@ $endif
|
|||||||
|
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
When <command>\e</command> or <command>\ef</command> is used
|
When <command>\e</command>, <command>\ef</command>, or
|
||||||
|
<command>\ev</command> is used
|
||||||
with a line number argument, this variable specifies the
|
with a line number argument, this variable specifies the
|
||||||
command-line argument used to pass the starting line number to
|
command-line argument used to pass the starting line number to
|
||||||
the user's editor. For editors such as <productname>Emacs</> or
|
the user's editor. For editors such as <productname>Emacs</> or
|
||||||
|
@ -49,6 +49,14 @@
|
|||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
#include "variables.h"
|
#include "variables.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Editable database object types.
|
||||||
|
*/
|
||||||
|
typedef enum EditableObjectType
|
||||||
|
{
|
||||||
|
EditableFunction,
|
||||||
|
EditableView
|
||||||
|
} EditableObjectType;
|
||||||
|
|
||||||
/* functions for use in this file */
|
/* functions for use in this file */
|
||||||
static backslashResult exec_command(const char *cmd,
|
static backslashResult exec_command(const char *cmd,
|
||||||
@ -59,9 +67,14 @@ static bool do_edit(const char *filename_arg, PQExpBuffer query_buf,
|
|||||||
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 do_watch(PQExpBuffer query_buf, long sleep);
|
static bool do_watch(PQExpBuffer query_buf, long sleep);
|
||||||
static bool lookup_function_oid(const char *desc, Oid *foid);
|
static bool lookup_object_oid(EditableObjectType obj_type, const char *desc,
|
||||||
static bool get_create_function_cmd(Oid oid, PQExpBuffer buf);
|
Oid *obj_oid);
|
||||||
static int strip_lineno_from_funcdesc(char *func);
|
static bool get_create_object_cmd(EditableObjectType obj_type, Oid oid,
|
||||||
|
PQExpBuffer buf);
|
||||||
|
static int strip_lineno_from_objdesc(char *obj);
|
||||||
|
static int count_lines_in_buf(PQExpBuffer buf);
|
||||||
|
static void print_with_linenumbers(FILE *output, char *lines,
|
||||||
|
const char *header_keyword);
|
||||||
static void minimal_error_message(PGresult *res);
|
static void minimal_error_message(PGresult *res);
|
||||||
|
|
||||||
static void printSSLInfo(void);
|
static void printSSLInfo(void);
|
||||||
@ -612,7 +625,7 @@ exec_command(const char *cmd,
|
|||||||
|
|
||||||
func = psql_scan_slash_option(scan_state,
|
func = psql_scan_slash_option(scan_state,
|
||||||
OT_WHOLE_LINE, NULL, true);
|
OT_WHOLE_LINE, NULL, true);
|
||||||
lineno = strip_lineno_from_funcdesc(func);
|
lineno = strip_lineno_from_objdesc(func);
|
||||||
if (lineno == 0)
|
if (lineno == 0)
|
||||||
{
|
{
|
||||||
/* error already reported */
|
/* error already reported */
|
||||||
@ -629,12 +642,12 @@ exec_command(const char *cmd,
|
|||||||
"AS $function$\n"
|
"AS $function$\n"
|
||||||
"\n$function$\n");
|
"\n$function$\n");
|
||||||
}
|
}
|
||||||
else if (!lookup_function_oid(func, &foid))
|
else if (!lookup_object_oid(EditableFunction, func, &foid))
|
||||||
{
|
{
|
||||||
/* error already reported */
|
/* error already reported */
|
||||||
status = PSQL_CMD_ERROR;
|
status = PSQL_CMD_ERROR;
|
||||||
}
|
}
|
||||||
else if (!get_create_function_cmd(foid, query_buf))
|
else if (!get_create_object_cmd(EditableFunction, foid, query_buf))
|
||||||
{
|
{
|
||||||
/* error already reported */
|
/* error already reported */
|
||||||
status = PSQL_CMD_ERROR;
|
status = PSQL_CMD_ERROR;
|
||||||
@ -682,6 +695,74 @@ exec_command(const char *cmd,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* \ev -- edit the named view, or present a blank CREATE VIEW template if
|
||||||
|
* no argument is given
|
||||||
|
*/
|
||||||
|
else if (strcmp(cmd, "ev") == 0)
|
||||||
|
{
|
||||||
|
int lineno = -1;
|
||||||
|
|
||||||
|
if (pset.sversion < 70400)
|
||||||
|
{
|
||||||
|
psql_error("The server (version %d.%d) does not support editing view definitions.\n",
|
||||||
|
pset.sversion / 10000, (pset.sversion / 100) % 100);
|
||||||
|
status = PSQL_CMD_ERROR;
|
||||||
|
}
|
||||||
|
else if (!query_buf)
|
||||||
|
{
|
||||||
|
psql_error("no query buffer\n");
|
||||||
|
status = PSQL_CMD_ERROR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
char *view;
|
||||||
|
Oid view_oid = InvalidOid;
|
||||||
|
|
||||||
|
view = psql_scan_slash_option(scan_state,
|
||||||
|
OT_WHOLE_LINE, NULL, true);
|
||||||
|
lineno = strip_lineno_from_objdesc(view);
|
||||||
|
if (lineno == 0)
|
||||||
|
{
|
||||||
|
/* error already reported */
|
||||||
|
status = PSQL_CMD_ERROR;
|
||||||
|
}
|
||||||
|
else if (!view)
|
||||||
|
{
|
||||||
|
/* set up an empty command to fill in */
|
||||||
|
printfPQExpBuffer(query_buf,
|
||||||
|
"CREATE VIEW AS\n"
|
||||||
|
" SELECT \n"
|
||||||
|
" -- something...\n");
|
||||||
|
}
|
||||||
|
else if (!lookup_object_oid(EditableView, view, &view_oid))
|
||||||
|
{
|
||||||
|
/* error already reported */
|
||||||
|
status = PSQL_CMD_ERROR;
|
||||||
|
}
|
||||||
|
else if (!get_create_object_cmd(EditableView, view_oid, query_buf))
|
||||||
|
{
|
||||||
|
/* error already reported */
|
||||||
|
status = PSQL_CMD_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (view)
|
||||||
|
free(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != PSQL_CMD_ERROR)
|
||||||
|
{
|
||||||
|
bool edited = false;
|
||||||
|
|
||||||
|
if (!do_edit(NULL, query_buf, lineno, &edited))
|
||||||
|
status = PSQL_CMD_ERROR;
|
||||||
|
else if (!edited)
|
||||||
|
puts(_("No changes"));
|
||||||
|
else
|
||||||
|
status = PSQL_CMD_NEWEDIT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* \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)
|
||||||
{
|
{
|
||||||
@ -1235,12 +1316,12 @@ exec_command(const char *cmd,
|
|||||||
psql_error("function name is required\n");
|
psql_error("function name is required\n");
|
||||||
status = PSQL_CMD_ERROR;
|
status = PSQL_CMD_ERROR;
|
||||||
}
|
}
|
||||||
else if (!lookup_function_oid(func, &foid))
|
else if (!lookup_object_oid(EditableFunction, func, &foid))
|
||||||
{
|
{
|
||||||
/* error already reported */
|
/* error already reported */
|
||||||
status = PSQL_CMD_ERROR;
|
status = PSQL_CMD_ERROR;
|
||||||
}
|
}
|
||||||
else if (!get_create_function_cmd(foid, func_buf))
|
else if (!get_create_object_cmd(EditableFunction, foid, func_buf))
|
||||||
{
|
{
|
||||||
/* error already reported */
|
/* error already reported */
|
||||||
status = PSQL_CMD_ERROR;
|
status = PSQL_CMD_ERROR;
|
||||||
@ -1254,18 +1335,7 @@ exec_command(const char *cmd,
|
|||||||
if (pset.queryFout == stdout)
|
if (pset.queryFout == stdout)
|
||||||
{
|
{
|
||||||
/* count lines in function to see if pager is needed */
|
/* count lines in function to see if pager is needed */
|
||||||
int lineno = 0;
|
int lineno = count_lines_in_buf(func_buf);
|
||||||
const char *lines = func_buf->data;
|
|
||||||
|
|
||||||
while (*lines != '\0')
|
|
||||||
{
|
|
||||||
lineno++;
|
|
||||||
/* find start of next line */
|
|
||||||
lines = strchr(lines, '\n');
|
|
||||||
if (!lines)
|
|
||||||
break;
|
|
||||||
lines++;
|
|
||||||
}
|
|
||||||
|
|
||||||
output = PageOutput(lineno, &(pset.popt.topt));
|
output = PageOutput(lineno, &(pset.popt.topt));
|
||||||
is_pager = true;
|
is_pager = true;
|
||||||
@ -1279,45 +1349,14 @@ exec_command(const char *cmd,
|
|||||||
|
|
||||||
if (show_linenumbers)
|
if (show_linenumbers)
|
||||||
{
|
{
|
||||||
bool in_header = true;
|
|
||||||
int lineno = 0;
|
|
||||||
char *lines = func_buf->data;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* lineno "1" should correspond to the first line of the
|
* lineno "1" should correspond to the first line of the
|
||||||
* function body. We expect that pg_get_functiondef() will
|
* function body. We expect that pg_get_functiondef() will
|
||||||
* emit that on a line beginning with "AS ", and that there
|
* emit that on a line beginning with "AS ", and that there
|
||||||
* can be no such line before the real start of the function
|
* can be no such line before the real start of the function
|
||||||
* body.
|
* body.
|
||||||
*
|
|
||||||
* Note that this loop scribbles on func_buf.
|
|
||||||
*/
|
*/
|
||||||
while (*lines != '\0')
|
print_with_linenumbers(output, func_buf->data, "AS ");
|
||||||
{
|
|
||||||
char *eol;
|
|
||||||
|
|
||||||
if (in_header && strncmp(lines, "AS ", 3) == 0)
|
|
||||||
in_header = false;
|
|
||||||
/* increment lineno only for body's lines */
|
|
||||||
if (!in_header)
|
|
||||||
lineno++;
|
|
||||||
|
|
||||||
/* find and mark end of current line */
|
|
||||||
eol = strchr(lines, '\n');
|
|
||||||
if (eol != NULL)
|
|
||||||
*eol = '\0';
|
|
||||||
|
|
||||||
/* show current line as appropriate */
|
|
||||||
if (in_header)
|
|
||||||
fprintf(output, " %s\n", lines);
|
|
||||||
else
|
|
||||||
fprintf(output, "%-7d %s\n", lineno, lines);
|
|
||||||
|
|
||||||
/* advance to next line, if any */
|
|
||||||
if (eol == NULL)
|
|
||||||
break;
|
|
||||||
lines = ++eol;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -1334,6 +1373,79 @@ exec_command(const char *cmd,
|
|||||||
destroyPQExpBuffer(func_buf);
|
destroyPQExpBuffer(func_buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* \sv -- show a view's source code */
|
||||||
|
else if (strcmp(cmd, "sv") == 0 || strcmp(cmd, "sv+") == 0)
|
||||||
|
{
|
||||||
|
bool show_linenumbers = (strcmp(cmd, "sv+") == 0);
|
||||||
|
PQExpBuffer view_buf;
|
||||||
|
char *view;
|
||||||
|
Oid view_oid = InvalidOid;
|
||||||
|
|
||||||
|
view_buf = createPQExpBuffer();
|
||||||
|
view = psql_scan_slash_option(scan_state,
|
||||||
|
OT_WHOLE_LINE, NULL, true);
|
||||||
|
if (pset.sversion < 70400)
|
||||||
|
{
|
||||||
|
psql_error("The server (version %d.%d) does not support showing view definitions.\n",
|
||||||
|
pset.sversion / 10000, (pset.sversion / 100) % 100);
|
||||||
|
status = PSQL_CMD_ERROR;
|
||||||
|
}
|
||||||
|
else if (!view)
|
||||||
|
{
|
||||||
|
psql_error("view name is required\n");
|
||||||
|
status = PSQL_CMD_ERROR;
|
||||||
|
}
|
||||||
|
else if (!lookup_object_oid(EditableView, view, &view_oid))
|
||||||
|
{
|
||||||
|
/* error already reported */
|
||||||
|
status = PSQL_CMD_ERROR;
|
||||||
|
}
|
||||||
|
else if (!get_create_object_cmd(EditableView, view_oid, view_buf))
|
||||||
|
{
|
||||||
|
/* error already reported */
|
||||||
|
status = PSQL_CMD_ERROR;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FILE *output;
|
||||||
|
bool is_pager;
|
||||||
|
|
||||||
|
/* Select output stream: stdout, pager, or file */
|
||||||
|
if (pset.queryFout == stdout)
|
||||||
|
{
|
||||||
|
/* count lines in view to see if pager is needed */
|
||||||
|
int lineno = count_lines_in_buf(view_buf);
|
||||||
|
|
||||||
|
output = PageOutput(lineno, &(pset.popt.topt));
|
||||||
|
is_pager = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* use previously set output file, without pager */
|
||||||
|
output = pset.queryFout;
|
||||||
|
is_pager = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_linenumbers)
|
||||||
|
{
|
||||||
|
/* add line numbers, numbering all lines */
|
||||||
|
print_with_linenumbers(output, view_buf->data, NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
/* just send the view definition to output */
|
||||||
|
fputs(view_buf->data, output);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_pager)
|
||||||
|
ClosePager(output);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (view)
|
||||||
|
free(view);
|
||||||
|
destroyPQExpBuffer(view_buf);
|
||||||
|
}
|
||||||
|
|
||||||
/* \t -- turn off headers and row count */
|
/* \t -- turn off headers and row count */
|
||||||
else if (strcmp(cmd, "t") == 0)
|
else if (strcmp(cmd, "t") == 0)
|
||||||
{
|
{
|
||||||
@ -3002,7 +3114,7 @@ do_watch(PQExpBuffer query_buf, long sleep)
|
|||||||
* returns true unless we have ECHO_HIDDEN_NOEXEC.
|
* returns true unless we have ECHO_HIDDEN_NOEXEC.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
lookup_function_echo_hidden(char *query)
|
echo_hidden_command(const char *query)
|
||||||
{
|
{
|
||||||
if (pset.echo_hidden != PSQL_ECHO_HIDDEN_OFF)
|
if (pset.echo_hidden != PSQL_ECHO_HIDDEN_OFF)
|
||||||
{
|
{
|
||||||
@ -3026,34 +3138,57 @@ lookup_function_echo_hidden(char *query)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This function takes a function description, e.g. "x" or "x(int)", and
|
* Look up the object identified by obj_type and desc. If successful,
|
||||||
* issues a query on the given connection to retrieve the function's OID
|
* store its OID in *obj_oid and return TRUE, else return FALSE.
|
||||||
* 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
|
* Note that we'll fail if the object doesn't exist OR if there are multiple
|
||||||
* function doesn't exist OR if there are multiple matching candidates
|
* matching candidates OR if there's something syntactically wrong with the
|
||||||
* OR if there's something syntactically wrong with the function description;
|
* object description; unfortunately it can be hard to tell the difference.
|
||||||
* unfortunately it can be hard to tell the difference.
|
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
lookup_function_oid(const char *desc, Oid *foid)
|
lookup_object_oid(EditableObjectType obj_type, const char *desc,
|
||||||
|
Oid *obj_oid)
|
||||||
{
|
{
|
||||||
bool result = true;
|
bool result = true;
|
||||||
PQExpBuffer query;
|
PQExpBuffer query = createPQExpBuffer();
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
|
|
||||||
query = createPQExpBuffer();
|
switch (obj_type)
|
||||||
|
{
|
||||||
|
case EditableFunction:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* We have a function description, e.g. "x" or "x(int)". Issue a
|
||||||
|
* query to retrieve the function's OID using a cast to regproc or
|
||||||
|
* regprocedure (as appropriate).
|
||||||
|
*/
|
||||||
appendPQExpBufferStr(query, "SELECT ");
|
appendPQExpBufferStr(query, "SELECT ");
|
||||||
appendStringLiteralConn(query, desc, pset.db);
|
appendStringLiteralConn(query, desc, pset.db);
|
||||||
appendPQExpBuffer(query, "::pg_catalog.%s::pg_catalog.oid",
|
appendPQExpBuffer(query, "::pg_catalog.%s::pg_catalog.oid",
|
||||||
strchr(desc, '(') ? "regprocedure" : "regproc");
|
strchr(desc, '(') ? "regprocedure" : "regproc");
|
||||||
if (!lookup_function_echo_hidden(query->data))
|
break;
|
||||||
|
|
||||||
|
case EditableView:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Convert view name (possibly schema-qualified) to OID. Note:
|
||||||
|
* this code doesn't check if the relation is actually a view.
|
||||||
|
* We'll detect that in get_create_object_cmd().
|
||||||
|
*/
|
||||||
|
appendPQExpBufferStr(query, "SELECT ");
|
||||||
|
appendStringLiteralConn(query, desc, pset.db);
|
||||||
|
appendPQExpBuffer(query, "::pg_catalog.regclass::pg_catalog.oid");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!echo_hidden_command(query->data))
|
||||||
{
|
{
|
||||||
destroyPQExpBuffer(query);
|
destroyPQExpBuffer(query);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
res = PQexec(pset.db, query->data);
|
res = PQexec(pset.db, query->data);
|
||||||
if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1)
|
if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1)
|
||||||
*foid = atooid(PQgetvalue(res, 0, 0));
|
*obj_oid = atooid(PQgetvalue(res, 0, 0));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
minimal_error_message(res);
|
minimal_error_message(res);
|
||||||
@ -3067,20 +3202,42 @@ lookup_function_oid(const char *desc, Oid *foid)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Fetches the "CREATE OR REPLACE FUNCTION ..." command that describes the
|
* Construct a "CREATE OR REPLACE ..." command that describes the specified
|
||||||
* function with the given OID. If successful, the result is stored in buf.
|
* database object. If successful, the result is stored in buf.
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
get_create_function_cmd(Oid oid, PQExpBuffer buf)
|
get_create_object_cmd(EditableObjectType obj_type, Oid oid,
|
||||||
|
PQExpBuffer buf)
|
||||||
{
|
{
|
||||||
bool result = true;
|
bool result = true;
|
||||||
PQExpBuffer query;
|
PQExpBuffer query = createPQExpBuffer();
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
|
|
||||||
query = createPQExpBuffer();
|
switch (obj_type)
|
||||||
printfPQExpBuffer(query, "SELECT pg_catalog.pg_get_functiondef(%u)", oid);
|
{
|
||||||
|
case EditableFunction:
|
||||||
|
printfPQExpBuffer(query,
|
||||||
|
"SELECT pg_catalog.pg_get_functiondef(%u)",
|
||||||
|
oid);
|
||||||
|
break;
|
||||||
|
|
||||||
if (!lookup_function_echo_hidden(query->data))
|
case EditableView:
|
||||||
|
|
||||||
|
/*
|
||||||
|
* pg_get_viewdef() just prints the query, so we must prepend
|
||||||
|
* CREATE for ourselves. We must fully qualify the view name to
|
||||||
|
* ensure the right view gets replaced. Also, check relation kind
|
||||||
|
* to be sure it's a view.
|
||||||
|
*/
|
||||||
|
printfPQExpBuffer(query,
|
||||||
|
"SELECT nspname, relname, relkind, pg_catalog.pg_get_viewdef(c.oid, true) FROM "
|
||||||
|
"pg_catalog.pg_class c LEFT JOIN pg_catalog.pg_namespace n "
|
||||||
|
"ON c.relnamespace = n.oid WHERE c.oid = %u",
|
||||||
|
oid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!echo_hidden_command(query->data))
|
||||||
{
|
{
|
||||||
destroyPQExpBuffer(query);
|
destroyPQExpBuffer(query);
|
||||||
return false;
|
return false;
|
||||||
@ -3089,7 +3246,53 @@ get_create_function_cmd(Oid oid, PQExpBuffer buf)
|
|||||||
if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1)
|
if (PQresultStatus(res) == PGRES_TUPLES_OK && PQntuples(res) == 1)
|
||||||
{
|
{
|
||||||
resetPQExpBuffer(buf);
|
resetPQExpBuffer(buf);
|
||||||
|
switch (obj_type)
|
||||||
|
{
|
||||||
|
case EditableFunction:
|
||||||
appendPQExpBufferStr(buf, PQgetvalue(res, 0, 0));
|
appendPQExpBufferStr(buf, PQgetvalue(res, 0, 0));
|
||||||
|
break;
|
||||||
|
|
||||||
|
case EditableView:
|
||||||
|
{
|
||||||
|
char *nspname = PQgetvalue(res, 0, 0);
|
||||||
|
char *relname = PQgetvalue(res, 0, 1);
|
||||||
|
char *relkind = PQgetvalue(res, 0, 2);
|
||||||
|
char *viewdef = PQgetvalue(res, 0, 3);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If the backend ever supports CREATE OR REPLACE
|
||||||
|
* MATERIALIZED VIEW, allow that here; but as of today it
|
||||||
|
* does not, so editing a matview definition in this way
|
||||||
|
* is impossible.
|
||||||
|
*/
|
||||||
|
switch (relkind[0])
|
||||||
|
{
|
||||||
|
#ifdef NOT_USED
|
||||||
|
case 'm':
|
||||||
|
appendPQExpBufferStr(buf, "CREATE OR REPLACE MATERIALIZED VIEW ");
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
case 'v':
|
||||||
|
appendPQExpBufferStr(buf, "CREATE OR REPLACE VIEW ");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
psql_error("%s.%s is not a view\n",
|
||||||
|
nspname, relname);
|
||||||
|
result = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
appendPQExpBuffer(buf, "%s.", fmtId(nspname));
|
||||||
|
appendPQExpBuffer(buf, "%s AS\n", fmtId(relname));
|
||||||
|
appendPQExpBufferStr(buf, viewdef);
|
||||||
|
/* Get rid of the semicolon that pg_get_viewdef appends */
|
||||||
|
if (buf->len > 0 && buf->data[buf->len - 1] == ';')
|
||||||
|
buf->data[--(buf->len)] = '\0';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* Make sure result ends with a newline */
|
||||||
|
if (buf->len > 0 && buf->data[buf->len - 1] != '\n')
|
||||||
|
appendPQExpBufferChar(buf, '\n');
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@ -3104,24 +3307,24 @@ get_create_function_cmd(Oid oid, PQExpBuffer buf)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If the given argument of \ef ends with a line number, delete the line
|
* If the given argument of \ef or \ev ends with a line number, delete the line
|
||||||
* number from the argument string and return it as an integer. (We need
|
* number from the argument string and return it as an integer. (We need
|
||||||
* this kluge because we're too lazy to parse \ef's function name argument
|
* this kluge because we're too lazy to parse \ef's function or \ev's view
|
||||||
* carefully --- we just slop it up in OT_WHOLE_LINE mode.)
|
* argument carefully --- we just slop it up in OT_WHOLE_LINE mode.)
|
||||||
*
|
*
|
||||||
* Returns -1 if no line number is present, 0 on error, or a positive value
|
* Returns -1 if no line number is present, 0 on error, or a positive value
|
||||||
* on success.
|
* on success.
|
||||||
*/
|
*/
|
||||||
static int
|
static int
|
||||||
strip_lineno_from_funcdesc(char *func)
|
strip_lineno_from_objdesc(char *obj)
|
||||||
{
|
{
|
||||||
char *c;
|
char *c;
|
||||||
int lineno;
|
int lineno;
|
||||||
|
|
||||||
if (!func || func[0] == '\0')
|
if (!obj || obj[0] == '\0')
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
c = func + strlen(func) - 1;
|
c = obj + strlen(obj) - 1;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* This business of parsing backwards is dangerous as can be in a
|
* This business of parsing backwards is dangerous as can be in a
|
||||||
@ -3134,20 +3337,20 @@ strip_lineno_from_funcdesc(char *func)
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* skip trailing whitespace */
|
/* skip trailing whitespace */
|
||||||
while (c > func && isascii((unsigned char) *c) && isspace((unsigned char) *c))
|
while (c > obj && isascii((unsigned char) *c) && isspace((unsigned char) *c))
|
||||||
c--;
|
c--;
|
||||||
|
|
||||||
/* must have a digit as last non-space char */
|
/* must have a digit as last non-space char */
|
||||||
if (c == func || !isascii((unsigned char) *c) || !isdigit((unsigned char) *c))
|
if (c == obj || !isascii((unsigned char) *c) || !isdigit((unsigned char) *c))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
/* find start of digit string */
|
/* find start of digit string */
|
||||||
while (c > func && isascii((unsigned char) *c) && isdigit((unsigned char) *c))
|
while (c > obj && isascii((unsigned char) *c) && isdigit((unsigned char) *c))
|
||||||
c--;
|
c--;
|
||||||
|
|
||||||
/* digits must be separated from func name by space or closing paren */
|
/* digits must be separated from object name by space or closing paren */
|
||||||
/* notice also that we are not allowing an empty func name ... */
|
/* notice also that we are not allowing an empty object name ... */
|
||||||
if (c == func || !isascii((unsigned char) *c) ||
|
if (c == obj || !isascii((unsigned char) *c) ||
|
||||||
!(isspace((unsigned char) *c) || *c == ')'))
|
!(isspace((unsigned char) *c) || *c == ')'))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
@ -3160,12 +3363,80 @@ strip_lineno_from_funcdesc(char *func)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* strip digit string from func */
|
/* strip digit string from object name */
|
||||||
*c = '\0';
|
*c = '\0';
|
||||||
|
|
||||||
return lineno;
|
return lineno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Count number of lines in the buffer.
|
||||||
|
* This is used to test if pager is needed or not.
|
||||||
|
*/
|
||||||
|
static int
|
||||||
|
count_lines_in_buf(PQExpBuffer buf)
|
||||||
|
{
|
||||||
|
int lineno = 0;
|
||||||
|
const char *lines = buf->data;
|
||||||
|
|
||||||
|
while (*lines != '\0')
|
||||||
|
{
|
||||||
|
lineno++;
|
||||||
|
/* find start of next line */
|
||||||
|
lines = strchr(lines, '\n');
|
||||||
|
if (!lines)
|
||||||
|
break;
|
||||||
|
lines++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return lineno;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Write text at *lines to output with line numbers.
|
||||||
|
*
|
||||||
|
* If header_keyword isn't NULL, then line 1 should be the first line beginning
|
||||||
|
* with header_keyword; lines before that are unnumbered.
|
||||||
|
*
|
||||||
|
* Caution: this scribbles on *lines.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
print_with_linenumbers(FILE *output, char *lines,
|
||||||
|
const char *header_keyword)
|
||||||
|
{
|
||||||
|
bool in_header = (header_keyword != NULL);
|
||||||
|
size_t header_sz = in_header ? strlen(header_keyword) : 0;
|
||||||
|
int lineno = 0;
|
||||||
|
|
||||||
|
while (*lines != '\0')
|
||||||
|
{
|
||||||
|
char *eol;
|
||||||
|
|
||||||
|
if (in_header && strncmp(lines, header_keyword, header_sz) == 0)
|
||||||
|
in_header = false;
|
||||||
|
|
||||||
|
/* increment lineno only for body's lines */
|
||||||
|
if (!in_header)
|
||||||
|
lineno++;
|
||||||
|
|
||||||
|
/* find and mark end of current line */
|
||||||
|
eol = strchr(lines, '\n');
|
||||||
|
if (eol != NULL)
|
||||||
|
*eol = '\0';
|
||||||
|
|
||||||
|
/* show current line as appropriate */
|
||||||
|
if (in_header)
|
||||||
|
fprintf(output, " %s\n", lines);
|
||||||
|
else
|
||||||
|
fprintf(output, "%-7d %s\n", lineno, lines);
|
||||||
|
|
||||||
|
/* advance to next line, if any */
|
||||||
|
if (eol == NULL)
|
||||||
|
break;
|
||||||
|
lines = ++eol;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Report just the primary error; this is to avoid cluttering the output
|
* Report just the primary error; this is to avoid cluttering the output
|
||||||
* with, for instance, a redisplay of the internally generated query
|
* with, for instance, a redisplay of the internally generated query
|
||||||
|
@ -181,6 +181,7 @@ slashUsage(unsigned short int pager)
|
|||||||
fprintf(output, _("Query Buffer\n"));
|
fprintf(output, _("Query Buffer\n"));
|
||||||
fprintf(output, _(" \\e [FILE] [LINE] edit the query buffer (or file) with external editor\n"));
|
fprintf(output, _(" \\e [FILE] [LINE] edit the query buffer (or file) with external editor\n"));
|
||||||
fprintf(output, _(" \\ef [FUNCNAME [LINE]] edit function definition with external editor\n"));
|
fprintf(output, _(" \\ef [FUNCNAME [LINE]] edit function definition with external editor\n"));
|
||||||
|
fprintf(output, _(" \\ev [VIEWNAME [LINE]] edit view definition with external editor\n"));
|
||||||
fprintf(output, _(" \\p show the contents of the query buffer\n"));
|
fprintf(output, _(" \\p show the contents of the query buffer\n"));
|
||||||
fprintf(output, _(" \\r reset (clear) the query buffer\n"));
|
fprintf(output, _(" \\r reset (clear) the query buffer\n"));
|
||||||
#ifdef USE_READLINE
|
#ifdef USE_READLINE
|
||||||
@ -238,6 +239,7 @@ slashUsage(unsigned short int pager)
|
|||||||
fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
|
fprintf(output, _(" \\dy [PATTERN] list event triggers\n"));
|
||||||
fprintf(output, _(" \\l[+] [PATTERN] list databases\n"));
|
fprintf(output, _(" \\l[+] [PATTERN] list databases\n"));
|
||||||
fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n"));
|
fprintf(output, _(" \\sf[+] FUNCNAME show a function's definition\n"));
|
||||||
|
fprintf(output, _(" \\sv[+] VIEWNAME show a view's definition\n"));
|
||||||
fprintf(output, _(" \\z [PATTERN] same as \\dp\n"));
|
fprintf(output, _(" \\z [PATTERN] same as \\dp\n"));
|
||||||
fprintf(output, "\n");
|
fprintf(output, "\n");
|
||||||
|
|
||||||
@ -388,7 +390,7 @@ helpVariables(unsigned short int pager)
|
|||||||
fprintf(output, _(" PGPASSWORD connection password (not recommended)\n"));
|
fprintf(output, _(" PGPASSWORD connection password (not recommended)\n"));
|
||||||
fprintf(output, _(" PGPASSFILE password file name\n"));
|
fprintf(output, _(" PGPASSFILE password file name\n"));
|
||||||
fprintf(output, _(" PSQL_EDITOR, EDITOR, VISUAL\n"
|
fprintf(output, _(" PSQL_EDITOR, EDITOR, VISUAL\n"
|
||||||
" editor used by the \\e and \\ef commands\n"));
|
" editor used by the \\e, \\ef, and \\ev commands\n"));
|
||||||
fprintf(output, _(" PSQL_EDITOR_LINENUMBER_ARG\n"
|
fprintf(output, _(" PSQL_EDITOR_LINENUMBER_ARG\n"
|
||||||
" how to specify a line number when invoking the editor\n"));
|
" how to specify a line number when invoking the editor\n"));
|
||||||
fprintf(output, _(" PSQL_HISTORY alternative location for the command history file\n"));
|
fprintf(output, _(" PSQL_HISTORY alternative location for the command history file\n"));
|
||||||
|
@ -900,11 +900,11 @@ psql_completion(const char *text, int start, int end)
|
|||||||
"\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\det", "\\deu", "\\dew", "\\df",
|
"\\d", "\\da", "\\db", "\\dc", "\\dC", "\\dd", "\\dD", "\\des", "\\det", "\\deu", "\\dew", "\\df",
|
||||||
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
|
"\\dF", "\\dFd", "\\dFp", "\\dFt", "\\dg", "\\di", "\\dl", "\\dL",
|
||||||
"\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du", "\\dx",
|
"\\dn", "\\do", "\\dp", "\\drds", "\\ds", "\\dS", "\\dt", "\\dT", "\\dv", "\\du", "\\dx",
|
||||||
"\\e", "\\echo", "\\ef", "\\encoding",
|
"\\e", "\\echo", "\\ef", "\\encoding", "\\ev",
|
||||||
"\\f", "\\g", "\\gset", "\\h", "\\help", "\\H", "\\i", "\\ir", "\\l",
|
"\\f", "\\g", "\\gset", "\\h", "\\help", "\\H", "\\i", "\\ir", "\\l",
|
||||||
"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
|
"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
|
||||||
"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
|
"\\o", "\\p", "\\password", "\\prompt", "\\pset", "\\q", "\\qecho", "\\r",
|
||||||
"\\set", "\\sf", "\\t", "\\T",
|
"\\set", "\\sf", "\\sv", "\\t", "\\T",
|
||||||
"\\timing", "\\unset", "\\x", "\\w", "\\watch", "\\z", "\\!", NULL
|
"\\timing", "\\unset", "\\x", "\\w", "\\watch", "\\z", "\\!", NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -3798,6 +3798,8 @@ psql_completion(const char *text, int start, int end)
|
|||||||
|
|
||||||
else if (strcmp(prev_wd, "\\ef") == 0)
|
else if (strcmp(prev_wd, "\\ef") == 0)
|
||||||
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
|
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
|
||||||
|
else if (strcmp(prev_wd, "\\ev") == 0)
|
||||||
|
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
|
||||||
|
|
||||||
else if (strcmp(prev_wd, "\\encoding") == 0)
|
else if (strcmp(prev_wd, "\\encoding") == 0)
|
||||||
COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
|
COMPLETE_WITH_QUERY(Query_for_list_of_encodings);
|
||||||
@ -3912,6 +3914,8 @@ psql_completion(const char *text, int start, int end)
|
|||||||
}
|
}
|
||||||
else if (strcmp(prev_wd, "\\sf") == 0 || strcmp(prev_wd, "\\sf+") == 0)
|
else if (strcmp(prev_wd, "\\sf") == 0 || strcmp(prev_wd, "\\sf+") == 0)
|
||||||
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
|
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_functions, NULL);
|
||||||
|
else if (strcmp(prev_wd, "\\sv") == 0 || strcmp(prev_wd, "\\sv+") == 0)
|
||||||
|
COMPLETE_WITH_SCHEMA_QUERY(Query_for_list_of_views, NULL);
|
||||||
else if (strcmp(prev_wd, "\\cd") == 0 ||
|
else if (strcmp(prev_wd, "\\cd") == 0 ||
|
||||||
strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 ||
|
strcmp(prev_wd, "\\e") == 0 || strcmp(prev_wd, "\\edit") == 0 ||
|
||||||
strcmp(prev_wd, "\\g") == 0 ||
|
strcmp(prev_wd, "\\g") == 0 ||
|
||||||
|
Loading…
x
Reference in New Issue
Block a user