* Includes tab completion. It's not magic, but it's very cool. At any
rate it's better than what used to be there. * Does proper SQL "host variable" substitution as pointed out by Andreas Zeugwetter (thanks): select * from :foo; Also some changes in how ':' and ';' are treated (escape with \ to send to backend). This does _not_ affect the '::' cast operator, but perhaps others that contain : or ; (but there are none right now). * To show description with a <something> listing, append '?' to command name, e.g., \df?. This seemed to be the convenient and logical solution. Or append a '+' to see more useless information, e.g., \df+. * Fixed fflush()'ing bug pointed out by Jan during the regression test discussion. * Added LastOid variable. This ought to take care of TODO item "Add a function to return the last inserted oid, for use in psql scripts" (under CLIENTS) E.g., insert into foo values(...); insert into bar values(..., :LastOid); \echo $LastOid * \d command shows constraints, rules, and triggers defined on the table (in addition to indices) * Various fixes, optimizations, corrections * Documentation update as well Note: This now requires snprintf(), which, if necessary, is taken from src/backend/port. This is certainly a little weird, but it should suffice until a source tree cleanup is done. Enjoy. -- Peter Eisentraut Sernanders väg 10:115
This commit is contained in:
parent
c83b4d1cd8
commit
78bc83fedf
@ -1,5 +1,5 @@
|
||||
<!--
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.17 1999/11/05 18:21:08 momjian Exp $
|
||||
$Header: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.18 1999/11/26 04:24:16 momjian Exp $
|
||||
Postgres documentation
|
||||
-->
|
||||
|
||||
@ -90,6 +90,11 @@ Postgres documentation
|
||||
to be run at the start of every session.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<application>psql</application> can be used in a pipe sequence, and
|
||||
automatically detects when it is not used interactively.
|
||||
</para>
|
||||
|
||||
<refsect2 id="R2-APP-PSQL-3">
|
||||
<refsect2info>
|
||||
<date>1998-09-26</date>
|
||||
@ -104,7 +109,7 @@ Postgres documentation
|
||||
<application>libpq</application> client library, upon which
|
||||
<application>psql</application> is built, will choose defaults.
|
||||
(This will usually mean the environment variables <envar>PGDATABASE</envar>,
|
||||
<envar>PGHOST</envar>, <envar>PGPORT</envar>, <envar>PQUSER</envar>,
|
||||
<envar>PGHOST</envar>, <envar>PGPORT</envar>, <envar>PGUSER</envar>,
|
||||
respectively, if they are set. Otherwise the default host is the local host
|
||||
via Unix domain sockets, the default port is decided at compile time,
|
||||
the default user is the system user name, and the default database is
|
||||
@ -129,7 +134,7 @@ Postgres documentation
|
||||
In normal operation, <application>psql</application> provides a prompt with
|
||||
the name of the database that <application>psql</application> is currently
|
||||
connected to followed by the string "=>". For example,
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
$ <userinput>psql testdb</userinput>
|
||||
Welcome to psql, the PostgreSQL interactive terminal.
|
||||
|
||||
@ -140,7 +145,7 @@ Type: \copyright for distribution terms
|
||||
\q to quit
|
||||
|
||||
testdb=>
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -156,11 +161,6 @@ testdb=>
|
||||
<xref linkend="SQL-LISTEN" endterm="SQL-LISTEN-title"> and
|
||||
<xref linkend="SQL-NOTIFY" endterm="SQL-NOTIFY-title">.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<application>psql</application> can be used in a pipe sequence, and
|
||||
automatically detects when it is not used interactively.
|
||||
</para>
|
||||
</refsect2>
|
||||
</refsect1>
|
||||
|
||||
@ -343,17 +343,17 @@ testdb=>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
Shows all column of <replaceable class="parameter">relation</replaceable>
|
||||
Shows all columns of <replaceable class="parameter">relation</replaceable>
|
||||
(which could be a table, view, index, or sequence),
|
||||
their types, and any special attributes such as <literal>NOT NULL</literal>
|
||||
or defaults, if any.
|
||||
If the relation is, in fact, a table, any defined indices are also listed.
|
||||
If the relation is a view, the view definition is also shown.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If the relation is, in fact, a table, any defined indices are also listed.
|
||||
If the relation is a view, the view definition is also shown.
|
||||
If the variable <envar>description</envar> is set, any comments associated
|
||||
with a table columns are shown as well.
|
||||
The command form <literal>\d?</literal> is identical, but any comments
|
||||
associated with the table columns are shown as well.
|
||||
</para>
|
||||
|
||||
<note>
|
||||
@ -375,8 +375,10 @@ testdb=>
|
||||
Lists all available aggregate functions, together with the data type they operate on.
|
||||
If <replaceable class="parameter">pattern</replaceable>
|
||||
(a regular expression) is specified, only matching aggregates are shown.
|
||||
If the variable <envar>description</envar> is set, comments are listed for
|
||||
each function as well.
|
||||
If the alternative command form <literal>\da?</literal> is used,
|
||||
comments are listed for each function as well. The command form
|
||||
<literal>\da+</literal> will show more information about each aggregate
|
||||
function, which is usually not of general interest.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -389,14 +391,14 @@ testdb=>
|
||||
(which can be a regular expression), or of all objects if no argument is given.
|
||||
(<quote>Object</quote> covers aggregates, functions, operators, types, relations
|
||||
(tables, views, indices, sequences, large objects), rules, and triggers.) For example:
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
=> <userinput>\dd version</userinput>
|
||||
Object descriptions
|
||||
Name | What | Description
|
||||
---------+----------+---------------------------
|
||||
version | function | PostgreSQL version string
|
||||
(1 row)
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -423,8 +425,9 @@ testdb=>
|
||||
Lists available functions, together with their argument and return types.
|
||||
If <replaceable class="parameter">pattern</replaceable>
|
||||
(a regular expression) is specified, only matching functions are shown.
|
||||
If the variable <envar>description</envar> is set, comments are listed for
|
||||
each function as well.
|
||||
If the form <literal>\df+</literal> is used, additional information about
|
||||
each function is shown. Comments for each function can be shown with
|
||||
the <literal>\df?</literal> form.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -444,7 +447,7 @@ testdb=>
|
||||
<para>
|
||||
If <replaceable class="parameter">pattern</replaceable> is specified,
|
||||
it is a regular expression restricts the listing to those objects
|
||||
whose name matches. If the variable <envar>description</envar> is set,
|
||||
whose name matches. If one appends a <quote>?</quote> to the command name,
|
||||
each object is listed with its associated description, if any.
|
||||
</para>
|
||||
</listitem>
|
||||
@ -462,18 +465,19 @@ testdb=>
|
||||
|
||||
|
||||
<varlistentry>
|
||||
<term><literal>\do [ <replaceable class="parameter">name</replaceable> ]</literal></term>
|
||||
<term><literal>\do [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
Lists available operators with their operand and return types.
|
||||
If <replaceable class="parameter">name</replaceable>
|
||||
If <replaceable class="parameter">pattern</replaceable>
|
||||
is specified, only operators with that name will be shown.
|
||||
(Note that, unlike with similar commands, this is not a regular expression
|
||||
because operator names were likely to interfere with regular expression
|
||||
meta-characters.)
|
||||
(Since this is a regular expression, be sure to quote all special
|
||||
characters in you operator name with backslashes. To prevent
|
||||
interpretation of the backslash as a new command, you might also
|
||||
wish to quote the argument.)
|
||||
</para>
|
||||
<para>
|
||||
If the variable <envar>description</envar> is set, comments are listed for
|
||||
If the form <literal>\do?</literal> is used, comments are listed for
|
||||
each operator.
|
||||
</para>
|
||||
</listitem>
|
||||
@ -495,9 +499,9 @@ testdb=>
|
||||
<term><literal>\dT [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
|
||||
<listitem>
|
||||
<para>
|
||||
List all data types or only those that match <replaceable class="parameter">pattern</replaceable>.
|
||||
If the variable <envar>description</envar> is set, each type is listed with
|
||||
its associated description.
|
||||
Lists all data types or only those that match <replaceable class="parameter">pattern</replaceable>.
|
||||
The command forms <literal>\dT+</literal> and <literal>\dT?</literal> show extra information
|
||||
and the associated descriptions of the types, respectively.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -519,8 +523,8 @@ testdb=>
|
||||
The new query buffer is then re-parsed according to the normal rules of
|
||||
<application>psql</application>, where the whole buffer is treated as
|
||||
a single line. (Thus you cannot make <quote>scripts</quote> this way,
|
||||
use <command>\i</command> for that.) In particular, this means that
|
||||
if the query ends (or rather contains) a semicolon, it is immediately
|
||||
use <command>\i</command> for that.) This means also that
|
||||
if the query ends with (or rather contains) a semicolon, it is immediately
|
||||
executed. In other cases it will merely wait in the query buffer.
|
||||
</para>
|
||||
|
||||
@ -542,10 +546,10 @@ testdb=>
|
||||
<para>
|
||||
Prints the arguments to the standard output. This can be useful to
|
||||
intersperse information in the output of scripts. For example:
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
=> <userinput>\echo `date`</userinput>
|
||||
Tue Oct 26 21:40:57 CEST 1999
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<tip>
|
||||
@ -579,7 +583,7 @@ Tue Oct 26 21:40:57 CEST 1999
|
||||
Sends the current query input buffer to the backend and optionally
|
||||
saves the output in <replaceable class="parameter">filename</replaceable>
|
||||
or pipes the output into a separate Unix shell to execute
|
||||
<replaceable class="parameter">command</replaceable>. A blank <literal>\g</literal>
|
||||
<replaceable class="parameter">command</replaceable>. A bare <literal>\g</literal>
|
||||
is virtually equivalent to a semicolon. A <literal>\g</literal> with argument
|
||||
is a <quote>one-shot</quote> alternative to the <command>\o</command> command.
|
||||
</para>
|
||||
@ -591,8 +595,7 @@ Tue Oct 26 21:40:57 CEST 1999
|
||||
<listitem>
|
||||
<para>
|
||||
Give syntax help on the specified <acronym>SQL</acronym> command.
|
||||
If <replaceable class="parameter">command</replaceable> is not a defined <acronym>SQL</acronym> command
|
||||
or if <replaceable class="parameter">command</replaceable> is not specified,
|
||||
If <replaceable class="parameter">command</replaceable> is not specified,
|
||||
then <application>psql</application> will
|
||||
list all the commands for which syntax help is
|
||||
available. If <replaceable class="parameter">command</replaceable>
|
||||
@ -628,7 +631,7 @@ Tue Oct 26 21:40:57 CEST 1999
|
||||
<listitem>
|
||||
<para>
|
||||
Reads input from the file <replaceable class="parameter">filename</replaceable>
|
||||
and executes it as though it has been typed on the keyboard.
|
||||
and executes it as though it had been typed on the keyboard.
|
||||
</para>
|
||||
<note>
|
||||
<para>
|
||||
@ -644,9 +647,9 @@ Tue Oct 26 21:40:57 CEST 1999
|
||||
<term><literal>\l</literal> (or <literal>\list</literal>)</term>
|
||||
<listitem>
|
||||
<para>
|
||||
List all the databases in the server as well as their owners. If the
|
||||
variable <envar>description</envar> is set, any descriptions for
|
||||
the databases are shown as well. If your <productname>PostgreSQL</productname>
|
||||
List all the databases in the server as well as their owners. Append a
|
||||
<quote>?</quote> (question mark) to the command name to see any descriptions
|
||||
for the databases as well. If your <productname>PostgreSQL</productname>
|
||||
installation was
|
||||
compiled with multibyte encoding support, the encoding scheme of each
|
||||
database is shown as well.
|
||||
@ -688,15 +691,15 @@ Tue Oct 26 21:40:57 CEST 1999
|
||||
<para>
|
||||
Stores the file into a <productname>PostgreSQL</productname> <quote>large object</quote>.
|
||||
Optionally, it associates the given comment with the object. Example:
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
foo=> <userinput>\lo_import '/home/me/pictures/photo.xcf' 'a picture of me'</userinput>
|
||||
lo_import 152801
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
The response indicates that the large object received object id 152801
|
||||
which one ought to remember if one wants to access the object every again.
|
||||
which one ought to remember if one wants to access the object ever again.
|
||||
For that reason it is recommended to always associate a human-readable
|
||||
comment with every object. Those can then be seen with the
|
||||
<command>\lo_list</command> command.
|
||||
<command>\lo_list?</command> command.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -720,8 +723,8 @@ lo_import 152801
|
||||
<para>
|
||||
Shows a list of all <productname>PostgreSQL</productname> <quote>large
|
||||
objects</quote> currently stored in the database along with their owners.
|
||||
If the variable <envar>description</envar> is set, the associated
|
||||
comments are shown as well.
|
||||
Append a question mark to the command name (<literal>\lo_list?</literal>) to
|
||||
see the the associated comments as well.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -863,7 +866,7 @@ lo_import 152801
|
||||
<para>
|
||||
The second argument is a string that should be printed whenever a field
|
||||
is null. The default is not to print anything, which can easily be mistaken
|
||||
for, say, an empty string. There one might choose to write
|
||||
for, say, an empty string. Thus, one might choose to write
|
||||
<literal>\pset null "(null)"</literal>.
|
||||
</para>
|
||||
</listitem>
|
||||
@ -1015,7 +1018,7 @@ lo_import 152801
|
||||
|
||||
<note>
|
||||
<para>
|
||||
As of <application>psql</application> version 6.6 it is no longer
|
||||
As of <application>psql</application> version 7.0 it is no longer
|
||||
necessary, in fact, to save the command history as that will be done
|
||||
automatically on program termination. The history is then
|
||||
also automatically loaded every time <application>psql</application>
|
||||
@ -1034,17 +1037,14 @@ lo_import 152801
|
||||
Sets the internal variable <replaceable class="parameter">name</replaceable>
|
||||
to <replaceable class="parameter">value</replaceable>. If no second argument
|
||||
is given, the variable is unset (which is different from setting it to,
|
||||
for example, and empty string: <literal>\set foo ''</literal>). If no
|
||||
for example, an empty string: <literal>\set foo ''</literal>). If no
|
||||
arguments are given, all currently defined variables are listed with their
|
||||
values.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Valid variable names can contain lower-case characters, digits, and
|
||||
underscores. In particular, no upper-case characters are allowed, as
|
||||
those are reserved for certain <quote>magic</quote> variables and
|
||||
environment variables. See the section about <application>psql</application>
|
||||
variables for details.
|
||||
Valid variable names can contain characters, digits, and underscores.
|
||||
See the section about <application>psql</application> variables for details.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -1119,14 +1119,14 @@ lo_import 152801
|
||||
</para>
|
||||
|
||||
<para>
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
test=> <userinput>\z</userinput>
|
||||
Access permissions for database "test"
|
||||
Relation | Access permissions
|
||||
----------+-------------------------------------
|
||||
my_table | {"=r","joe=arwR", "group staff=ar"}
|
||||
(1 row )
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
Read this as follows:
|
||||
|
||||
<itemizedlist>
|
||||
@ -1170,7 +1170,10 @@ Access permissions for database "test"
|
||||
<listitem>
|
||||
<para>
|
||||
Escapes to a separate Unix shell or executes the Unix command
|
||||
<replaceable class="parameter">command</replaceable>.
|
||||
<replaceable class="parameter">command</replaceable>. The arguments
|
||||
are not further interpreted, the shell will see them as is. If you wish
|
||||
to capture the output of a shell command and/or use <application>psql</application>'s
|
||||
variable substitution features, use the backticks (<literal>`</literal>).
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -1290,7 +1293,7 @@ Access permissions for database "test"
|
||||
<para>
|
||||
Use the file <replaceable class="parameter">filename</replaceable>
|
||||
as the source of queries instead of reading queries interactively.
|
||||
After the file is processed, <application>terminates</application>.
|
||||
After the file is processed, <application>psql</application> terminates.
|
||||
This in many ways equivalent to the internal command <command>\i</command>.
|
||||
</para>
|
||||
</listitem>
|
||||
@ -1514,12 +1517,12 @@ Access permissions for database "test"
|
||||
|
||||
<para>
|
||||
The output looks similar to this:
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
~$ <userinput>psql -V</userinput>
|
||||
Server: PostgreSQL 6.5.2 on i586-pc-linux-gnu, compiled by egcs
|
||||
psql 6.6.0 on i586-pc-linux-gnu, compiled by gcc 2.8.1 (Oct 27 1999 15:15:04), long options,
|
||||
readline, history, locale, assert checks
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
The <quote>Server</quote> line is identical to the one returned by the
|
||||
backend function <function>version()</function> and thus might vary
|
||||
if you query different servers by using different connection
|
||||
@ -1553,7 +1556,7 @@ readline, history, locale, assert checks
|
||||
</para>
|
||||
|
||||
<para>
|
||||
As of version 6.6, <application>psql</application> automatically issues a
|
||||
As of version 7.0, <application>psql</application> automatically issues a
|
||||
password prompt whenever the backend requests password authentication.
|
||||
Because this is currently based on a <quote>hack</quote> the automatic
|
||||
recognition might mysteriously fail, hence this option to force a prompt.
|
||||
@ -1606,37 +1609,38 @@ readline, history, locale, assert checks
|
||||
|
||||
<para>
|
||||
<application>psql</application> provides variable substitution features
|
||||
similar to common Unix command shells. Variables are simply name/values
|
||||
similar to common Unix command shells. Variables are simply name/value
|
||||
pairs, where the value can be any string of any length. To set variables,
|
||||
use the <application>psql</application> meta-command <command>\set</command>:
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
testdb=> <userinput>\set foo bar</userinput>
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
sets the variable <quote>foo</quote> to the value <quote>bar</quote>. To retrieve
|
||||
the content of the variable, precede the name with a dollar-sign and use it
|
||||
as the argument of any slash command:
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
testdb=> <userinput>\echo $foo</userinput>
|
||||
bar
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
Alternatively, the value can also be interpolated into a double-quoted (or backtick-quoted)
|
||||
string, like so:
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
testdb=> <userinput>\echo "foo is now ${foo}."</userinput>
|
||||
foo is now bar.
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
(The curly braces are required. This is not <productname>Perl</productname>.) No variable substitution
|
||||
will be performed in single-quoted strings or in any of the backslash commands
|
||||
that have special parsing rules (<command>\copy</command>, <command>\help</command>).
|
||||
that have special parsing rules (e.g., <command>\copy</command>).
|
||||
</para>
|
||||
|
||||
<note>
|
||||
<para>
|
||||
The arguments of <command>\set</command> are subject to the same substitution
|
||||
rules as with other commands. Thus you can construct interesting references
|
||||
such as <literal>\set "${foo}bar" 'something'</literal> and get <quote>variable
|
||||
variables</quote> of <productname>Perl</productname> or <productname><acronym>PHP</acronym></productname>
|
||||
fame. Unfortunately (or fortunately?), there is not way to do anything useful
|
||||
such as <literal>\set "${foo}bar" 'something'</literal> and get <quote>soft
|
||||
links</quote> or <quote>variable variables</quote> of <productname>Perl</productname>
|
||||
or <productname><acronym>PHP</acronym></productname> fame, respectively.
|
||||
Unfortunately (or fortunately?), there is not way to do anything useful
|
||||
with these constructs. (<literal>\echo ${${foo}}</literal> doesn't work.) On the
|
||||
other hand, <literal>\set bar $foo</literal> is a perfectly valid way to copy
|
||||
a variable.
|
||||
@ -1645,14 +1649,21 @@ foo is now bar.
|
||||
|
||||
<para>
|
||||
<application>psql</application>'s internal variable names can consist of
|
||||
lower-case letters, numbers, and underscores in any order and any number of
|
||||
them. Upper-case letters are not allowed. (There is a reason for that. Keep reading.)
|
||||
If you attempt to refer to a variable that does not consist of those
|
||||
characters <application>psql</application> first checks if it is the name of
|
||||
one of several defined <quote>magic</quote> variables. Those variables you cannot
|
||||
set but they always have a value. By convention they all start with an
|
||||
upper-case letter. Finally, if no match is found that way, the value of
|
||||
the respective environment variable is substituted.
|
||||
letters, numbers, and underscores in any order and any number of them.
|
||||
It is recommended, however, that you stick to lower-case letters and do not
|
||||
begin with a digit. The partial rationale for this follows.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
If you attempt to refer to a variable that is not set,
|
||||
<application>psql</application> first checks if it is the name of one of
|
||||
several defined <quote>magic</quote> variables. Those variables are
|
||||
maintained internally and always have a value (at least when their semantics
|
||||
permit it). By convention they all start with an upper-case letter. You can
|
||||
set those variables manually, but that will <quote>shadow</quote> their
|
||||
special meaning, until you unset your personal copy. Finally, if no match is
|
||||
found that way, the value of the respective environment variable is
|
||||
substituted.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -1660,7 +1671,10 @@ foo is now bar.
|
||||
<envar>Version</envar> which contains a string with the version of
|
||||
<application>psql</application>; <envar>Database</envar>, <envar>Host</envar>,
|
||||
<envar>Port</envar>, <envar>User</envar> are the currently active
|
||||
connection options.
|
||||
connection options. <envar>LastOid</envar> contains the oid that was the
|
||||
result of the last <command>INSERT</command> or <command>\lo_import</command>
|
||||
command. If the last command was not one of those two, the value
|
||||
is undefined.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
@ -1673,19 +1687,6 @@ foo is now bar.
|
||||
only care whether or not are they set, not what to. A list of all specially
|
||||
treated variables follows.
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><envar>description</envar></term>
|
||||
<listitem>
|
||||
<para>
|
||||
If set, the various <command>\d*</command> commands as well as
|
||||
<command>\l</command> and <command>\lo_list</command> show object
|
||||
descriptions along with the normal information. (Except for
|
||||
<command>\dd</command> which always shows descriptions as this
|
||||
is its very purpose.)
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><envar>die_on_error</envar></term>
|
||||
<listitem>
|
||||
@ -1731,25 +1732,29 @@ foo is now bar.
|
||||
<term><envar>lo_transaction</envar></term>
|
||||
<listitem>
|
||||
<para>
|
||||
If you use the <productname>PostgreSQL</productname> large object interface to store
|
||||
data that does not fit into one tuple specially all the operations must be contained
|
||||
in a transaction block. (See the documentation of the large object interface for
|
||||
more information.) Since <application>psql</application> has no way to keep track if
|
||||
you already have a transaction in progress when you call one of its internal commands
|
||||
<command>\lo_export</command>, <command>\lo_import</command>, <command>\lo_unlink</command>
|
||||
it must take some arbitrary action. This action could either be to roll back any transaction
|
||||
that might already be in progress, or to commit any such transaction, or to do nothing
|
||||
at all. In the latter case you must provide you own <command>BEGIN</command>/<command>END</command>
|
||||
block or the results are unpredictable (usually resulting in the desired action not being
|
||||
performed anyway).
|
||||
If you use the <productname>PostgreSQL</productname> large object
|
||||
interface to specially store data that does not fit into one tuple,
|
||||
all the operations must be contained in a transaction block. (See the
|
||||
documentation of the large object interface for more information.) Since
|
||||
<application>psql</application> has no way to keep track if you already
|
||||
have a transaction in progress when you call one of its internal
|
||||
commands <command>\lo_export</command>, <command>\lo_import</command>,
|
||||
<command>\lo_unlink</command> it must take some arbitrary action. This
|
||||
action could either be to roll back any transaction that might already
|
||||
be in progress, or to commit any such transaction, or to do nothing at
|
||||
all. In the latter case you must provide you own
|
||||
<command>BEGIN TRANSACTION</command>/<command>COMMIT</command> block or
|
||||
the results will be unpredictable (usually resulting in the desired
|
||||
action not being performed anyway).
|
||||
</para>
|
||||
|
||||
<para>
|
||||
To choose what you want to do you set this variable to one of
|
||||
<quote>rollback</quote>, <quote>commit</quote>, or <quote>nothing</quote>. The default is
|
||||
to roll back the transaction. If you just want to load one or a few objects this is fine.
|
||||
However, if you intend to transfer many large objects, it might be advisable to
|
||||
provide one explicit transaction block around all commands.
|
||||
<quote>rollback</quote>, <quote>commit</quote>, or <quote>nothing</quote>.
|
||||
The default is to roll back the transaction. If you just want to load one
|
||||
or a few objects this is fine. However, if you intend to transfer many
|
||||
large objects, it might be advisable to provide one explicit transaction
|
||||
block around all commands.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
@ -1794,16 +1799,6 @@ foo is now bar.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><envar>sql_interpol</envar></term>
|
||||
<listitem>
|
||||
<para>
|
||||
The escape character for <acronym>SQL</acronym> variable interpolation. See below.
|
||||
</para>
|
||||
</listitem>
|
||||
</varlistentry>
|
||||
|
||||
</variablelist>
|
||||
|
||||
</para>
|
||||
@ -1817,48 +1812,37 @@ foo is now bar.
|
||||
<para>
|
||||
An additional useful feature of <application>psql</application> variables
|
||||
is that you can substitute (<quote>interpolate</quote>) them into
|
||||
regular <acronym>SQL</acronym> statements. In order not to break existing
|
||||
<acronym>SQL</acronym> statements, you must choose your own special
|
||||
character that tells <application>psql</application> that you wish to
|
||||
interpolate the value of a variable here. You do this by setting the
|
||||
variable <envar>sql_interpol</envar>. Only the first character will be
|
||||
looked at. You can set this variable to anything you want but, for instance,
|
||||
letters, numbers, semicolons, or backslashes will not make your life easier.
|
||||
Reasonable choices include the dollar (<quote>$</quote>) and pound
|
||||
(<quote>#</quote>) signs.
|
||||
<programlisting>
|
||||
testdb=> <userinput>\set sql_interpol '#'</userinput>
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
<para>
|
||||
Once this is set up, whenever <application>psql</application> sees the
|
||||
magic character where it would expect a query, it will continue scanning
|
||||
until it sees the same character again and will interpret anything in
|
||||
between as a variable name.
|
||||
<programlisting>
|
||||
regular <acronym>SQL</acronym> statements. The syntax for this is to prepend
|
||||
the variable name with a colon (<literal>:</literal>).
|
||||
<programlisting>
|
||||
testdb=> <userinput>\set foo 'my_table'</userinput>
|
||||
testdb=> <userinput>SELECT * FROM #foo#;</userinput>
|
||||
</programlisting>
|
||||
testdb=> <userinput>SELECT * FROM :foo;</userinput>
|
||||
</programlisting>
|
||||
would then query the table <literal>my_table</literal>. The value of the
|
||||
variable is copied literally, so it can even contain unbalanced quotes or
|
||||
backslash commands. You must make sure that it makes sense where you put it.
|
||||
Variable interpolation will not be performed into quoted <acronym>SQL</acronym>
|
||||
entities.
|
||||
</para>
|
||||
|
||||
<para>
|
||||
One possible application of this mechanism is to copy the contents of a file
|
||||
A popular application of this facility is to refer to the last inserted
|
||||
<acronym>OID</acronym> in subsequent statement to build a foreign key
|
||||
scenario.
|
||||
Another possible use of this mechanism is to copy the contents of a file
|
||||
into a field. First load the file into a variable and then proceed as above.
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
testdb=> <userinput>\set content `cat my_file.txt`</userinput>
|
||||
testdb=> <userinput>INSERT INTO my_table VALUES ('#content#');</userinput>
|
||||
</programlisting>
|
||||
testdb=> <userinput>\set content "'${content}'"</userinput>
|
||||
testdb=> <userinput>INSERT INTO my_table VALUES (:content);</userinput>
|
||||
</programlisting>
|
||||
One possible problem with this approach is that <filename>my_file.txt</filename>
|
||||
might contain single quotes. These need to be escaped so that
|
||||
they don't cause a syntax error when the second line is processed. This
|
||||
they don't cause a syntax error when the third line is processed. This
|
||||
could be done with the program <application>sed</application>:
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
testdb=> <userinput>\set content `sed -e "s/'/\\\\\\'/g" < my_file.txt`</userinput>
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
Observe the correct number of backslashes (6)! You can resolve it this way: After
|
||||
<application>psql</application> has parsed this line, it passes
|
||||
<literal>sed -e "s/'/\\\'/g" < my_file.txt</literal> to the shell. The shell
|
||||
@ -2003,15 +1987,15 @@ testdb=> <userinput>\set content `sed -e "s/'/\\\\\\'/g" < my_file.txt`</userinp
|
||||
<para>
|
||||
The first example shows how to spread a query over several lines of input.
|
||||
Notice the changing prompt.
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
testdb=> <userinput>CREATE TABLE my_table (</userinput>
|
||||
testdb-> <userinput> first int4 not null default 0,</userinput>
|
||||
testdb-> <userinput> second text</userinput>
|
||||
testdb-> <userinput>);</userinput>
|
||||
CREATE
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
Now look at the table definition again:
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
testdb=> <userinput>\d my_table</userinput>
|
||||
Table "my_table"
|
||||
Attribute | Type | Info
|
||||
@ -2019,15 +2003,15 @@ testdb=> <userinput>\d my_table</userinput>
|
||||
first | int4 | not null default 0
|
||||
second | text |
|
||||
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
At this point you decide to change the prompt to something more
|
||||
interesting:
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
testdb=> <userinput>\set prompt1 '%n@%m %~%R%# '</userinput>
|
||||
peter@localhost testdb=>
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
Let's assume you have filled the table with data and want to take a look at it:
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
peter@localhost testdb=> SELECT * FROM my_table;
|
||||
first | second
|
||||
-------+--------
|
||||
@ -2037,11 +2021,11 @@ peter@localhost testdb=> SELECT * FROM my_table;
|
||||
4 | four
|
||||
(4 rows)
|
||||
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
Notice how the int4 colums in right aligned while the text column in left aligned.
|
||||
You can make this table look differently by using the <command>\pset</command>
|
||||
command.
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
peter@localhost testdb=> <userinput>\pset border 2</userinput>
|
||||
Border style is 2.
|
||||
peter@localhost testdb=> <userinput>SELECT * FROM my_table;</userinput>
|
||||
@ -2079,9 +2063,9 @@ one,1
|
||||
two,2
|
||||
three,3
|
||||
four,4
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
Alternatively, use the short commands:
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
peter@localhost testdb=> <userinput>\a \t \x</userinput>
|
||||
Output format is aligned.
|
||||
Tuples only is off.
|
||||
@ -2099,7 +2083,7 @@ second | three
|
||||
-[ RECORD 4 ]-
|
||||
first | 4
|
||||
second | four
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
</para>
|
||||
|
||||
</refsect1>
|
||||
@ -2123,10 +2107,10 @@ second | four
|
||||
compatibility this is still supported to some extent but I am not
|
||||
going to explain the details here as this use is discouraged. But
|
||||
if you get strange messages, keep this in mind. For example
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
testdb=> <userinput>\foo</userinput>
|
||||
Field separator is "oo".
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
is perhaps not what one would expect.
|
||||
</para>
|
||||
</listitem>
|
||||
@ -2138,7 +2122,8 @@ Field separator is "oo".
|
||||
and attempting to get along with each other. Sometimes they do, sometimes
|
||||
they don't. An excellent example of this can be seen in section
|
||||
<quote><xref linkend="APP-PSQL-sql-interpol" endterm="APP-PSQL-sql-interpol-title"></quote>.
|
||||
Changing this situation, however, is beyond feasability.
|
||||
There are vague dreams of using <application>flex</application> in the future,
|
||||
but it won't happen soon.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
@ -2151,6 +2136,15 @@ Field separator is "oo".
|
||||
these limits sooner rather than later.
|
||||
</para>
|
||||
</listitem>
|
||||
|
||||
<listitem>
|
||||
<para>
|
||||
The number of options for a backslash command is limited, probably to 16.
|
||||
You can easily change this in the source code, and perhaps I will get around
|
||||
to fixing this one day (see previous item). Not that there is any command
|
||||
that actually uses that many options though.
|
||||
</para>
|
||||
</listitem>
|
||||
</itemizedlist>
|
||||
|
||||
</refsect2>
|
||||
@ -2169,9 +2163,9 @@ Field separator is "oo".
|
||||
<para>
|
||||
The present version is the result of a major clean-up and re-write in 1999 by
|
||||
<ulink URL="mailto:peter_e@gmx.net">Peter Eisentraut</ulink> in preparation for release 7.0.
|
||||
Many people had again contributed their ideas. The author would also like
|
||||
to recognize the influence of <application>tcsh</application> at a number
|
||||
of places.
|
||||
Many people had again contributed their ideas. A bunch of features were stolen
|
||||
from various shells (in case you hadn't noticed), in particular
|
||||
<application>tcsh</application>.
|
||||
</para>
|
||||
</refsect2>
|
||||
|
||||
@ -2197,9 +2191,9 @@ Field separator is "oo".
|
||||
<filename>readline/history.h</filename>) in appropriate directories. If
|
||||
you have the library and header files installed in an obscure place you
|
||||
must tell <filename>configure</filename> about them, for example:
|
||||
<programlisting>
|
||||
<programlisting>
|
||||
$ ./configure --with-includes=/opt/gnu/include --with-libraries=/opt/gnu/lib ...
|
||||
</programlisting>
|
||||
</programlisting>
|
||||
Then you have to recompile <application>psql</application> (not necessarily
|
||||
the entire code tree).
|
||||
</para>
|
||||
|
@ -7,7 +7,7 @@
|
||||
#
|
||||
#
|
||||
# IDENTIFICATION
|
||||
# $Header: /cvsroot/pgsql/src/bin/psql/Attic/Makefile.in,v 1.17 1999/11/08 15:59:59 momjian Exp $
|
||||
# $Header: /cvsroot/pgsql/src/bin/psql/Attic/Makefile.in,v 1.18 1999/11/26 04:24:16 momjian Exp $
|
||||
#
|
||||
#-------------------------------------------------------------------------
|
||||
|
||||
@ -30,10 +30,18 @@ endif
|
||||
|
||||
OBJS=command.o common.o help.o input.o stringutils.o mainloop.o \
|
||||
copy.o startup.o prompt.o variables.o large_obj.o print.o describe.o \
|
||||
@STRDUP@ @STRERROR2@
|
||||
tab-complete.o @STRDUP@ @STRERROR2@ @SNPRINTF@
|
||||
|
||||
all: submake psql
|
||||
|
||||
# Move this to the utils directory
|
||||
ifneq (@SNPRINTF@,)
|
||||
OBJS+=../../backend/port/snprintf.o
|
||||
|
||||
../../backend/port/snprintf.o:
|
||||
$(MAKE) -C ../../backend/port snprintf.o
|
||||
endif
|
||||
|
||||
psql: $(OBJS) $(LIBPQDIR)/libpq.a
|
||||
$(CC) -o psql -L$(LIBPQDIR) $(OBJS) -lpq $(LDFLAGS)
|
||||
|
||||
@ -69,7 +77,7 @@ clean:
|
||||
# sql_help.h is gone, for it needs the docs in the right place to be
|
||||
# regenerated. -- (pe)
|
||||
|
||||
distclean: clean
|
||||
maintainer-clean: clean
|
||||
rm -f sql_help.h
|
||||
|
||||
ifeq (depend,$(wildcard depend))
|
||||
|
@ -33,7 +33,7 @@
|
||||
#endif
|
||||
|
||||
|
||||
/* functions for use in this file only */
|
||||
/* functions for use in this file */
|
||||
|
||||
static backslashResult exec_command(const char *cmd,
|
||||
char *const * options,
|
||||
@ -41,16 +41,24 @@ static backslashResult exec_command(const char *cmd,
|
||||
PQExpBuffer query_buf,
|
||||
PsqlSettings *pset);
|
||||
|
||||
static bool
|
||||
do_edit(const char *filename_arg, PQExpBuffer query_buf);
|
||||
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf);
|
||||
|
||||
static char *
|
||||
unescape(const char *source, PsqlSettings *pset);
|
||||
static char * unescape(const char *source, PsqlSettings *pset);
|
||||
|
||||
static bool
|
||||
do_shell(const char *command);
|
||||
static bool do_connect(const char *new_dbname,
|
||||
const char *new_user,
|
||||
PsqlSettings *pset);
|
||||
|
||||
|
||||
static bool do_shell(const char *command);
|
||||
|
||||
/*
|
||||
* Perhaps this should be changed to "infinity",
|
||||
* but there is no convincing reason to bother
|
||||
* at this point.
|
||||
*/
|
||||
#define NR_OPTIONS 16
|
||||
|
||||
|
||||
/*----------
|
||||
* HandleSlashCmds:
|
||||
@ -78,7 +86,7 @@ HandleSlashCmds(PsqlSettings *pset,
|
||||
{
|
||||
backslashResult status = CMD_SKIP_LINE;
|
||||
char *my_line;
|
||||
char *options[17] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
|
||||
char *options[NR_OPTIONS+1];
|
||||
char *token;
|
||||
const char *options_string = NULL;
|
||||
const char *cmd;
|
||||
@ -120,7 +128,7 @@ HandleSlashCmds(PsqlSettings *pset,
|
||||
i = 0;
|
||||
token = strtokx(options_string, " \t", "\"'`", '\\', "e, &pos);
|
||||
|
||||
for (i = 0; token && i < 16; i++)
|
||||
for (i = 0; token && i < NR_OPTIONS; i++)
|
||||
{
|
||||
switch (quote)
|
||||
{
|
||||
@ -194,14 +202,15 @@ HandleSlashCmds(PsqlSettings *pset,
|
||||
options[i] = xstrdup(interpolate_var(token + 1, pset));
|
||||
else
|
||||
options[i] = xstrdup(token);
|
||||
break;
|
||||
}
|
||||
|
||||
if (continue_parse)
|
||||
break;
|
||||
|
||||
token = strtokx(NULL, " \t", "\"'`", '\\', "e, &pos);
|
||||
}
|
||||
} /* for */
|
||||
|
||||
options[i] = NULL;
|
||||
}
|
||||
|
||||
cmd = my_line;
|
||||
@ -216,11 +225,11 @@ HandleSlashCmds(PsqlSettings *pset,
|
||||
* arguments to start immediately after the command, but that is
|
||||
* no longer encouraged.
|
||||
*/
|
||||
const char *new_options[17];
|
||||
const char *new_options[NR_OPTIONS+1];
|
||||
char new_cmd[2];
|
||||
int i;
|
||||
|
||||
for (i = 1; i < 17; i++)
|
||||
for (i = 1; i < NR_OPTIONS+1; i++)
|
||||
new_options[i] = options[i - 1];
|
||||
new_options[0] = cmd + 1;
|
||||
|
||||
@ -249,7 +258,7 @@ HandleSlashCmds(PsqlSettings *pset,
|
||||
}
|
||||
|
||||
/* clean up */
|
||||
for (i = 0; i < 16 && options[i]; i++)
|
||||
for (i = 0; i < NR_OPTIONS && options[i]; i++)
|
||||
free(options[i]);
|
||||
|
||||
free(my_line);
|
||||
@ -274,10 +283,7 @@ exec_command(const char *cmd,
|
||||
backslashResult status = CMD_SKIP_LINE;
|
||||
|
||||
|
||||
/*
|
||||
* \a -- toggle field alignment This is deprecated and makes no sense,
|
||||
* but we keep it around.
|
||||
*/
|
||||
/* \a -- toggle field alignment This makes little sense but we keep it around. */
|
||||
if (strcmp(cmd, "a") == 0)
|
||||
{
|
||||
if (pset->popt.topt.format != PRINT_ALIGNED)
|
||||
@ -287,22 +293,19 @@ exec_command(const char *cmd,
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* \C -- override table title (formerly change HTML caption) This is
|
||||
* deprecated.
|
||||
*/
|
||||
/* \C -- override table title (formerly change HTML caption) */
|
||||
else if (strcmp(cmd, "C") == 0)
|
||||
success = do_pset("title", options[0], &pset->popt, quiet);
|
||||
|
||||
|
||||
|
||||
/*
|
||||
/*----------
|
||||
* \c or \connect -- connect to new database or as different user
|
||||
*
|
||||
* \c foo bar : connect to db "foo" as user "bar" \c foo [-] :
|
||||
* connect to db "foo" as current user \c - bar : connect to
|
||||
* current db as user "bar" \c : connect to default db as
|
||||
* default user
|
||||
* \c foo bar: connect to db "foo" as user "bar"
|
||||
* \c foo [-]: connect to db "foo" as current user
|
||||
* \c - bar: connect to current db as user "bar"
|
||||
* \c: connect to default db as default user
|
||||
*----------
|
||||
*/
|
||||
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
|
||||
{
|
||||
@ -335,35 +338,39 @@ exec_command(const char *cmd,
|
||||
/* \d* commands */
|
||||
else if (cmd[0] == 'd')
|
||||
{
|
||||
bool show_verbose = strchr(cmd, '+') ? true : false;
|
||||
bool show_desc = strchr(cmd, '?') ? true : false;
|
||||
|
||||
switch (cmd[1])
|
||||
{
|
||||
case '\0':
|
||||
case '?':
|
||||
if (options[0])
|
||||
success = describeTableDetails(options[0], pset);
|
||||
success = describeTableDetails(options[0], pset, show_desc);
|
||||
else
|
||||
success = listTables("tvs", NULL, pset); /* standard listing of
|
||||
* interesting things */
|
||||
/* standard listing of interesting things */
|
||||
success = listTables("tvs", NULL, pset, show_desc);
|
||||
break;
|
||||
case 'a':
|
||||
success = describeAggregates(options[0], pset);
|
||||
success = describeAggregates(options[0], pset, show_verbose, show_desc);
|
||||
break;
|
||||
case 'd':
|
||||
success = objectDescription(options[0], pset);
|
||||
break;
|
||||
case 'f':
|
||||
success = describeFunctions(options[0], pset);
|
||||
success = describeFunctions(options[0], pset, show_verbose, show_desc);
|
||||
break;
|
||||
case 'l':
|
||||
success = do_lo_list(pset);
|
||||
success = do_lo_list(pset, show_desc);
|
||||
break;
|
||||
case 'o':
|
||||
success = describeOperators(options[0], pset);
|
||||
success = describeOperators(options[0], pset, show_verbose, show_desc);
|
||||
break;
|
||||
case 'p':
|
||||
success = permissionsList(options[0], pset);
|
||||
break;
|
||||
case 'T':
|
||||
success = describeTypes(options[0], pset);
|
||||
success = describeTypes(options[0], pset, show_verbose, show_desc);
|
||||
break;
|
||||
case 't':
|
||||
case 'v':
|
||||
@ -371,9 +378,9 @@ exec_command(const char *cmd,
|
||||
case 's':
|
||||
case 'S':
|
||||
if (cmd[1] == 'S' && cmd[2] == '\0')
|
||||
success = listTables("Stvs", NULL, pset);
|
||||
success = listTables("Stvs", NULL, pset, show_desc);
|
||||
else
|
||||
success = listTables(&cmd[1], options[0], pset);
|
||||
success = listTables(&cmd[1], options[0], pset, show_desc);
|
||||
break;
|
||||
default:
|
||||
status = CMD_UNKNOWN;
|
||||
@ -399,14 +406,10 @@ exec_command(const char *cmd,
|
||||
fputs("\n", stdout);
|
||||
}
|
||||
|
||||
/*
|
||||
* \f -- change field separator (This is deprecated in favour of
|
||||
* \pset.)
|
||||
*/
|
||||
/* \f -- change field separator */
|
||||
else if (strcmp(cmd, "f") == 0)
|
||||
success = do_pset("fieldsep", options[0], &pset->popt, quiet);
|
||||
|
||||
|
||||
/* \g means send query */
|
||||
else if (strcmp(cmd, "g") == 0)
|
||||
{
|
||||
@ -419,12 +422,27 @@ exec_command(const char *cmd,
|
||||
|
||||
/* help */
|
||||
else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0)
|
||||
helpSQL(options_string);
|
||||
|
||||
{
|
||||
char buf[256] = "";
|
||||
int i;
|
||||
for (i=0; options && options[i] && strlen(buf)<255; i++)
|
||||
{
|
||||
strncat(buf, options[i], 255 - strlen(buf));
|
||||
if (strlen(buf)<255 && options[i+1])
|
||||
strcat(buf, " ");
|
||||
}
|
||||
buf[255] = '\0';
|
||||
helpSQL(buf);
|
||||
}
|
||||
|
||||
/* HTML mode */
|
||||
else if (strcmp(cmd, "H") == 0 || strcmp(cmd, "html") == 0)
|
||||
success = do_pset("format", "html", &pset->popt, quiet);
|
||||
{
|
||||
if (pset->popt.topt.format != PRINT_HTML)
|
||||
success = do_pset("format", "html", &pset->popt, quiet);
|
||||
else
|
||||
success = do_pset("format", "aligned", &pset->popt, quiet);
|
||||
}
|
||||
|
||||
|
||||
/* \i is include file */
|
||||
@ -439,9 +457,12 @@ exec_command(const char *cmd,
|
||||
success = process_file(options[0], pset);
|
||||
}
|
||||
|
||||
|
||||
/* \l is list databases */
|
||||
else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0)
|
||||
success = listAllDbs(pset);
|
||||
success = listAllDbs(pset, false);
|
||||
else if (strcmp(cmd, "l?") == 0 || strcmp(cmd, "list?") == 0)
|
||||
success = listAllDbs(pset, true);
|
||||
|
||||
|
||||
/* large object things */
|
||||
@ -470,7 +491,9 @@ exec_command(const char *cmd,
|
||||
}
|
||||
|
||||
else if (strcmp(cmd + 3, "list") == 0)
|
||||
success = do_lo_list(pset);
|
||||
success = do_lo_list(pset, false);
|
||||
else if (strcmp(cmd + 3, "list?") == 0)
|
||||
success = do_lo_list(pset, true);
|
||||
|
||||
else if (strcmp(cmd + 3, "unlink") == 0)
|
||||
{
|
||||
@ -828,7 +851,7 @@ unescape(const char *source, PsqlSettings *pset)
|
||||
* Returns true if all ok, false if the new connection couldn't be established
|
||||
* but the old one was set back. Otherwise it terminates the program.
|
||||
*/
|
||||
bool
|
||||
static bool
|
||||
do_connect(const char *new_dbname, const char *new_user, PsqlSettings *pset)
|
||||
{
|
||||
PGconn *oldconn = pset->db;
|
||||
|
@ -29,10 +29,6 @@ backslashResult HandleSlashCmds(PsqlSettings *pset,
|
||||
PQExpBuffer query_buf,
|
||||
const char **end_of_cmd);
|
||||
|
||||
bool do_connect(const char *new_dbname,
|
||||
const char *new_user,
|
||||
PsqlSettings *pset);
|
||||
|
||||
bool process_file(const char *filename,
|
||||
PsqlSettings *pset);
|
||||
|
||||
|
@ -3,11 +3,11 @@
|
||||
#include "common.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef HAVE_TERMIOS_H
|
||||
#include <termios.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifndef HAVE_STRDUP
|
||||
#include <strdup.h>
|
||||
#endif
|
||||
@ -15,9 +15,12 @@
|
||||
#include <assert.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h> /* for write() */
|
||||
#else
|
||||
#include <io.h> /* for _write() */
|
||||
#endif
|
||||
|
||||
#include <libpq-fe.h>
|
||||
#include <postgres_ext.h>
|
||||
#include <pqsignal.h>
|
||||
#include <version.h>
|
||||
|
||||
@ -30,6 +33,7 @@
|
||||
#ifdef WIN32
|
||||
#define popen(x,y) _popen(x,y)
|
||||
#define pclose(x) _pclose(x)
|
||||
#define write(a,b,c) _write(a,b,c)
|
||||
#endif
|
||||
|
||||
|
||||
@ -210,11 +214,13 @@ simple_prompt(const char *prompt, int maxlen, bool echo)
|
||||
/*
|
||||
* interpolate_var()
|
||||
*
|
||||
* If the variable is a regular psql variable, just return its value.
|
||||
* If it's a magic variable, return that value.
|
||||
* The idea here is that certain variables have a "magic" meaning, such as
|
||||
* LastOid. However, you can assign to those variables, but that will shadow
|
||||
* the magic meaning, until you unset it. If nothing matches, the value of
|
||||
* the environment variable is used.
|
||||
*
|
||||
* This function only returns NULL if you feed in NULL. Otherwise it's ready for
|
||||
* immediate consumption.
|
||||
* This function only returns NULL if you feed in NULL's (don't do that).
|
||||
* Otherwise, the return value is ready for immediate consumption.
|
||||
*/
|
||||
const char *
|
||||
interpolate_var(const char *name, PsqlSettings *pset)
|
||||
@ -229,14 +235,9 @@ interpolate_var(const char *name, PsqlSettings *pset)
|
||||
return NULL;
|
||||
#endif
|
||||
|
||||
if (strspn(name, VALID_VARIABLE_CHARS) == strlen(name))
|
||||
{
|
||||
var = GetVariable(pset->vars, name);
|
||||
if (var)
|
||||
return var;
|
||||
else
|
||||
return "";
|
||||
}
|
||||
var = GetVariable(pset->vars, name);
|
||||
if (var)
|
||||
return var;
|
||||
|
||||
/* otherwise return magic variable */
|
||||
|
||||
@ -279,11 +280,16 @@ interpolate_var(const char *name, PsqlSettings *pset)
|
||||
return "";
|
||||
}
|
||||
|
||||
/*
|
||||
* env vars (if env vars are all caps there should be no prob,
|
||||
* otherwise you're on your own
|
||||
*/
|
||||
if (strcmp(name, "LastOid") == 0)
|
||||
{
|
||||
static char buf[24];
|
||||
if (pset->lastOid == InvalidOid)
|
||||
return "";
|
||||
sprintf(buf, "%u", pset->lastOid);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* env vars */
|
||||
if ((var = getenv(name)))
|
||||
return var;
|
||||
|
||||
@ -293,42 +299,31 @@ interpolate_var(const char *name, PsqlSettings *pset)
|
||||
|
||||
|
||||
/*
|
||||
* Code to support command cancellation.
|
||||
* Code to support query cancellation
|
||||
*
|
||||
* If interactive, we enable a SIGINT signal catcher before we start a
|
||||
* query that sends a cancel request to the backend.
|
||||
* Note that sending the cancel directly from the signal handler is safe
|
||||
* only because PQrequestCancel is carefully written to make it so. We
|
||||
* have to be very careful what else we do in the signal handler.
|
||||
*
|
||||
* Writing on stderr is potentially dangerous, if the signal interrupted
|
||||
* some stdio operation on stderr. On Unix we can avoid trouble by using
|
||||
* write() instead; on Windows that's probably not workable, but we can
|
||||
* at least avoid trusting printf by using the more primitive fputs().
|
||||
* Before we start a query, we enable a SIGINT signal catcher that sends a
|
||||
* cancel request to the backend. Note that sending the cancel directly from
|
||||
* the signal handler is safe because PQrequestCancel() is written to make it
|
||||
* so. We have to be very careful what else we do in the signal handler. This
|
||||
* includes using write() for output.
|
||||
*/
|
||||
|
||||
PGconn *cancelConn;
|
||||
|
||||
#ifdef WIN32
|
||||
#define safe_write_stderr(String) fputs(s, stderr)
|
||||
#else
|
||||
#define safe_write_stderr(String) write(fileno(stderr), String, strlen(String))
|
||||
#endif
|
||||
static PGconn *cancelConn;
|
||||
|
||||
#define write_stderr(String) write(fileno(stderr), String, strlen(String))
|
||||
|
||||
static void
|
||||
handle_sigint(SIGNAL_ARGS)
|
||||
{
|
||||
/* accept signal if no connection */
|
||||
if (cancelConn == NULL)
|
||||
exit(1);
|
||||
return;
|
||||
/* Try to send cancel request */
|
||||
if (PQrequestCancel(cancelConn))
|
||||
safe_write_stderr("\nCANCEL request sent\n");
|
||||
write_stderr("\nCancel request sent\n");
|
||||
else
|
||||
{
|
||||
safe_write_stderr("\nCould not send cancel request: ");
|
||||
safe_write_stderr(PQerrorMessage(cancelConn));
|
||||
write_stderr("\nCould not send cancel request: ");
|
||||
write_stderr(PQerrorMessage(cancelConn));
|
||||
}
|
||||
}
|
||||
|
||||
@ -348,7 +343,7 @@ PSQLexec(PsqlSettings *pset, const char *query)
|
||||
|
||||
if (!pset->db)
|
||||
{
|
||||
fputs("You are not currently connected to a database.\n", stderr);
|
||||
fputs("You are currently not connected to a database.\n", stderr);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
@ -367,7 +362,7 @@ PSQLexec(PsqlSettings *pset, const char *query)
|
||||
|
||||
res = PQexec(pset->db, query);
|
||||
|
||||
pqsignal(SIGINT, SIG_DFL); /* no control-C is back to normal */
|
||||
pqsignal(SIGINT, SIG_DFL); /* now control-C is back to normal */
|
||||
|
||||
if (PQstatus(pset->db) == CONNECTION_BAD)
|
||||
{
|
||||
@ -393,7 +388,7 @@ PSQLexec(PsqlSettings *pset, const char *query)
|
||||
return res;
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "%s", PQerrorMessage(pset->db));
|
||||
fputs(PQerrorMessage(pset->db), pset->queryFout);
|
||||
PQclear(res);
|
||||
return NULL;
|
||||
}
|
||||
@ -422,7 +417,7 @@ SendQuery(PsqlSettings *pset, const char *query)
|
||||
|
||||
if (!pset->db)
|
||||
{
|
||||
fputs("You are not currently connected to a database.\n", stderr);
|
||||
fputs("You are currently not connected to a database.\n", stderr);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -430,10 +425,10 @@ SendQuery(PsqlSettings *pset, const char *query)
|
||||
{
|
||||
char buf[3];
|
||||
|
||||
fprintf(stdout, "***(Single step mode: Verify query)*********************************************\n"
|
||||
"QUERY: %s\n"
|
||||
"***(press return to proceed or enter x and return to cancel)********************\n",
|
||||
query);
|
||||
printf("***(Single step mode: Verify query)*********************************************\n"
|
||||
"%s\n"
|
||||
"***(press return to proceed or enter x and return to cancel)********************\n",
|
||||
query);
|
||||
fflush(stdout);
|
||||
fgets(buf, 3, stdin);
|
||||
if (buf[0] == 'x')
|
||||
@ -492,11 +487,15 @@ SendQuery(PsqlSettings *pset, const char *query)
|
||||
break;
|
||||
case PGRES_COMMAND_OK:
|
||||
success = true;
|
||||
fprintf(pset->queryFout, "%s\n", PQcmdStatus(results));
|
||||
pset->lastOid = PQoidValue(results);
|
||||
if (!GetVariableBool(pset->vars, "quiet")) {
|
||||
fprintf(pset->queryFout, "%s\n", PQcmdStatus(results));
|
||||
fflush(pset->queryFout);
|
||||
}
|
||||
break;
|
||||
|
||||
case PGRES_COPY_OUT:
|
||||
if (pset->cur_cmd_interactive && !GetVariable(pset->vars, "quiet"))
|
||||
if (pset->cur_cmd_interactive && !GetVariableBool(pset->vars, "quiet"))
|
||||
puts("Copy command returns:");
|
||||
|
||||
success = handleCopyOut(pset->db, pset->queryFout);
|
||||
@ -516,6 +515,7 @@ SendQuery(PsqlSettings *pset, const char *query)
|
||||
case PGRES_BAD_RESPONSE:
|
||||
success = false;
|
||||
fputs(PQerrorMessage(pset->db), pset->queryFout);
|
||||
fflush(pset->queryFout);
|
||||
break;
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,42 +1,34 @@
|
||||
#ifndef DESCRIBE_H
|
||||
#define DESCRIBE_H
|
||||
|
||||
#include <c.h>
|
||||
#include "settings.h"
|
||||
|
||||
/* \da */
|
||||
bool
|
||||
describeAggregates(const char *name, PsqlSettings *pset);
|
||||
bool describeAggregates(const char *name, PsqlSettings *pset, bool verbose, bool desc);
|
||||
|
||||
/* \df */
|
||||
bool
|
||||
describeFunctions(const char *name, PsqlSettings *pset);
|
||||
bool describeFunctions(const char *name, PsqlSettings *pset, bool verbose, bool desc);
|
||||
|
||||
/* \dT */
|
||||
bool
|
||||
describeTypes(const char *name, PsqlSettings *pset);
|
||||
bool describeTypes(const char *name, PsqlSettings *pset, bool verbose, bool desc);
|
||||
|
||||
/* \do */
|
||||
bool
|
||||
describeOperators(const char *name, PsqlSettings *pset);
|
||||
bool describeOperators(const char *name, PsqlSettings *pset, bool verbose, bool desc);
|
||||
|
||||
/* \dp (formerly \z) */
|
||||
bool
|
||||
permissionsList(const char *name, PsqlSettings *pset);
|
||||
/* \z (or \dp) */
|
||||
bool permissionsList(const char *name, PsqlSettings *pset);
|
||||
|
||||
/* \dd */
|
||||
bool
|
||||
objectDescription(const char *object, PsqlSettings *pset);
|
||||
bool objectDescription(const char *object, PsqlSettings *pset);
|
||||
|
||||
/* \d foo */
|
||||
bool
|
||||
describeTableDetails(const char *name, PsqlSettings *pset);
|
||||
bool describeTableDetails(const char *name, PsqlSettings *pset, bool desc);
|
||||
|
||||
/* \l */
|
||||
bool
|
||||
listAllDbs(PsqlSettings *pset);
|
||||
bool listAllDbs(PsqlSettings *pset, bool desc);
|
||||
|
||||
/* \dt, \di, \dS, etc. */
|
||||
bool
|
||||
listTables(const char *infotype, const char *name, PsqlSettings *pset);
|
||||
/* \dt, \di, \ds, \dS, etc. */
|
||||
bool listTables(const char *infotype, const char *name, PsqlSettings *pset, bool desc);
|
||||
|
||||
#endif /* DESCRIBE_H */
|
||||
|
@ -180,7 +180,7 @@ slashUsage(PsqlSettings *pset)
|
||||
fprintf(fout, " \\c[onnect] [dbname|- [user|?]] -- connect to new database (now '%s')\n", PQdb(pset->db));
|
||||
fprintf(fout, " \\copy [binary] <table> [with oids] {from|to} <fname>[using delimiters '<char>']\n");
|
||||
fprintf(fout, " \\copyright -- show PostgreSQL copyright\n");
|
||||
fprintf(fout, " \\d -- list tables, views, and sequences\n");
|
||||
fprintf(fout, " \\d <table> -- describe table (or view, index, sequence)\n");
|
||||
fprintf(fout, " \\d{i|s|t|v|S}-- list only indices/sequences/tables/views/system tables\n");
|
||||
fprintf(fout, " \\da -- list aggregates\n");
|
||||
fprintf(fout, " \\dd [object] -- list comment for table, type, function, or operator\n");
|
||||
|
@ -4,7 +4,8 @@
|
||||
|
||||
#include <pqexpbuffer.h>
|
||||
|
||||
/* Note that this file does not depend on any other files in psql. */
|
||||
#include "settings.h"
|
||||
#include "tab-complete.h"
|
||||
|
||||
/* Runtime options for turning off readline and history */
|
||||
/* (of course there is no runtime command for doing that :) */
|
||||
@ -96,13 +97,14 @@ gets_fromFile(FILE *source)
|
||||
* The only "flag" right now is 1 for use readline & history.
|
||||
*/
|
||||
void
|
||||
initializeInput(int flags)
|
||||
initializeInput(int flags, PsqlSettings *pset)
|
||||
{
|
||||
#ifdef USE_READLINE
|
||||
if (flags == 1)
|
||||
{
|
||||
useReadline = true;
|
||||
rl_readline_name = "psql";
|
||||
initialize_readline(&(pset->db));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -37,20 +37,15 @@
|
||||
#endif
|
||||
|
||||
|
||||
char *
|
||||
gets_interactive(const char *prompt);
|
||||
char * gets_interactive(const char *prompt);
|
||||
|
||||
char *
|
||||
gets_fromFile(FILE *source);
|
||||
char * gets_fromFile(FILE *source);
|
||||
|
||||
|
||||
void
|
||||
initializeInput(int flags);
|
||||
void initializeInput(int flags, PsqlSettings *pset);
|
||||
|
||||
bool
|
||||
saveHistory(const char *fname);
|
||||
bool saveHistory(const char *fname);
|
||||
|
||||
void
|
||||
finishInput(void);
|
||||
void finishInput(void);
|
||||
|
||||
#endif
|
||||
|
@ -220,6 +220,7 @@ do_lo_import(PsqlSettings *pset, const char *filename_arg, const char *comment_a
|
||||
|
||||
|
||||
fprintf(pset->queryFout, "lo_import %d\n", loid);
|
||||
pset->lastOid = loid;
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -311,21 +312,20 @@ do_lo_unlink(PsqlSettings *pset, const char *loid_arg)
|
||||
* Show all large objects in database, with comments if desired
|
||||
*/
|
||||
bool
|
||||
do_lo_list(PsqlSettings *pset)
|
||||
do_lo_list(PsqlSettings *pset, bool desc)
|
||||
{
|
||||
PGresult *res;
|
||||
char descbuf[512];
|
||||
char buf[512];
|
||||
printQueryOpt myopt = pset->popt;
|
||||
|
||||
descbuf[0] = '\0';
|
||||
strcat(descbuf, "SELECT usename as \"Owner\", substring(relname from 5) as \"ID\"");
|
||||
if (GetVariableBool(pset->vars, "description"))
|
||||
strcat(descbuf, ",\n obj_description(pg_class.oid) as \"Description\"");
|
||||
strcat(descbuf, "\nFROM pg_class, pg_user\n"
|
||||
strcpy(buf, "SELECT usename as \"Owner\", substring(relname from 5) as \"ID\"");
|
||||
if (desc)
|
||||
strcat(buf, ",\n obj_description(pg_class.oid) as \"Description\"");
|
||||
strcat(buf, "\nFROM pg_class, pg_user\n"
|
||||
"WHERE usesysid = relowner AND relkind = 'l'\n"
|
||||
"ORDER BY \"ID\"");
|
||||
|
||||
res = PSQLexec(pset, descbuf);
|
||||
res = PSQLexec(pset, buf);
|
||||
if (!res)
|
||||
return false;
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
#ifndef LARGE_OBJ_H
|
||||
#define LARGE_OBJ_H
|
||||
|
||||
#include <c.h>
|
||||
#include "settings.h"
|
||||
|
||||
bool do_lo_export(PsqlSettings *pset, const char *loid_arg, const char *filename_arg);
|
||||
bool do_lo_import(PsqlSettings *pset, const char *filename_arg, const char *comment_arg);
|
||||
bool do_lo_unlink(PsqlSettings *pset, const char *loid_arg);
|
||||
bool do_lo_list(PsqlSettings *pset);
|
||||
bool do_lo_list(PsqlSettings *pset, bool desc);
|
||||
|
||||
#endif /* LARGE_OBJ_H */
|
||||
|
@ -16,19 +16,20 @@
|
||||
|
||||
|
||||
|
||||
/* MainLoop()
|
||||
/*
|
||||
* Main processing loop for reading lines of input
|
||||
* and sending them to the backend.
|
||||
*
|
||||
* This loop is re-entrant. May be called by \i command
|
||||
* which reads input from a file.
|
||||
*
|
||||
* FIXME: rewrite this whole thing with flex
|
||||
*/
|
||||
int
|
||||
MainLoop(PsqlSettings *pset, FILE *source)
|
||||
{
|
||||
PQExpBuffer query_buf; /* buffer for query being accumulated */
|
||||
char *line; /* current line of input */
|
||||
char *xcomment; /* start of extended comment */
|
||||
int len; /* length of the line */
|
||||
int successResult = EXIT_SUCCESS;
|
||||
backslashResult slashCmdStatus;
|
||||
@ -37,6 +38,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
bool success;
|
||||
char in_quote; /* == 0 for no in_quote */
|
||||
bool was_bslash; /* backslash */
|
||||
bool xcomment; /* in extended comment */
|
||||
int paren_level;
|
||||
unsigned int query_start;
|
||||
|
||||
@ -49,7 +51,6 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
bool prev_cmd_interactive;
|
||||
|
||||
bool die_on_error;
|
||||
const char *interpol_char;
|
||||
|
||||
|
||||
/* Save old settings */
|
||||
@ -68,7 +69,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
xcomment = NULL;
|
||||
xcomment = false;
|
||||
in_quote = 0;
|
||||
paren_level = 0;
|
||||
slashCmdStatus = CMD_UNKNOWN; /* set default */
|
||||
@ -87,7 +88,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
line = strdup(query_buf->data);
|
||||
resetPQExpBuffer(query_buf);
|
||||
/* reset parsing state since we are rescanning whole query */
|
||||
xcomment = NULL;
|
||||
xcomment = false;
|
||||
in_quote = 0;
|
||||
paren_level = 0;
|
||||
}
|
||||
@ -106,7 +107,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
prompt_status = PROMPT_SINGLEQUOTE;
|
||||
else if (in_quote && in_quote == '"')
|
||||
prompt_status = PROMPT_DOUBLEQUOTE;
|
||||
else if (xcomment != NULL)
|
||||
else if (xcomment)
|
||||
prompt_status = PROMPT_COMMENT;
|
||||
else if (query_buf->len > 0)
|
||||
prompt_status = PROMPT_CONTINUE;
|
||||
@ -120,10 +121,11 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
}
|
||||
|
||||
|
||||
/* Setting these will not have effect until next line */
|
||||
/* Setting this will not have effect until next line. (Faster.
|
||||
Also think about what happens if there is an error processing
|
||||
_this_ command.)
|
||||
*/
|
||||
die_on_error = GetVariableBool(pset->vars, "die_on_error");
|
||||
interpol_char = GetVariable(pset->vars, "sql_interpol");;
|
||||
|
||||
|
||||
/*
|
||||
* query_buf holds query already accumulated. line is the
|
||||
@ -144,11 +146,6 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
continue;
|
||||
}
|
||||
|
||||
/* not currently inside an extended comment? */
|
||||
if (xcomment)
|
||||
xcomment = line;
|
||||
|
||||
|
||||
/* strip trailing backslashes, they don't have a clear meaning */
|
||||
while (1)
|
||||
{
|
||||
@ -160,52 +157,6 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
/* echo back if input is from file and flag is set */
|
||||
if (!pset->cur_cmd_interactive && GetVariableBool(pset->vars, "echo"))
|
||||
fprintf(stderr, "%s\n", line);
|
||||
|
||||
|
||||
/* interpolate variables into SQL */
|
||||
len = strlen(line);
|
||||
thislen = PQmblen(line);
|
||||
|
||||
for (i = 0; line[i]; i += (thislen = PQmblen(&line[i])))
|
||||
{
|
||||
if (interpol_char && interpol_char[0] != '\0' && interpol_char[0] == line[i])
|
||||
{
|
||||
size_t in_length,
|
||||
out_length;
|
||||
const char *value;
|
||||
char *new;
|
||||
bool closer; /* did we have a closing delimiter
|
||||
* or just an end of line? */
|
||||
|
||||
in_length = strcspn(&line[i + thislen], interpol_char);
|
||||
closer = line[i + thislen + in_length] == line[i];
|
||||
line[i + thislen + in_length] = '\0';
|
||||
value = interpolate_var(&line[i + thislen], pset);
|
||||
out_length = strlen(value);
|
||||
|
||||
new = malloc(len + out_length - (in_length + (closer ? 2 : 1)) + 1);
|
||||
if (!new)
|
||||
{
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
new[0] = '\0';
|
||||
strncat(new, line, i);
|
||||
strcat(new, value);
|
||||
if (closer)
|
||||
strcat(new, line + i + 2 + in_length);
|
||||
|
||||
free(line);
|
||||
line = new;
|
||||
i += out_length;
|
||||
}
|
||||
}
|
||||
|
||||
/* nothing left on line? then ignore */
|
||||
if (line[0] == '\0')
|
||||
{
|
||||
@ -213,6 +164,12 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
/* echo back if input is from file and flag is set */
|
||||
if (!pset->cur_cmd_interactive && GetVariableBool(pset->vars, "echo"))
|
||||
puts(line);
|
||||
|
||||
|
||||
slashCmdStatus = CMD_UNKNOWN;
|
||||
|
||||
len = strlen(line);
|
||||
@ -224,24 +181,15 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
* The current character is at line[i], the prior character at line[i
|
||||
* - prevlen], the next character at line[i + thislen].
|
||||
*/
|
||||
prevlen = 0;
|
||||
thislen = (len > 0) ? PQmblen(line) : 0;
|
||||
|
||||
#define ADVANCE_1 (prevlen = thislen, i += thislen, thislen = PQmblen(line+i))
|
||||
#define ADVANCE_1 (prevlen = thislen, i += thislen, thislen = PQmblen(line+i))
|
||||
|
||||
success = true;
|
||||
for (i = 0; i < len; ADVANCE_1)
|
||||
for (i = 0, prevlen = 0, thislen = (len > 0) ? PQmblen(line) : 0;
|
||||
i < len;
|
||||
ADVANCE_1)
|
||||
{
|
||||
if (!success && die_on_error)
|
||||
break;
|
||||
|
||||
|
||||
/* was the previous character a backslash? */
|
||||
if (i > 0 && line[i - prevlen] == '\\')
|
||||
was_bslash = true;
|
||||
else
|
||||
was_bslash = false;
|
||||
|
||||
was_bslash = (i > 0 && line[i - prevlen] == '\\');
|
||||
|
||||
/* in quote? */
|
||||
if (in_quote)
|
||||
@ -256,11 +204,11 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
in_quote = line[i];
|
||||
|
||||
/* in extended comment? */
|
||||
else if (xcomment != NULL)
|
||||
else if (xcomment)
|
||||
{
|
||||
if (line[i] == '*' && line[i + thislen] == '/')
|
||||
{
|
||||
xcomment = NULL;
|
||||
xcomment = false;
|
||||
ADVANCE_1;
|
||||
}
|
||||
}
|
||||
@ -268,7 +216,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
/* start of extended comment? */
|
||||
else if (line[i] == '/' && line[i + thislen] == '*')
|
||||
{
|
||||
xcomment = &line[i];
|
||||
xcomment = true;
|
||||
ADVANCE_1;
|
||||
}
|
||||
|
||||
@ -287,8 +235,45 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
else if (line[i] == ')' && paren_level > 0)
|
||||
paren_level--;
|
||||
|
||||
/* colon -> substitute variable */
|
||||
/* we need to be on the watch for the '::' operator */
|
||||
else if (line[i] == ':' && !was_bslash &&
|
||||
strspn(line+i+thislen, VALID_VARIABLE_CHARS)>0 &&
|
||||
(prevlen > 0 && line[i-prevlen]!=':')
|
||||
)
|
||||
{
|
||||
size_t in_length,
|
||||
out_length;
|
||||
const char *value;
|
||||
char *new;
|
||||
char after; /* the character after the variable name
|
||||
will be temporarily overwritten */
|
||||
|
||||
in_length = strspn(&line[i + thislen], VALID_VARIABLE_CHARS);
|
||||
after = line[i + thislen + in_length];
|
||||
line[i + thislen + in_length] = '\0';
|
||||
value = interpolate_var(&line[i + thislen], pset);
|
||||
out_length = strlen(value);
|
||||
|
||||
new = malloc(len + out_length - (1 + in_length) + 1);
|
||||
if (!new)
|
||||
{
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
sprintf(new, "%.*s%s%c", i, line, value, after);
|
||||
if (after)
|
||||
strcat(new, line + i + 1 + in_length + 1);
|
||||
|
||||
free(line);
|
||||
line = new;
|
||||
continue; /* reparse the just substituted */
|
||||
}
|
||||
|
||||
|
||||
/* semicolon? then send query */
|
||||
else if (line[i] == ';' && !was_bslash && paren_level == 0)
|
||||
else if (line[i] == ';' && !was_bslash)
|
||||
{
|
||||
line[i] = '\0';
|
||||
/* is there anything else on the line? */
|
||||
@ -312,6 +297,15 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
query_start = i + thislen;
|
||||
}
|
||||
|
||||
/* if you have a burning need to send a semicolon or colon to
|
||||
the backend ... */
|
||||
else if (was_bslash && (line[i] == ';' || line[i] == ':'))
|
||||
{
|
||||
/* remove the backslash */
|
||||
memmove(line + i - prevlen, line + i, len - i + 1);
|
||||
len--;
|
||||
}
|
||||
|
||||
/* backslash command */
|
||||
else if (was_bslash)
|
||||
{
|
||||
@ -355,7 +349,13 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* stop the script after error */
|
||||
if (!success && die_on_error)
|
||||
break;
|
||||
|
||||
} /* for (line) */
|
||||
|
||||
|
||||
if (!success && die_on_error && !pset->cur_cmd_interactive)
|
||||
|
@ -30,8 +30,8 @@
|
||||
static void
|
||||
print_unaligned_text(const char *title, const char * const * headers,
|
||||
const char * const * cells, const char * const * footers,
|
||||
const char *opt_fieldsep, bool opt_barebones,
|
||||
FILE *fout)
|
||||
const char *opt_fieldsep, bool opt_barebones,
|
||||
FILE *fout)
|
||||
{
|
||||
unsigned int col_count = 0;
|
||||
unsigned int i;
|
||||
@ -83,8 +83,8 @@ print_unaligned_text(const char *title, const char * const * headers,
|
||||
static void
|
||||
print_unaligned_vertical(const char *title, const char * const * headers,
|
||||
const char * const * cells, const char * const * footers,
|
||||
const char *opt_fieldsep, bool opt_barebones,
|
||||
FILE *fout)
|
||||
const char *opt_fieldsep, bool opt_barebones,
|
||||
FILE *fout)
|
||||
{
|
||||
unsigned int col_count = 0;
|
||||
unsigned int i;
|
||||
@ -172,7 +172,7 @@ static void
|
||||
print_aligned_text(const char *title, const char * const * headers,
|
||||
const char * const * cells, const char * const * footers,
|
||||
const char *opt_align, bool opt_barebones, unsigned short int opt_border,
|
||||
FILE *fout)
|
||||
FILE *fout)
|
||||
{
|
||||
unsigned int col_count = 0;
|
||||
unsigned int i,
|
||||
@ -313,8 +313,8 @@ print_aligned_text(const char *title, const char * const * headers,
|
||||
static void
|
||||
print_aligned_vertical(const char *title, const char * const * headers,
|
||||
const char * const * cells, const char * const * footers,
|
||||
bool opt_barebones, unsigned short int opt_border,
|
||||
FILE *fout)
|
||||
bool opt_barebones, unsigned short int opt_border,
|
||||
FILE *fout)
|
||||
{
|
||||
unsigned int col_count = 0;
|
||||
unsigned int record = 1;
|
||||
@ -371,11 +371,10 @@ print_aligned_vertical(const char *title, const char * const * headers,
|
||||
{
|
||||
if (!opt_barebones)
|
||||
{
|
||||
char *div_copy = strdup(divider);
|
||||
char *record_str = malloc(32);
|
||||
size_t record_str_len;
|
||||
|
||||
if (!div_copy || !record_str)
|
||||
if (!record_str)
|
||||
{
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
@ -386,29 +385,32 @@ print_aligned_vertical(const char *title, const char * const * headers,
|
||||
else
|
||||
sprintf(record_str, "[ RECORD %d ]", record++);
|
||||
record_str_len = strlen(record_str);
|
||||
if (record_str_len + opt_border > strlen(div_copy))
|
||||
{
|
||||
void *new;
|
||||
|
||||
new = realloc(div_copy, record_str_len + opt_border);
|
||||
if (!new)
|
||||
{
|
||||
perror("realloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
div_copy = new;
|
||||
}
|
||||
strncpy(div_copy + opt_border, record_str, record_str_len);
|
||||
fprintf(fout, "%s\n", div_copy);
|
||||
if (record_str_len + opt_border > strlen(divider))
|
||||
fprintf(fout, "%.*s%s\n", opt_border, divider, record_str);
|
||||
else
|
||||
{
|
||||
char *div_copy = strdup(divider);
|
||||
|
||||
if (!div_copy) {
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
strncpy(div_copy + opt_border, record_str, record_str_len);
|
||||
fprintf(fout, "%s\n", div_copy);
|
||||
free(div_copy);
|
||||
}
|
||||
free(record_str);
|
||||
free(div_copy);
|
||||
}
|
||||
else if (i != 0 && opt_border < 2)
|
||||
else if (i != 0 || opt_border == 2)
|
||||
fprintf(fout, "%s\n", divider);
|
||||
}
|
||||
|
||||
if (opt_border == 2)
|
||||
fputs("| ", fout);
|
||||
fprintf(fout, "%-*s", hwidth, headers[i % col_count]);
|
||||
|
||||
if (opt_border > 0)
|
||||
fputs(" | ", fout);
|
||||
else
|
||||
@ -479,7 +481,7 @@ print_html_text(const char *title, const char * const * headers,
|
||||
const char * const * cells, const char * const * footers,
|
||||
const char *opt_align, bool opt_barebones, unsigned short int opt_border,
|
||||
const char *opt_table_attr,
|
||||
FILE *fout)
|
||||
FILE *fout)
|
||||
{
|
||||
unsigned int col_count = 0;
|
||||
unsigned int i;
|
||||
@ -661,7 +663,7 @@ static void
|
||||
print_latex_text(const char *title, const char * const * headers,
|
||||
const char * const * cells, const char * const * footers,
|
||||
const char *opt_align, bool opt_barebones, unsigned short int opt_border,
|
||||
FILE *fout)
|
||||
FILE *fout)
|
||||
{
|
||||
unsigned int col_count = 0;
|
||||
unsigned int i;
|
||||
@ -757,7 +759,7 @@ static void
|
||||
print_latex_vertical(const char *title, const char * const * headers,
|
||||
const char * const * cells, const char * const * footers,
|
||||
const char *opt_align, bool opt_barebones, unsigned short int opt_border,
|
||||
FILE *fout)
|
||||
FILE *fout)
|
||||
{
|
||||
unsigned int col_count = 0;
|
||||
unsigned int i;
|
||||
@ -836,7 +838,7 @@ print_latex_vertical(const char *title, const char * const * headers,
|
||||
|
||||
|
||||
/********************************/
|
||||
/* Public functions */
|
||||
/* Public functions */
|
||||
/********************************/
|
||||
|
||||
|
||||
@ -845,8 +847,8 @@ printTable(const char *title,
|
||||
const char * const * headers,
|
||||
const char * const * cells,
|
||||
const char * const * footers,
|
||||
const char *align,
|
||||
const printTableOpt * opt, FILE *fout)
|
||||
const char *align,
|
||||
const printTableOpt * opt, FILE *fout)
|
||||
{
|
||||
const char *default_footer[] = {NULL};
|
||||
unsigned short int border = opt->border;
|
||||
@ -968,8 +970,8 @@ printQuery(const PGresult *result, const printQueryOpt * opt, FILE *fout)
|
||||
int nfields;
|
||||
const char **headers;
|
||||
const char **cells;
|
||||
char **footers;
|
||||
char *align;
|
||||
char **footers;
|
||||
char *align;
|
||||
int i;
|
||||
|
||||
/* extract headers */
|
||||
|
@ -46,8 +46,8 @@ typedef struct _printTableOpt
|
||||
*/
|
||||
void printTable(const char *title, const char * const * headers,
|
||||
const char * const * cells, const char * const * footers,
|
||||
const char *align,
|
||||
const printTableOpt * opt, FILE *fout);
|
||||
const char *align,
|
||||
const printTableOpt * opt, FILE *fout);
|
||||
|
||||
|
||||
|
||||
|
@ -85,7 +85,7 @@ get_prompt(PsqlSettings *pset, promptStatus_t status)
|
||||
p && *p && strlen(destination) < MAX_PROMPT_SIZE;
|
||||
p++)
|
||||
{
|
||||
MemSet(buf, 0, MAX_PROMPT_SIZE + 1);
|
||||
memset(buf, 0, MAX_PROMPT_SIZE + 1);
|
||||
if (esc)
|
||||
{
|
||||
switch (*p)
|
||||
|
@ -1,11 +1,13 @@
|
||||
#ifndef SETTINGS_H
|
||||
#define SETTINGS_H
|
||||
#include <config.h>
|
||||
#include <c.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <libpq-fe.h>
|
||||
#include <c.h>
|
||||
#include <postgres_ext.h>
|
||||
|
||||
#include "variables.h"
|
||||
#include "print.h"
|
||||
@ -41,6 +43,8 @@ typedef struct _psqlSettings
|
||||
|
||||
bool has_client_encoding; /* was PGCLIENTENCODING set on
|
||||
* startup? */
|
||||
Oid lastOid; /* saves oid from insert command
|
||||
because people want it so badly */
|
||||
} PsqlSettings;
|
||||
|
||||
|
||||
|
@ -88,7 +88,7 @@ main(int argc, char **argv)
|
||||
char *password = NULL;
|
||||
bool need_pass;
|
||||
|
||||
MemSet(&settings, 0, sizeof settings);
|
||||
memset(&settings, 0, sizeof settings);
|
||||
|
||||
settings.cur_cmd_source = stdin;
|
||||
settings.cur_cmd_interactive = false;
|
||||
@ -161,7 +161,7 @@ main(int argc, char **argv)
|
||||
|
||||
if (options.action == ACT_LIST_DB)
|
||||
{
|
||||
int success = listAllDbs(&settings);
|
||||
int success = listAllDbs(&settings, false);
|
||||
|
||||
PQfinish(settings.db);
|
||||
exit(!success);
|
||||
@ -179,15 +179,15 @@ main(int argc, char **argv)
|
||||
{
|
||||
puts("Welcome to psql, the PostgreSQL interactive terminal.\n\n"
|
||||
"Type: \\copyright for distribution terms\n"
|
||||
" \\h for help with SQL commands\n"
|
||||
" \\? for help on internal slash commands\n"
|
||||
" \\g or terminate with semicolon to execute query\n"
|
||||
" \\q to quit\n");
|
||||
" \\h for help with SQL commands\n"
|
||||
" \\? for help on internal slash commands\n"
|
||||
" \\g or terminate with semicolon to execute query\n"
|
||||
" \\q to quit\n");
|
||||
}
|
||||
|
||||
process_psqlrc(&settings);
|
||||
|
||||
initializeInput(options.no_readline ? 0 : 1);
|
||||
initializeInput(options.no_readline ? 0 : 1, &settings);
|
||||
|
||||
/* Now find something to do */
|
||||
|
||||
@ -270,7 +270,7 @@ parse_options(int argc, char *argv[], PsqlSettings *pset, struct adhoc_opts * op
|
||||
extern int optind;
|
||||
int c;
|
||||
|
||||
MemSet(options, 0, sizeof *options);
|
||||
memset(options, 0, sizeof *options);
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
while ((c = getopt_long(argc, argv, "Ac:d:eEf:F:lh:Hno:p:P:qsStT:uU:v:VWx?", long_options, &optindex)) != -1)
|
||||
@ -516,8 +516,8 @@ showVersion(PsqlSettings *pset)
|
||||
|
||||
/* get backend version */
|
||||
if (pset->db && PQstatus(pset->db) == CONNECTION_OK) {
|
||||
res = PSQLexec(pset, "SELECT version()");
|
||||
if (PQresultStatus(res) == PGRES_TUPLES_OK)
|
||||
res = PSQLexec(pset, "SELECT version()");
|
||||
if (PQresultStatus(res) == PGRES_TUPLES_OK)
|
||||
versionstr = PQgetvalue(res, 0, 0);
|
||||
}
|
||||
|
||||
@ -566,5 +566,5 @@ showVersion(PsqlSettings *pset)
|
||||
"distribution.");
|
||||
|
||||
if (res)
|
||||
PQclear(res);
|
||||
PQclear(res);
|
||||
}
|
||||
|
812
src/bin/psql/tab-complete.c
Normal file
812
src/bin/psql/tab-complete.c
Normal file
@ -0,0 +1,812 @@
|
||||
/*-----------
|
||||
This file implements a somewhat more sophisticated readline "TAB completion"
|
||||
in psql. It is not intended to be AI, to replace learning SQL, or to relieve
|
||||
you from thinking about what you're doing. Also it does not always give you
|
||||
all the syntactically legal completions, only those that are the most common
|
||||
or the ones that the programmer felt most like implementing.
|
||||
|
||||
CAVEAT: Tab completion causes queries to be sent to the backend. The number
|
||||
tuples returned gets limited, in most default installations to 101, but if
|
||||
you still don't like this prospect, you can turn off tab completion in your
|
||||
~/.inputrc (or else ${INPUTRC}) file so:
|
||||
$if psql
|
||||
TAB: self-insert
|
||||
$endif
|
||||
See `man 3 readline` or `info readline` for the full details. Also, hence the
|
||||
|
||||
BUGS:
|
||||
* If you split your queries across lines, this whole things gets confused.
|
||||
(To fix this, one would have to read psql's query buffer rather than
|
||||
readline's line buffer, which would require some major revisions of
|
||||
things.)
|
||||
* Table or attribute names with spaces in it will equally confuse it.
|
||||
* Quotes, parenthesis, and other funny characters are not handled all that
|
||||
gracefully.
|
||||
-------------*/
|
||||
|
||||
#include <config.h>
|
||||
#include <c.h>
|
||||
#include "tab-complete.h"
|
||||
|
||||
#include "input.h"
|
||||
|
||||
/* If we don't have this, we might as well forget about the whole thing: */
|
||||
#ifdef USE_READLINE
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h> /* toupper */
|
||||
#include <stdlib.h> /* malloc, free */
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
#include <libpq-fe.h>
|
||||
|
||||
#include "common.h"
|
||||
|
||||
|
||||
#define BUF_SIZE 2048
|
||||
#define ERROR_QUERY_TOO_LONG /* empty */
|
||||
|
||||
/* This pointer saves the place where psql stores its own pointer to the
|
||||
currently active database connection. This is probably a less than ideal way
|
||||
of passing this around, but this way I only had to make minimal changes to
|
||||
psql.c. */
|
||||
static PGconn ** database_connection;
|
||||
|
||||
|
||||
/* Forward declaration of functions */
|
||||
static char ** psql_completion(char *text, int start, int end);
|
||||
static char * create_command_generator(char *text, int state);
|
||||
static char * complete_from_query(char *text, int state);
|
||||
static char * complete_from_const(char *text, int state);
|
||||
static char * complete_from_list(char *text, int state);
|
||||
|
||||
static PGresult * exec_query(char * query);
|
||||
char * quote_file_name(char *text, int match_type, char * quote_pointer);
|
||||
//char * dequote_file_name(char *text, char quote_char);
|
||||
static char * previous_word(int point, int skip);
|
||||
|
||||
/* These variables are used to pass information into the completion functions.
|
||||
Realizing that this is the cardinal sin of programming, I don't see a better
|
||||
way. */
|
||||
char * completion_charp; /* if you need to pass a string */
|
||||
char ** completion_charpp; /* if you need to pass a list of strings */
|
||||
char * completion_info_charp; /* if you need to pass another string */
|
||||
|
||||
/* Store how many records from a database query we want to return at most
|
||||
(implemented via SELECT ... LIMIT xx). */
|
||||
static int completion_max_records;
|
||||
|
||||
|
||||
static void * xmalloc(size_t length)
|
||||
{
|
||||
void *tmp = malloc(length);
|
||||
if (!tmp) {
|
||||
perror("malloc");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
|
||||
/* Initialize the readline library for our purposes. */
|
||||
void initialize_readline(PGconn ** conn)
|
||||
{
|
||||
rl_readline_name = "psql";
|
||||
rl_attempted_completion_function = psql_completion;
|
||||
|
||||
rl_filename_quoting_function = quote_file_name;
|
||||
// rl_filename_dequoting_function = dequote_file_name;
|
||||
rl_filename_quote_characters = "qwertyuioplkjhgfdsazxcvbnm";
|
||||
|
||||
rl_special_prefixes = "()'";
|
||||
rl_basic_word_break_characters = "\t\n\"'`@$><=;|&{ ";
|
||||
|
||||
completion_max_records = rl_completion_query_items + 1;
|
||||
|
||||
database_connection = conn;
|
||||
}
|
||||
|
||||
|
||||
/* This is a list of all "things" in Pgsql, which can show up after CREATE or
|
||||
DROP; and there is also a query to get a list of them.
|
||||
The %s will be replaced by the text entered so far, the %d by it's length.
|
||||
If you change the order here or insert things, make sure to also adjust the
|
||||
referencing macros below.
|
||||
*/
|
||||
typedef struct {
|
||||
char * name;
|
||||
char * query;
|
||||
} pgsql_thing_t;
|
||||
|
||||
pgsql_thing_t words_after_create[] = {
|
||||
{ "AGGREGATE", "SELECT distinct aggname FROM pg_aggregate WHERE substr(aggname,1,%d)='%s'" },
|
||||
{ "DATABASE", "SELECT datname FROM pg_database WHERE substr(datname,1,%d)='%s'" },
|
||||
{ "FUNCTION", "SELECT distinct proname FROM pg_proc WHERE substr(proname,1,%d)='%s'" },
|
||||
{ "INDEX", "SELECT relname FROM pg_class WHERE relkind='i' and substr(relname,1,%d)='%s'" },
|
||||
{ "OPERATOR", NULL }, /* Querying for this is probably not such a good idea. */
|
||||
{ "RULE", "SELECT rulename FROM pg_rules WHERE substr(rulename,1,%d)='%s'" },
|
||||
{ "SEQUENCE", "SELECT relname FROM pg_class WHERE relkind='S' and substr(relname,1,%d)='%s'" },
|
||||
{ "TABLE", "SELECT relname FROM pg_class WHERE relkind='r' and substr(relname,1,%d)='%s'" },
|
||||
{ "TEMP", NULL }, /* for CREATE TEMP TABLE ... */
|
||||
{ "TRIGGER", "SELECT tgname FROM pg_trigger WHERE substr(tgname,1,%d)='%s'" },
|
||||
{ "TYPE", "SELECT typname FROM pg_type WHERE substr(typname,1,%d)='%s'" },
|
||||
{ "UNIQUE", NULL }, /* for CREATE UNIQUE INDEX ... */
|
||||
{ "USER", "SELECT usename FROM pg_user WHERE substr(usename,1,%d)='%s'" },
|
||||
{ "VIEW", NULL }, /* Telling a view from a table is not the easiest
|
||||
thing in the world, and the solutions I've seen
|
||||
don't really work, so I'll wait on this. */
|
||||
{ NULL, NULL } /* end of list */
|
||||
};
|
||||
|
||||
|
||||
/* The query to get a list of tables and a list of indexes, which are used at
|
||||
various places. */
|
||||
#define Query_for_list_of_tables words_after_create[7].query
|
||||
#define Query_for_list_of_indexes words_after_create[3].query
|
||||
#define Query_for_list_of_databases words_after_create[1].query
|
||||
#define Query_for_list_of_attributes "SELECT a.attname FROM pg_attribute a, pg_class c WHERE c.oid = a.attrelid and a.attnum>0 and substr(a.attname,1,%d)='%s' and c.relname='%s'"
|
||||
|
||||
|
||||
/* A couple of macros to ease typing. You can use these to complete the given
|
||||
string with
|
||||
1) The results from a query you pass it. (Perhaps one of those right above?)
|
||||
2) The items from a null-pointer-terminated list.
|
||||
3) A string constant
|
||||
4) The list of attributes to the given table.
|
||||
*/
|
||||
#define COMPLETE_WITH_QUERY(query) \
|
||||
do { completion_charp = query; matches = completion_matches(text, complete_from_query); } while(0)
|
||||
#define COMPLETE_WITH_LIST(list) \
|
||||
do { completion_charpp = list; matches = completion_matches(text, complete_from_list); } while(0)
|
||||
#define COMPLETE_WITH_CONST(string) \
|
||||
do { completion_charp = string; matches = completion_matches(text, complete_from_const); } while(0)
|
||||
#define COMPLETE_WITH_ATTR(table) \
|
||||
do {completion_charp = Query_for_list_of_attributes; completion_info_charp = table; matches = completion_matches(text, complete_from_query); } while(0)
|
||||
|
||||
|
||||
/* The completion function. Acc. to readline spec this gets passed the text
|
||||
entered to far and its start and end in the readline buffer. The return value
|
||||
is some partially obscure list format that can be generated by the readline
|
||||
libraries completion_matches() function, so we don't have to worry about it.
|
||||
*/
|
||||
char ** psql_completion(char *text, int start, int end)
|
||||
{
|
||||
/* This is the variable we'll return. */
|
||||
char **matches = NULL;
|
||||
/* These are going to contain some scannage of the input line. */
|
||||
char *prev_wd, *prev2_wd, *prev3_wd, *prev4_wd;
|
||||
|
||||
static char * sql_commands[] = {
|
||||
"ABORT", "ALTER", "BEGIN", "CLOSE", "CLUSTER", "COMMIT", "COPY",
|
||||
"CREATE", "DECLARE", "DELETE", "DROP", "EXPLAIN", "FETCH", "GRANT",
|
||||
"INSERT", "LISTEN", "LOAD", "LOCK", "MOVE", "NOTIFY", "RESET",
|
||||
"REVOKE", "ROLLBACK", "SELECT", "SET", "SHOW", "UNLISTEN", "UPDATE",
|
||||
"VACUUM", NULL
|
||||
};
|
||||
|
||||
static char * pgsql_variables[] = {
|
||||
"Client_Encoding", "Names", "DateStyle", "Server_Encoding", "TimeZone",
|
||||
"TRANSACTION", "Cost_Heap", "Cost_Index", "GEQO", "KSQO", "Query_Limit",
|
||||
NULL
|
||||
};
|
||||
|
||||
static char * backslash_commands[] = {
|
||||
"\\connect", "\\copy", "\\d", "\\di", "\\di", "\\ds", "\\dS", "\\dv",
|
||||
"\\da", "\\df", "\\do", "\\dt", "\\e", "\\echo", "\\g", "\\h", "\\i", "\\l",
|
||||
"\\lo_import", "\\lo_export", "\\lo_list", "\\lo_unlink",
|
||||
"\\o", "\\p", "\\pset", "\\q", "\\qecho", "\\r", "\\set", "\\t", "\\x",
|
||||
"\\w", "\\z", "\\!", NULL
|
||||
};
|
||||
|
||||
(void)end; /* not used */
|
||||
|
||||
rl_filename_quoting_desired = 1;
|
||||
|
||||
/* Clear a few things. */
|
||||
completion_charp = NULL;
|
||||
completion_charpp = NULL;
|
||||
completion_info_charp = NULL;
|
||||
|
||||
/* Scan the input line before our current position for the last four words.
|
||||
According to those we'll make some smart decisions on what the user is
|
||||
probably intending to type.
|
||||
TODO: Use strtokx() to do this.
|
||||
*/
|
||||
prev_wd = previous_word(start,0);
|
||||
prev2_wd = previous_word(start,1);
|
||||
prev3_wd = previous_word(start,2);
|
||||
prev4_wd = previous_word(start,3);
|
||||
|
||||
/* If a backslash command was started, continue */
|
||||
if (text[0]=='\\')
|
||||
COMPLETE_WITH_LIST(backslash_commands);
|
||||
|
||||
/* If no previous word, suggest one of the basic sql commands */
|
||||
else if (!prev_wd)
|
||||
COMPLETE_WITH_LIST(sql_commands);
|
||||
|
||||
/* CREATE or DROP */
|
||||
/* complete with something you can create or drop */
|
||||
else if( strcasecmp(prev_wd, "CREATE") == 0 || strcasecmp(prev_wd, "DROP") == 0 )
|
||||
matches = completion_matches(text, create_command_generator);
|
||||
|
||||
/* ALTER */
|
||||
/* complete with what you can alter (TABLE or USER) */
|
||||
else if( strcasecmp(prev_wd, "ALTER") == 0 ) {
|
||||
char * list_ALTER[] = { "TABLE", "USER", NULL };
|
||||
COMPLETE_WITH_LIST(list_ALTER);
|
||||
}
|
||||
/* If we detect ALTER TABLE <name>, suggest either "ADD" or "RENAME" */
|
||||
else if( strcasecmp(prev3_wd, "ALTER")==0 && strcasecmp(prev2_wd, "TABLE")==0 ) {
|
||||
char * list_ALTER2[] = { "ADD", "RENAME", NULL };
|
||||
COMPLETE_WITH_LIST(list_ALTER2);
|
||||
}
|
||||
/* If we have TABLE <sth> ADD|RENAME, provide list of columns */
|
||||
else if( strcasecmp(prev3_wd, "TABLE")==0 &&
|
||||
(strcasecmp(prev_wd,"ADD")==0 || strcasecmp(prev_wd,"RENAME")==0) )
|
||||
COMPLETE_WITH_ATTR(prev2_wd);
|
||||
|
||||
/* CLUSTER */
|
||||
/* If the previous word is CLUSTER, produce list of indexes. */
|
||||
else if( strcasecmp(prev_wd, "CLUSTER") == 0 )
|
||||
COMPLETE_WITH_QUERY(Query_for_list_of_indexes);
|
||||
/* If we have CLUSTER <sth>, then add "ON" */
|
||||
else if( strcasecmp(prev2_wd, "CLUSTER") == 0 )
|
||||
COMPLETE_WITH_CONST("ON");
|
||||
/* If we have CLUSTER <sth> ON, then add the correct tablename as well. */
|
||||
else if ( strcasecmp(prev3_wd, "CLUSTER")==0 && strcasecmp(prev_wd, "ON")==0 ) {
|
||||
char query_buffer[BUF_SIZE]; /* Some room to build queries. */
|
||||
if(snprintf(query_buffer, BUF_SIZE,
|
||||
"SELECT c1.relname FROM pg_class c1, pg_class c2, pg_index i WHERE c1.oid=i.indrelid and i.indexrelid=c2.oid and c2.relname='%s'",
|
||||
prev2_wd) == -1)
|
||||
ERROR_QUERY_TOO_LONG;
|
||||
else
|
||||
COMPLETE_WITH_QUERY(query_buffer);
|
||||
}
|
||||
|
||||
/* COPY */
|
||||
/* If we have COPY [BINARY] (which you'd have to type yourself), offer list of tables
|
||||
(Also cover the analogous backslash command) */
|
||||
else if( strcasecmp(prev_wd, "COPY")==0 ||
|
||||
strcasecmp(prev_wd, "\\copy")==0 ||
|
||||
(strcasecmp(prev2_wd,"COPY")==0 && strcasecmp(prev_wd,"BINARY")==0) )
|
||||
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
|
||||
/* If we have COPY|BINARY <sth>, complete it with "TO" or "FROM" */
|
||||
else if( strcasecmp(prev2_wd, "COPY")==0 ||
|
||||
strcasecmp(prev2_wd, "\\copy")==0 ||
|
||||
strcasecmp(prev2_wd, "BINARY")==0 ) {
|
||||
char * list_FROMTO[] = { "FROM", "TO", NULL };
|
||||
COMPLETE_WITH_LIST(list_FROMTO);
|
||||
}
|
||||
|
||||
/* CREATE INDEX */
|
||||
/* First off we complete CREATE UNIQUE with "INDEX" */
|
||||
else if( strcasecmp(prev2_wd, "CREATE")==0 && strcasecmp(prev_wd, "UNIQUE")==0 )
|
||||
COMPLETE_WITH_CONST("INDEX");
|
||||
/* If we have CREATE|UNIQUE INDEX <sth>, then add "ON" */
|
||||
else if( strcasecmp(prev2_wd, "INDEX") == 0 &&
|
||||
(strcasecmp(prev3_wd,"CREATE")==0 || strcasecmp(prev3_wd,"UNIQUE")==0) )
|
||||
COMPLETE_WITH_CONST("ON");
|
||||
/* Complete ... INDEX <name> ON with a list of tables */
|
||||
else if( (strcasecmp(prev3_wd, "INDEX")==0 && strcasecmp(prev_wd, "ON")==0) || (0) )
|
||||
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
|
||||
/* Complete INDEX <name> ON <table> with a list of table columns (which should really be in parens) */
|
||||
else if( (strcasecmp(prev4_wd, "INDEX")==0 && strcasecmp(prev2_wd, "ON")==0) )
|
||||
COMPLETE_WITH_ATTR(prev_wd);
|
||||
/* same if you put in USING */
|
||||
else if ((strcasecmp(prev4_wd,"ON")==0 && strcasecmp(prev2_wd,"USING")==0) )
|
||||
COMPLETE_WITH_ATTR(prev3_wd);
|
||||
/* Complete USING with an index method */
|
||||
else if( strcasecmp(prev_wd, "USING")==0 ) {
|
||||
char * index_mth[] = { "BTREE", "RTREE", "HASH", NULL };
|
||||
COMPLETE_WITH_LIST(index_mth);
|
||||
}
|
||||
|
||||
/* CREATE RULE */
|
||||
/* Complete "CREATE RULE <sth>" with "AS" */
|
||||
else if( strcasecmp(prev3_wd,"CREATE")==0 && strcasecmp(prev2_wd,"RULE")==0 )
|
||||
COMPLETE_WITH_CONST("AS");
|
||||
/* Complete "CREATE RULE <sth> AS with "ON" */
|
||||
else if( strcasecmp(prev4_wd,"CREATE")==0 &&
|
||||
strcasecmp(prev3_wd,"RULE")==0 &&
|
||||
strcasecmp(prev_wd,"AS")==0 )
|
||||
COMPLETE_WITH_CONST("ON");
|
||||
/* Complete "RULE * AS ON" with SELECT|UPDATE|DELETE|INSERT */
|
||||
else if( strcasecmp(prev4_wd,"RULE")==0 &&
|
||||
strcasecmp(prev2_wd,"AS")==0 &&
|
||||
strcasecmp(prev_wd,"ON")==0 ) {
|
||||
char * rule_events[] = { "SELECT", "UPDATE", "INSERT", "DELETE", NULL };
|
||||
COMPLETE_WITH_LIST(rule_events);
|
||||
}
|
||||
/* Complete "AS ON <sth with a 'T' :)>" with a "TO" */
|
||||
else if( strcasecmp(prev3_wd,"AS")==0 &&
|
||||
strcasecmp(prev2_wd,"ON")==0 &&
|
||||
(toupper(prev_wd[4])=='T' || toupper(prev_wd[5])=='T') )
|
||||
COMPLETE_WITH_CONST("TO");
|
||||
/* Complete "AS ON <sth> TO" with a table name */
|
||||
else if( strcasecmp(prev4_wd,"AS")==0 &&
|
||||
strcasecmp(prev3_wd,"ON")==0 &&
|
||||
strcasecmp(prev_wd,"TO")==0 )
|
||||
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
|
||||
|
||||
/* CREATE TABLE */
|
||||
/* Complete CREATE TEMP with "TABLE" */
|
||||
else if( strcasecmp(prev2_wd, "CREATE")==0 && strcasecmp(prev_wd, "TEMP")==0 )
|
||||
COMPLETE_WITH_CONST("TABLE");
|
||||
|
||||
/* CREATE TRIGGER */
|
||||
/* is on the agenda . . . */
|
||||
|
||||
/* CREATE VIEW */
|
||||
/* Complete "CREATE VIEW <name>" with "AS" */
|
||||
else if( strcasecmp(prev3_wd,"CREATE")==0 && strcasecmp(prev2_wd,"VIEW")==0 )
|
||||
COMPLETE_WITH_CONST("AS");
|
||||
/* Complete "CREATE VIEW <sth> AS with "SELECT" */
|
||||
else if( strcasecmp(prev4_wd,"CREATE")==0 &&
|
||||
strcasecmp(prev3_wd,"VIEW")==0 &&
|
||||
strcasecmp(prev_wd,"AS")==0 )
|
||||
COMPLETE_WITH_CONST("SELECT");
|
||||
|
||||
/* DELETE */
|
||||
/* Complete DELETE with FROM (only if the word before that is not "ON" (cf.
|
||||
rules) or "BEFORE" or "AFTER" (cf. triggers) ) */
|
||||
else if( strcasecmp(prev_wd,"DELETE")==0 &&
|
||||
!(strcasecmp(prev2_wd,"ON")==0 ||
|
||||
strcasecmp(prev2_wd,"BEFORE")==0 ||
|
||||
strcasecmp(prev2_wd,"AFTER")==0) )
|
||||
COMPLETE_WITH_CONST("FROM");
|
||||
/* Complete DELETE FROM with a list of tables */
|
||||
else if( strcasecmp(prev2_wd,"DELETE")==0 && strcasecmp(prev_wd,"FROM")==0 )
|
||||
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
|
||||
/* Complete DELETE FROM <table> with "WHERE" (perhaps a safe idea?) */
|
||||
else if( strcasecmp(prev3_wd,"DELETE")==0 && strcasecmp(prev2_wd,"FROM")==0 )
|
||||
COMPLETE_WITH_CONST("WHERE");
|
||||
|
||||
/* EXPLAIN */
|
||||
/* Complete EXPLAIN [VERBOSE] (which you'd have to type yourself) with the list of SQL commands */
|
||||
else if( strcasecmp(prev_wd,"EXPLAIN")==0 ||
|
||||
(strcasecmp(prev2_wd,"EXPLAIN")==0 && strcasecmp(prev_wd,"VERBOSE")==0) )
|
||||
COMPLETE_WITH_LIST(sql_commands);
|
||||
|
||||
/* FETCH && MOVE */
|
||||
/* Complete FETCH with one of FORWARD, BACKWARD, RELATIVE */
|
||||
else if( strcasecmp(prev_wd,"FETCH")==0 || strcasecmp(prev_wd,"MOVE")==0 ) {
|
||||
char * list_FETCH1[] = { "FORWARD", "BACKWARD", "RELATIVE", NULL };
|
||||
COMPLETE_WITH_LIST(list_FETCH1);
|
||||
}
|
||||
/* Complete FETCH <sth> with one of ALL, NEXT, PRIOR */
|
||||
else if( strcasecmp(prev2_wd,"FETCH")==0 || strcasecmp(prev2_wd,"MOVE")==0 ) {
|
||||
char * list_FETCH2[] = { "ALL", "NEXT", "PRIOR", NULL };
|
||||
COMPLETE_WITH_LIST(list_FETCH2);
|
||||
}
|
||||
/* Complete FETCH <sth1> <sth2> with "FROM" or "TO".
|
||||
(Is there a difference? If not, remove one.) */
|
||||
else if( strcasecmp(prev3_wd,"FETCH")==0 || strcasecmp(prev3_wd,"MOVE")==0 ) {
|
||||
char * list_FROMTO[] = { "FROM", "TO", NULL };
|
||||
COMPLETE_WITH_LIST(list_FROMTO);
|
||||
}
|
||||
|
||||
/* GRANT && REVOKE*/
|
||||
/* Complete GRANT/REVOKE with a list of privileges */
|
||||
else if( strcasecmp(prev_wd,"GRANT")==0 || strcasecmp(prev_wd,"REVOKE")==0 ) {
|
||||
char * list_privileg[] = { "SELECT", "INSERT", "UPDATE", "DELETE", "RULE", "ALL", NULL };
|
||||
COMPLETE_WITH_LIST(list_privileg);
|
||||
}
|
||||
/* Complete GRANT/REVOKE <sth> with "ON" */
|
||||
else if( strcasecmp(prev2_wd,"GRANT")==0 || strcasecmp(prev2_wd,"REVOKE")==0 )
|
||||
COMPLETE_WITH_CONST("ON");
|
||||
/* Complete GRANT/REVOKE <sth> ON with a list of tables, views, sequences, and indexes */
|
||||
else if( (strcasecmp(prev3_wd,"GRANT")==0 || strcasecmp(prev3_wd,"REVOKE")==0) &&
|
||||
strcasecmp(prev_wd,"ON")==0 )
|
||||
COMPLETE_WITH_QUERY("SELECT relname FROM pg_class WHERE relkind in ('r','i','s') and substr(relname,1,%d)='%s'");
|
||||
/* Complete "GRANT * ON * " with "TO" */
|
||||
else if( strcasecmp(prev4_wd,"GRANT")==0 && strcasecmp(prev2_wd,"ON")==0 )
|
||||
COMPLETE_WITH_CONST("TO");
|
||||
/* Complete "REVOKE * ON * " with "FROM" */
|
||||
else if( strcasecmp(prev4_wd,"REVOKE")==0 && strcasecmp(prev2_wd,"ON")==0 )
|
||||
COMPLETE_WITH_CONST("FROM");
|
||||
/* TODO: to complete with user name we need prev5_wd -- wait for a more general solution there */
|
||||
|
||||
/* INSERT */
|
||||
/* Complete INSERT with "INTO" */
|
||||
else if( strcasecmp(prev_wd,"INSERT")==0 )
|
||||
COMPLETE_WITH_CONST("INTO");
|
||||
/* Complete INSERT INTO with table names */
|
||||
else if( strcasecmp(prev2_wd, "INSERT")==0 && strcasecmp(prev_wd,"INTO")==0 )
|
||||
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
|
||||
/* Complete INSERT INTO <table> with "VALUES" or "SELECT" */
|
||||
else if( strcasecmp(prev3_wd, "INSERT")==0 && strcasecmp(prev2_wd,"INTO")==0 ) {
|
||||
char * list_INSERT[] = { "SELECT", "VALUES", NULL };
|
||||
COMPLETE_WITH_LIST(list_INSERT);
|
||||
}
|
||||
/* Insert an open parenthesis after "VALUES" */
|
||||
else if( strcasecmp(prev_wd,"VALUES")==0 )
|
||||
COMPLETE_WITH_CONST("(");
|
||||
|
||||
/* LOCK */
|
||||
/* Complete with list of tables */
|
||||
else if( strcasecmp(prev_wd, "LOCK") == 0 )
|
||||
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
|
||||
/* (If you want more with LOCK, you better think about it yourself.) */
|
||||
|
||||
/* SELECT */
|
||||
/* naah . . . */
|
||||
|
||||
/* SET, RESET, SHOW */
|
||||
/* Complete with a variable name */
|
||||
else if( (strcasecmp(prev_wd,"SET")==0 && strcasecmp(prev3_wd,"UPDATE")!=0) ||
|
||||
strcasecmp(prev_wd,"RESET")==0 ||
|
||||
strcasecmp(prev_wd,"SHOW")==0 )
|
||||
COMPLETE_WITH_LIST(pgsql_variables);
|
||||
/* Complete "SET TRANSACTION ISOLOLATION LEVEL" */
|
||||
else if( strcasecmp(prev2_wd,"SET")==0 && strcasecmp(prev_wd,"TRANSACTION")==0 )
|
||||
COMPLETE_WITH_CONST("ISOLATION");
|
||||
else if( strcasecmp(prev3_wd,"SET")==0 &&
|
||||
strcasecmp(prev2_wd,"TRANSACTION")==0 &&
|
||||
strcasecmp(prev_wd,"ISOLATION")==0 )
|
||||
COMPLETE_WITH_CONST("LEVEL");
|
||||
else if( strcasecmp(prev4_wd,"SET")==0 &&
|
||||
strcasecmp(prev3_wd,"TRANSACTION")==0 &&
|
||||
strcasecmp(prev2_wd,"ISOLATION")==0 &&
|
||||
strcasecmp(prev_wd,"LEVEL")==0 ) {
|
||||
char * my_list[] = {"READ","SERIALIZED",NULL};
|
||||
COMPLETE_WITH_LIST(my_list);
|
||||
}
|
||||
else if( strcasecmp(prev4_wd,"TRANSACTION")==0 &&
|
||||
strcasecmp(prev3_wd,"ISOLATION")==0 &&
|
||||
strcasecmp(prev2_wd,"LEVEL")==0 &&
|
||||
strcasecmp(prev_wd,"READ")==0 )
|
||||
COMPLETE_WITH_CONST("COMMITTED");
|
||||
/* Complete SET <var> with "TO" */
|
||||
else if( strcasecmp(prev2_wd,"SET")==0 &&
|
||||
strcasecmp(prev4_wd,"UPDATE")!=0 )
|
||||
COMPLETE_WITH_CONST("TO");
|
||||
/* Suggest possible variable values */
|
||||
else if( strcasecmp(prev3_wd,"SET")==0 &&
|
||||
(strcasecmp(prev_wd,"TO")==0 || strcmp(prev_wd,"=")==0) ) {
|
||||
if ( strcasecmp(prev2_wd,"DateStyle")==0 ) {
|
||||
char * my_list[] = {"'ISO'", "'SQL'", "'Postgres'", "'European'", "'NonEuropean'", "'German'", "DEFAULT", NULL};
|
||||
COMPLETE_WITH_LIST(my_list);
|
||||
}
|
||||
else if( strcasecmp(prev2_wd,"GEQO")==0 || strcasecmp(prev2_wd,"KSQO")==0 ) {
|
||||
char * my_list[] = {"ON", "OFF", "DEFAULT", NULL};
|
||||
COMPLETE_WITH_LIST(my_list);
|
||||
}
|
||||
else {
|
||||
char * my_list[] = {"DEFAULT", NULL};
|
||||
COMPLETE_WITH_LIST(my_list);
|
||||
}
|
||||
}
|
||||
|
||||
/* UPDATE */
|
||||
/* If prev. word is UPDATE suggest a list of tables */
|
||||
else if( strcasecmp(prev_wd, "UPDATE") == 0 )
|
||||
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
|
||||
/* Complete UPDATE <table> with "SET" */
|
||||
else if( strcasecmp(prev2_wd, "UPDATE") == 0 )
|
||||
COMPLETE_WITH_CONST("SET");
|
||||
/* If the previous word is SET (and it wasn't caught above as the _first_
|
||||
word) the word before it was (hopefully) a table name and we'll now make
|
||||
a list of attributes. */
|
||||
else if( strcasecmp(prev_wd, "SET") == 0 )
|
||||
COMPLETE_WITH_ATTR(prev2_wd);
|
||||
|
||||
/* VACUUM */
|
||||
else if( strcasecmp(prev_wd, "VACUUM") == 0 )
|
||||
COMPLETE_WITH_QUERY("SELECT relname FROM pg_class WHERE relkind='r' and substr(relname,1,%d)='%s' UNION SELECT 'ANALYZE'::text");
|
||||
else if( strcasecmp(prev2_wd, "VACUUM")==0 && strcasecmp(prev_wd, "ANALYZE")==0 )
|
||||
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
|
||||
|
||||
|
||||
/* Backslash commands */
|
||||
else if (strcmp(prev_wd, "\\connect")==0 || strcmp(prev_wd, "\\c")==0)
|
||||
COMPLETE_WITH_QUERY(Query_for_list_of_databases);
|
||||
else if (strcmp(prev_wd, "\\d")==0)
|
||||
COMPLETE_WITH_QUERY(Query_for_list_of_tables);
|
||||
else if (strcmp(prev_wd, "\\h")==0 || strcmp(prev_wd, "\\help")==0)
|
||||
COMPLETE_WITH_LIST(sql_commands);
|
||||
else if (strcmp(prev_wd, "\\pset")==0) {
|
||||
char * my_list[] = { "format", "border", "expanded", "null", "fieldsep",
|
||||
"tuples_only", "title", "tableattr", "pager" };
|
||||
COMPLETE_WITH_LIST(my_list);
|
||||
}
|
||||
else if( strcmp(prev_wd, "\\e")==0 || strcmp(prev_wd, "\\edit")==0 ||
|
||||
strcmp(prev_wd, "\\g")==0 ||
|
||||
strcmp(prev_wd, "\\i")==0 || strcmp(prev_wd, "\\include")==0 ||
|
||||
strcmp(prev_wd, "\\o")==0 || strcmp(prev_wd, "\\out")==0 ||
|
||||
strcmp(prev_wd, "\\s")==0 ||
|
||||
strcmp(prev_wd, "\\w")==0 || strcmp(prev_wd, "\\write")==0
|
||||
) {
|
||||
matches = completion_matches(text, filename_completion_function);
|
||||
}
|
||||
|
||||
|
||||
/* Finally, we look through the list of "things", such as TABLE, INDEX and
|
||||
check if that was the previous word. If so, execute the query to get a
|
||||
list of them. */
|
||||
else {
|
||||
int i;
|
||||
for(i=0; words_after_create[i].name; i++)
|
||||
if ( strcasecmp(prev_wd, words_after_create[i].name) == 0 ) {
|
||||
COMPLETE_WITH_QUERY(words_after_create[i].query);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* If we still don't have anything to match we have to fabricate some sort
|
||||
of default list. If we were to just return NULL, readline automatically
|
||||
attempts filename completion, and that's usually no good. */
|
||||
if (matches == NULL) {
|
||||
char * my_list[] = { "", "", NULL };
|
||||
COMPLETE_WITH_LIST(my_list);
|
||||
}
|
||||
|
||||
|
||||
/* free storage */
|
||||
free(prev_wd);
|
||||
free(prev2_wd);
|
||||
free(prev3_wd);
|
||||
free(prev4_wd);
|
||||
|
||||
/* Return our Grand List O' Matches */
|
||||
return matches;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* GENERATOR FUNCTIONS
|
||||
|
||||
These functions do all the actual work of completing the input. They get
|
||||
passed the text so far and the count how many times they have been called so
|
||||
far with the same text.
|
||||
If you read the above carefully, you'll see that these don't get called
|
||||
directly but through the readline interface.
|
||||
The return value is expected to be the full completion of the text, going
|
||||
through a list each time, or NULL if there are no more matches. The string
|
||||
will be free()'d be readline, so you must run it through strdup() or
|
||||
something of that sort.
|
||||
*/
|
||||
|
||||
/* This one gives you one from a list of things you can put after CREATE or DROP
|
||||
as defined above.
|
||||
*/
|
||||
char * create_command_generator(char *text, int state)
|
||||
{
|
||||
static int list_index, string_length;
|
||||
char *name;
|
||||
|
||||
/* If this is the first time for this completion, init some values */
|
||||
if (state == 0) {
|
||||
list_index = 0;
|
||||
string_length = strlen(text);
|
||||
}
|
||||
|
||||
/* find something that matches */
|
||||
while ( (name = words_after_create[list_index++].name) )
|
||||
if ( strncasecmp(name, text, string_length) == 0 )
|
||||
return xstrdup(name);
|
||||
|
||||
/* if nothing matches, return NULL */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* This creates a list of matching things, according to a query pointed to
|
||||
by completion_charp. The query needs to have a %d and a %s in it, which will
|
||||
be replaced by the string length of the text and the text itself. See some
|
||||
example queries at the top.
|
||||
The query may also have another %s in it, which will be replaced by the value
|
||||
of completion_info_charp.
|
||||
Ordinarily this would be used to get a list of matching tables or functions,
|
||||
etc.
|
||||
*/
|
||||
char * complete_from_query(char *text, int state)
|
||||
{
|
||||
static int list_index, string_length;
|
||||
static PGresult *result = NULL;
|
||||
char query_buffer[BUF_SIZE];
|
||||
const char * item;
|
||||
|
||||
/* If this ist the first time for this completion, we fetch a list of our
|
||||
"things" from the backend. */
|
||||
if (state == 0) {
|
||||
list_index = 0;
|
||||
string_length = strlen(text);
|
||||
|
||||
/* Need to have a query */
|
||||
if (completion_charp == NULL) return NULL;
|
||||
|
||||
if (snprintf(query_buffer, BUF_SIZE, completion_charp, string_length, text, completion_info_charp) == -1) {
|
||||
ERROR_QUERY_TOO_LONG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = exec_query(query_buffer);
|
||||
}
|
||||
|
||||
/* Find something that matches */
|
||||
if( result && PQresultStatus(result) == PGRES_TUPLES_OK )
|
||||
while( list_index < PQntuples(result) && (item = PQgetvalue(result, list_index++, 0)) )
|
||||
if ( strncasecmp(text, item, string_length) == 0)
|
||||
return xstrdup(item);
|
||||
|
||||
/* If nothing matches, free the db structure and return null */
|
||||
PQclear(result);
|
||||
result = NULL;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* This function returns in order one of a fixed, NULL pointer terminated list
|
||||
of strings (if matching). This can be used if there are only a fixed number
|
||||
SQL words that can appear at certain spot.
|
||||
*/
|
||||
char * complete_from_list(char *text, int state) {
|
||||
static int string_length, list_index;
|
||||
char * item;
|
||||
|
||||
/* need to have a list */
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
assert(completion_charpp);
|
||||
#endif
|
||||
|
||||
/* Initialization */
|
||||
if (state == 0) {
|
||||
list_index = 0;
|
||||
string_length = strlen(text);
|
||||
}
|
||||
|
||||
while( (item = completion_charpp[list_index++]) )
|
||||
if ( strncasecmp(text,item,string_length) == 0 )
|
||||
return xstrdup(item);
|
||||
|
||||
/* If no more matches, return null. */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/* This function returns one fixed string the first time even if it doesn't
|
||||
match what's there, and nothing the second time. This should be used if there
|
||||
is only one possibility that can appear at a certain spot, so misspellings
|
||||
will be overwritten.
|
||||
The string to be passed must be in completion_charp.
|
||||
*/
|
||||
char * complete_from_const(char *text, int state)
|
||||
{
|
||||
(void)text; /* We don't care about what was entered already. */
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
assert(completion_charp);
|
||||
#endif
|
||||
if (state==0)
|
||||
return xstrdup(completion_charp);
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* HELPER FUNCTIONS */
|
||||
|
||||
|
||||
/* Execute a query and report any errors. This should be the preferred way of
|
||||
talking to the database in this file.
|
||||
Note that the query passed in here must not have a semicolon at the end
|
||||
because we need to append LIMIT xxx.
|
||||
*/
|
||||
PGresult * exec_query(char * query)
|
||||
{
|
||||
PGresult * result;
|
||||
char query_buffer[BUF_SIZE];
|
||||
|
||||
if (query == NULL || PQstatus(*database_connection) != CONNECTION_OK)
|
||||
return NULL;
|
||||
#ifdef USE_ASSERT_CHECKING
|
||||
assert( query[strlen(query)-1] != ';' );
|
||||
#endif
|
||||
|
||||
if( snprintf(query_buffer, BUF_SIZE, "%s LIMIT %d;", query, completion_max_records) == -1 ) {
|
||||
ERROR_QUERY_TOO_LONG;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
result = PQexec(*database_connection, query);
|
||||
|
||||
if (result != NULL && PQresultStatus(result) != PGRES_TUPLES_OK) {
|
||||
fprintf(stderr, "\nThe completion query \"%s\" failed thus: %s\n",
|
||||
query, PQresStatus(PQresultStatus(result)));
|
||||
PQclear(result);
|
||||
result = NULL;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Return the word (space delimited) before point. Set skip > 0 to skip that
|
||||
many words; e.g. skip=1 finds the word before the previous one.
|
||||
TODO: Take account of quotes. (Right now, if you table names contain spaces
|
||||
you're screwed.)
|
||||
*/
|
||||
char * previous_word(int point, int skip) {
|
||||
int i, start=0, end=-1;
|
||||
char * s;
|
||||
|
||||
while (skip-- >=0) {
|
||||
/* first we look for a space before the current word */
|
||||
for(i=point; i>=0; i--)
|
||||
if (rl_line_buffer[i] == ' ')
|
||||
break;
|
||||
|
||||
/* now find the first non-space which then constitutes the end */
|
||||
for(; i>=0; i--)
|
||||
if (rl_line_buffer[i] != ' ') {
|
||||
end = i;
|
||||
break;
|
||||
}
|
||||
|
||||
/* If no end found we return null, because there is no word before the point */
|
||||
if (end == -1)
|
||||
return NULL;
|
||||
|
||||
/* Otherwise we now look for the start. The start is either the last
|
||||
character before any space going backwards from the end, or it's
|
||||
simply character 0 */
|
||||
for (start=end; start>0; start--)
|
||||
if (rl_line_buffer[start-1] == ' ')
|
||||
break;
|
||||
|
||||
point=start;
|
||||
}
|
||||
|
||||
/* make a copy */
|
||||
s = (char *)xmalloc(end-start+2);
|
||||
strncpy(s, &rl_line_buffer[start], end-start+1);
|
||||
s[end-start+1] = '\0';
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
/* Surround a string with single quotes. This works for both SQL and
|
||||
psql internal. Doesn't work so well yet.
|
||||
*/
|
||||
char * quote_file_name(char *text, int match_type, char * quote_pointer)
|
||||
{
|
||||
char *s;
|
||||
size_t length;
|
||||
|
||||
(void)quote_pointer; /* not used */
|
||||
|
||||
length = strlen(text) + ( match_type==SINGLE_MATCH ? 3 : 2 );
|
||||
s = xmalloc(length);
|
||||
s[0] = '\'';
|
||||
strcpy(s+1, text);
|
||||
if (match_type==SINGLE_MATCH)
|
||||
s[length-2] = '\'';
|
||||
s[length-1] = '\0';
|
||||
return s;
|
||||
}
|
||||
|
||||
|
||||
char * dequote_file_name(char *text, char quote_char)
|
||||
{
|
||||
char *s;
|
||||
size_t length;
|
||||
|
||||
if (!quote_char)
|
||||
return xstrdup(text);
|
||||
|
||||
length = strlen(text);
|
||||
s = xmalloc(length-2+1);
|
||||
strncpy(s, text+1, length-2);
|
||||
s[length] = '\0';
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif /* USE_READLINE */
|
8
src/bin/psql/tab-complete.h
Normal file
8
src/bin/psql/tab-complete.h
Normal file
@ -0,0 +1,8 @@
|
||||
#ifndef TAB_COMPLETE_H
|
||||
#define TAB_COMPLETE_H
|
||||
|
||||
#include <libpq-fe.h>
|
||||
|
||||
void initialize_readline(PGconn ** conn);
|
||||
|
||||
#endif
|
@ -11,7 +11,8 @@
|
||||
#define VARIABLES_H
|
||||
#include <c.h>
|
||||
|
||||
#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
|
||||
#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz"\
|
||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789_"
|
||||
|
||||
struct _variable
|
||||
{
|
||||
|
@ -1,72 +0,0 @@
|
||||
# Makefile for Microsoft Visual C++ 5.0 (or compat)
|
||||
|
||||
!IF "$(OS)" == "Windows_NT"
|
||||
NULL=
|
||||
!ELSE
|
||||
NULL=nul
|
||||
!ENDIF
|
||||
|
||||
CPP=cl.exe
|
||||
|
||||
OUTDIR=.\Release
|
||||
INTDIR=.\Release
|
||||
# Begin Custom Macros
|
||||
OutDir=.\Release
|
||||
# End Custom Macros
|
||||
|
||||
ALL : "$(OUTDIR)\psql.exe"
|
||||
|
||||
CLEAN :
|
||||
-@erase "$(INTDIR)\psql.obj"
|
||||
-@erase "$(INTDIR)\stringutils.obj"
|
||||
-@erase "$(INTDIR)\getopt.obj"
|
||||
-@erase "$(INTDIR)\vc50.idb"
|
||||
-@erase "$(OUTDIR)\psql.exe"
|
||||
|
||||
"$(OUTDIR)" :
|
||||
if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"
|
||||
|
||||
CPP_PROJ=/nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D\
|
||||
"_MBCS" /Fp"$(INTDIR)\psql.pch" /YX /Fo"$(INTDIR)\\" /Fd"$(INTDIR)\\" /FD /c \
|
||||
/I ..\..\include /I ..\..\interfaces\libpq /D "HAVE_STRDUP" /D "BLCKSZ=8192"
|
||||
|
||||
!IFDEF MULTIBYTE
|
||||
!IFNDEF MBFLAGS
|
||||
MBFLAGS="-DMULTIBYTE=$(MULTIBYTE)"
|
||||
!ENDIF
|
||||
CPP_PROJ=$(MBFLAGS) $(CPP_PROJ)
|
||||
!ENDIF
|
||||
|
||||
CPP_OBJS=.\Release/
|
||||
CPP_SBRS=.
|
||||
|
||||
LINK32=link.exe
|
||||
LINK32_FLAGS=kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib\
|
||||
advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib\
|
||||
odbccp32.lib wsock32.lib /nologo /subsystem:console /incremental:no\
|
||||
/pdb:"$(OUTDIR)\psql.pdb" /machine:I386 /out:"$(OUTDIR)\psql.exe"
|
||||
LINK32_OBJS= \
|
||||
"$(INTDIR)\psql.obj" \
|
||||
"$(INTDIR)\stringutils.obj" \
|
||||
"$(INTDIR)\getopt.obj" \
|
||||
"..\..\interfaces\libpq\Release\libpqdll.lib"
|
||||
|
||||
"$(OUTDIR)\psql.exe" : "$(OUTDIR)" $(DEF_FILE) $(LINK32_OBJS)
|
||||
$(LINK32) @<<
|
||||
$(LINK32_FLAGS) $(LINK32_OBJS)
|
||||
<<
|
||||
|
||||
"$(OUTDIR)\getopt.obj" : "$(OUTDIR)" ..\..\utils\getopt.c
|
||||
$(CPP) @<<
|
||||
$(CPP_PROJ) ..\..\utils\getopt.c
|
||||
<<
|
||||
|
||||
.c{$(CPP_OBJS)}.obj::
|
||||
$(CPP) @<<
|
||||
$(CPP_PROJ) $<
|
||||
<<
|
||||
|
||||
.cpp{$(CPP_OBJS)}.obj::
|
||||
$(CPP) @<<
|
||||
$(CPP_PROJ) $<
|
||||
<<
|
Loading…
x
Reference in New Issue
Block a user