mirror of
https://github.com/postgres/postgres.git
synced 2025-04-22 23:02:54 +03:00
* 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
|
Postgres documentation
|
||||||
-->
|
-->
|
||||||
|
|
||||||
@ -90,6 +90,11 @@ Postgres documentation
|
|||||||
to be run at the start of every session.
|
to be run at the start of every session.
|
||||||
</para>
|
</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">
|
<refsect2 id="R2-APP-PSQL-3">
|
||||||
<refsect2info>
|
<refsect2info>
|
||||||
<date>1998-09-26</date>
|
<date>1998-09-26</date>
|
||||||
@ -104,7 +109,7 @@ Postgres documentation
|
|||||||
<application>libpq</application> client library, upon which
|
<application>libpq</application> client library, upon which
|
||||||
<application>psql</application> is built, will choose defaults.
|
<application>psql</application> is built, will choose defaults.
|
||||||
(This will usually mean the environment variables <envar>PGDATABASE</envar>,
|
(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
|
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,
|
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
|
the default user is the system user name, and the default database is
|
||||||
@ -156,11 +161,6 @@ testdb=>
|
|||||||
<xref linkend="SQL-LISTEN" endterm="SQL-LISTEN-title"> and
|
<xref linkend="SQL-LISTEN" endterm="SQL-LISTEN-title"> and
|
||||||
<xref linkend="SQL-NOTIFY" endterm="SQL-NOTIFY-title">.
|
<xref linkend="SQL-NOTIFY" endterm="SQL-NOTIFY-title">.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
|
||||||
<application>psql</application> can be used in a pipe sequence, and
|
|
||||||
automatically detects when it is not used interactively.
|
|
||||||
</para>
|
|
||||||
</refsect2>
|
</refsect2>
|
||||||
</refsect1>
|
</refsect1>
|
||||||
|
|
||||||
@ -343,17 +343,17 @@ testdb=>
|
|||||||
|
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<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),
|
(which could be a table, view, index, or sequence),
|
||||||
their types, and any special attributes such as <literal>NOT NULL</literal>
|
their types, and any special attributes such as <literal>NOT NULL</literal>
|
||||||
or defaults, if any.
|
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>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
If the relation is, in fact, a table, any defined indices are also listed.
|
The command form <literal>\d?</literal> is identical, but any comments
|
||||||
If the relation is a view, the view definition is also shown.
|
associated with the table columns are shown as well.
|
||||||
If the variable <envar>description</envar> is set, any comments associated
|
|
||||||
with a table columns are shown as well.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<note>
|
<note>
|
||||||
@ -375,8 +375,10 @@ testdb=>
|
|||||||
Lists all available aggregate functions, together with the data type they operate on.
|
Lists all available aggregate functions, together with the data type they operate on.
|
||||||
If <replaceable class="parameter">pattern</replaceable>
|
If <replaceable class="parameter">pattern</replaceable>
|
||||||
(a regular expression) is specified, only matching aggregates are shown.
|
(a regular expression) is specified, only matching aggregates are shown.
|
||||||
If the variable <envar>description</envar> is set, comments are listed for
|
If the alternative command form <literal>\da?</literal> is used,
|
||||||
each function as well.
|
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>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -423,8 +425,9 @@ testdb=>
|
|||||||
Lists available functions, together with their argument and return types.
|
Lists available functions, together with their argument and return types.
|
||||||
If <replaceable class="parameter">pattern</replaceable>
|
If <replaceable class="parameter">pattern</replaceable>
|
||||||
(a regular expression) is specified, only matching functions are shown.
|
(a regular expression) is specified, only matching functions are shown.
|
||||||
If the variable <envar>description</envar> is set, comments are listed for
|
If the form <literal>\df+</literal> is used, additional information about
|
||||||
each function as well.
|
each function is shown. Comments for each function can be shown with
|
||||||
|
the <literal>\df?</literal> form.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -444,7 +447,7 @@ testdb=>
|
|||||||
<para>
|
<para>
|
||||||
If <replaceable class="parameter">pattern</replaceable> is specified,
|
If <replaceable class="parameter">pattern</replaceable> is specified,
|
||||||
it is a regular expression restricts the listing to those objects
|
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.
|
each object is listed with its associated description, if any.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
@ -462,18 +465,19 @@ testdb=>
|
|||||||
|
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>\do [ <replaceable class="parameter">name</replaceable> ]</literal></term>
|
<term><literal>\do [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Lists available operators with their operand and return types.
|
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.
|
is specified, only operators with that name will be shown.
|
||||||
(Note that, unlike with similar commands, this is not a regular expression
|
(Since this is a regular expression, be sure to quote all special
|
||||||
because operator names were likely to interfere with regular expression
|
characters in you operator name with backslashes. To prevent
|
||||||
meta-characters.)
|
interpretation of the backslash as a new command, you might also
|
||||||
|
wish to quote the argument.)
|
||||||
</para>
|
</para>
|
||||||
<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.
|
each operator.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
@ -495,9 +499,9 @@ testdb=>
|
|||||||
<term><literal>\dT [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
|
<term><literal>\dT [ <replaceable class="parameter">pattern</replaceable> ]</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
List all data types or only those that match <replaceable class="parameter">pattern</replaceable>.
|
Lists 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
|
The command forms <literal>\dT+</literal> and <literal>\dT?</literal> show extra information
|
||||||
its associated description.
|
and the associated descriptions of the types, respectively.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -519,8 +523,8 @@ testdb=>
|
|||||||
The new query buffer is then re-parsed according to the normal rules of
|
The new query buffer is then re-parsed according to the normal rules of
|
||||||
<application>psql</application>, where the whole buffer is treated as
|
<application>psql</application>, where the whole buffer is treated as
|
||||||
a single line. (Thus you cannot make <quote>scripts</quote> this way,
|
a single line. (Thus you cannot make <quote>scripts</quote> this way,
|
||||||
use <command>\i</command> for that.) In particular, this means that
|
use <command>\i</command> for that.) This means also that
|
||||||
if the query ends (or rather contains) a semicolon, it is immediately
|
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.
|
executed. In other cases it will merely wait in the query buffer.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
@ -579,7 +583,7 @@ Tue Oct 26 21:40:57 CEST 1999
|
|||||||
Sends the current query input buffer to the backend and optionally
|
Sends the current query input buffer to the backend and optionally
|
||||||
saves the output in <replaceable class="parameter">filename</replaceable>
|
saves the output in <replaceable class="parameter">filename</replaceable>
|
||||||
or pipes the output into a separate Unix shell to execute
|
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 virtually equivalent to a semicolon. A <literal>\g</literal> with argument
|
||||||
is a <quote>one-shot</quote> alternative to the <command>\o</command> command.
|
is a <quote>one-shot</quote> alternative to the <command>\o</command> command.
|
||||||
</para>
|
</para>
|
||||||
@ -591,8 +595,7 @@ Tue Oct 26 21:40:57 CEST 1999
|
|||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Give syntax help on the specified <acronym>SQL</acronym> command.
|
Give syntax help on the specified <acronym>SQL</acronym> command.
|
||||||
If <replaceable class="parameter">command</replaceable> is not a defined <acronym>SQL</acronym> command
|
If <replaceable class="parameter">command</replaceable> is not specified,
|
||||||
or if <replaceable class="parameter">command</replaceable> is not specified,
|
|
||||||
then <application>psql</application> will
|
then <application>psql</application> will
|
||||||
list all the commands for which syntax help is
|
list all the commands for which syntax help is
|
||||||
available. If <replaceable class="parameter">command</replaceable>
|
available. If <replaceable class="parameter">command</replaceable>
|
||||||
@ -628,7 +631,7 @@ Tue Oct 26 21:40:57 CEST 1999
|
|||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Reads input from the file <replaceable class="parameter">filename</replaceable>
|
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>
|
</para>
|
||||||
<note>
|
<note>
|
||||||
<para>
|
<para>
|
||||||
@ -644,9 +647,9 @@ Tue Oct 26 21:40:57 CEST 1999
|
|||||||
<term><literal>\l</literal> (or <literal>\list</literal>)</term>
|
<term><literal>\l</literal> (or <literal>\list</literal>)</term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
List all the databases in the server as well as their owners. If the
|
List all the databases in the server as well as their owners. Append a
|
||||||
variable <envar>description</envar> is set, any descriptions for
|
<quote>?</quote> (question mark) to the command name to see any descriptions
|
||||||
the databases are shown as well. If your <productname>PostgreSQL</productname>
|
for the databases as well. If your <productname>PostgreSQL</productname>
|
||||||
installation was
|
installation was
|
||||||
compiled with multibyte encoding support, the encoding scheme of each
|
compiled with multibyte encoding support, the encoding scheme of each
|
||||||
database is shown as well.
|
database is shown as well.
|
||||||
@ -693,10 +696,10 @@ foo=> <userinput>\lo_import '/home/me/pictures/photo.xcf' 'a picture of me'</use
|
|||||||
lo_import 152801
|
lo_import 152801
|
||||||
</programlisting>
|
</programlisting>
|
||||||
The response indicates that the large object received object id 152801
|
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
|
For that reason it is recommended to always associate a human-readable
|
||||||
comment with every object. Those can then be seen with the
|
comment with every object. Those can then be seen with the
|
||||||
<command>\lo_list</command> command.
|
<command>\lo_list?</command> command.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -720,8 +723,8 @@ lo_import 152801
|
|||||||
<para>
|
<para>
|
||||||
Shows a list of all <productname>PostgreSQL</productname> <quote>large
|
Shows a list of all <productname>PostgreSQL</productname> <quote>large
|
||||||
objects</quote> currently stored in the database along with their owners.
|
objects</quote> currently stored in the database along with their owners.
|
||||||
If the variable <envar>description</envar> is set, the associated
|
Append a question mark to the command name (<literal>\lo_list?</literal>) to
|
||||||
comments are shown as well.
|
see the the associated comments as well.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -863,7 +866,7 @@ lo_import 152801
|
|||||||
<para>
|
<para>
|
||||||
The second argument is a string that should be printed whenever a field
|
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
|
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>.
|
<literal>\pset null "(null)"</literal>.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
@ -1015,7 +1018,7 @@ lo_import 152801
|
|||||||
|
|
||||||
<note>
|
<note>
|
||||||
<para>
|
<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
|
necessary, in fact, to save the command history as that will be done
|
||||||
automatically on program termination. The history is then
|
automatically on program termination. The history is then
|
||||||
also automatically loaded every time <application>psql</application>
|
also automatically loaded every time <application>psql</application>
|
||||||
@ -1034,17 +1037,14 @@ lo_import 152801
|
|||||||
Sets the internal variable <replaceable class="parameter">name</replaceable>
|
Sets the internal variable <replaceable class="parameter">name</replaceable>
|
||||||
to <replaceable class="parameter">value</replaceable>. If no second argument
|
to <replaceable class="parameter">value</replaceable>. If no second argument
|
||||||
is given, the variable is unset (which is different from setting it to,
|
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
|
arguments are given, all currently defined variables are listed with their
|
||||||
values.
|
values.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
Valid variable names can contain lower-case characters, digits, and
|
Valid variable names can contain characters, digits, and underscores.
|
||||||
underscores. In particular, no upper-case characters are allowed, as
|
See the section about <application>psql</application> variables for details.
|
||||||
those are reserved for certain <quote>magic</quote> variables and
|
|
||||||
environment variables. See the section about <application>psql</application>
|
|
||||||
variables for details.
|
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -1170,7 +1170,10 @@ Access permissions for database "test"
|
|||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
Escapes to a separate Unix shell or executes the Unix command
|
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>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -1290,7 +1293,7 @@ Access permissions for database "test"
|
|||||||
<para>
|
<para>
|
||||||
Use the file <replaceable class="parameter">filename</replaceable>
|
Use the file <replaceable class="parameter">filename</replaceable>
|
||||||
as the source of queries instead of reading queries interactively.
|
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>.
|
This in many ways equivalent to the internal command <command>\i</command>.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
@ -1553,7 +1556,7 @@ readline, history, locale, assert checks
|
|||||||
</para>
|
</para>
|
||||||
|
|
||||||
<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.
|
password prompt whenever the backend requests password authentication.
|
||||||
Because this is currently based on a <quote>hack</quote> the automatic
|
Because this is currently based on a <quote>hack</quote> the automatic
|
||||||
recognition might mysteriously fail, hence this option to force a prompt.
|
recognition might mysteriously fail, hence this option to force a prompt.
|
||||||
@ -1606,7 +1609,7 @@ readline, history, locale, assert checks
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
<application>psql</application> provides variable substitution features
|
<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,
|
pairs, where the value can be any string of any length. To set variables,
|
||||||
use the <application>psql</application> meta-command <command>\set</command>:
|
use the <application>psql</application> meta-command <command>\set</command>:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
@ -1627,16 +1630,17 @@ foo is now bar.
|
|||||||
</programlisting>
|
</programlisting>
|
||||||
(The curly braces are required. This is not <productname>Perl</productname>.) No variable substitution
|
(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
|
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>
|
</para>
|
||||||
|
|
||||||
<note>
|
<note>
|
||||||
<para>
|
<para>
|
||||||
The arguments of <command>\set</command> are subject to the same substitution
|
The arguments of <command>\set</command> are subject to the same substitution
|
||||||
rules as with other commands. Thus you can construct interesting references
|
rules as with other commands. Thus you can construct interesting references
|
||||||
such as <literal>\set "${foo}bar" 'something'</literal> and get <quote>variable
|
such as <literal>\set "${foo}bar" 'something'</literal> and get <quote>soft
|
||||||
variables</quote> of <productname>Perl</productname> or <productname><acronym>PHP</acronym></productname>
|
links</quote> or <quote>variable variables</quote> of <productname>Perl</productname>
|
||||||
fame. Unfortunately (or fortunately?), there is not way to do anything useful
|
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
|
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
|
other hand, <literal>\set bar $foo</literal> is a perfectly valid way to copy
|
||||||
a variable.
|
a variable.
|
||||||
@ -1645,14 +1649,21 @@ foo is now bar.
|
|||||||
|
|
||||||
<para>
|
<para>
|
||||||
<application>psql</application>'s internal variable names can consist of
|
<application>psql</application>'s internal variable names can consist of
|
||||||
lower-case letters, numbers, and underscores in any order and any number of
|
letters, numbers, and underscores in any order and any number of them.
|
||||||
them. Upper-case letters are not allowed. (There is a reason for that. Keep reading.)
|
It is recommended, however, that you stick to lower-case letters and do not
|
||||||
If you attempt to refer to a variable that does not consist of those
|
begin with a digit. The partial rationale for this follows.
|
||||||
characters <application>psql</application> first checks if it is the name of
|
</para>
|
||||||
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
|
<para>
|
||||||
upper-case letter. Finally, if no match is found that way, the value of
|
If you attempt to refer to a variable that is not set,
|
||||||
the respective environment variable is substituted.
|
<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>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
@ -1660,7 +1671,10 @@ foo is now bar.
|
|||||||
<envar>Version</envar> which contains a string with the version of
|
<envar>Version</envar> which contains a string with the version of
|
||||||
<application>psql</application>; <envar>Database</envar>, <envar>Host</envar>,
|
<application>psql</application>; <envar>Database</envar>, <envar>Host</envar>,
|
||||||
<envar>Port</envar>, <envar>User</envar> are the currently active
|
<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>
|
||||||
|
|
||||||
<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
|
only care whether or not are they set, not what to. A list of all specially
|
||||||
treated variables follows.
|
treated variables follows.
|
||||||
<variablelist>
|
<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>
|
<varlistentry>
|
||||||
<term><envar>die_on_error</envar></term>
|
<term><envar>die_on_error</envar></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
@ -1731,25 +1732,29 @@ foo is now bar.
|
|||||||
<term><envar>lo_transaction</envar></term>
|
<term><envar>lo_transaction</envar></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
If you use the <productname>PostgreSQL</productname> large object interface to store
|
If you use the <productname>PostgreSQL</productname> large object
|
||||||
data that does not fit into one tuple specially all the operations must be contained
|
interface to specially store data that does not fit into one tuple,
|
||||||
in a transaction block. (See the documentation of the large object interface for
|
all the operations must be contained in a transaction block. (See the
|
||||||
more information.) Since <application>psql</application> has no way to keep track if
|
documentation of the large object interface for more information.) Since
|
||||||
you already have a transaction in progress when you call one of its internal commands
|
<application>psql</application> has no way to keep track if you already
|
||||||
<command>\lo_export</command>, <command>\lo_import</command>, <command>\lo_unlink</command>
|
have a transaction in progress when you call one of its internal
|
||||||
it must take some arbitrary action. This action could either be to roll back any transaction
|
commands <command>\lo_export</command>, <command>\lo_import</command>,
|
||||||
that might already be in progress, or to commit any such transaction, or to do nothing
|
<command>\lo_unlink</command> it must take some arbitrary action. This
|
||||||
at all. In the latter case you must provide you own <command>BEGIN</command>/<command>END</command>
|
action could either be to roll back any transaction that might already
|
||||||
block or the results are unpredictable (usually resulting in the desired action not being
|
be in progress, or to commit any such transaction, or to do nothing at
|
||||||
performed anyway).
|
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>
|
||||||
|
|
||||||
<para>
|
<para>
|
||||||
To choose what you want to do you set this variable to one of
|
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
|
<quote>rollback</quote>, <quote>commit</quote>, or <quote>nothing</quote>.
|
||||||
to roll back the transaction. If you just want to load one or a few objects this is fine.
|
The default is to roll back the transaction. If you just want to load one
|
||||||
However, if you intend to transfer many large objects, it might be advisable to
|
or a few objects this is fine. However, if you intend to transfer many
|
||||||
provide one explicit transaction block around all commands.
|
large objects, it might be advisable to provide one explicit transaction
|
||||||
|
block around all commands.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
@ -1794,16 +1799,6 @@ foo is now bar.
|
|||||||
</para>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
</varlistentry>
|
</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>
|
</variablelist>
|
||||||
|
|
||||||
</para>
|
</para>
|
||||||
@ -1817,44 +1812,33 @@ foo is now bar.
|
|||||||
<para>
|
<para>
|
||||||
An additional useful feature of <application>psql</application> variables
|
An additional useful feature of <application>psql</application> variables
|
||||||
is that you can substitute (<quote>interpolate</quote>) them into
|
is that you can substitute (<quote>interpolate</quote>) them into
|
||||||
regular <acronym>SQL</acronym> statements. In order not to break existing
|
regular <acronym>SQL</acronym> statements. The syntax for this is to prepend
|
||||||
<acronym>SQL</acronym> statements, you must choose your own special
|
the variable name with a colon (<literal>:</literal>).
|
||||||
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>
|
<programlisting>
|
||||||
testdb=> <userinput>\set foo 'my_table'</userinput>
|
testdb=> <userinput>\set foo 'my_table'</userinput>
|
||||||
testdb=> <userinput>SELECT * FROM #foo#;</userinput>
|
testdb=> <userinput>SELECT * FROM :foo;</userinput>
|
||||||
</programlisting>
|
</programlisting>
|
||||||
would then query the table <literal>my_table</literal>. The value of the
|
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
|
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.
|
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>
|
||||||
|
|
||||||
<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.
|
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>\set content `cat my_file.txt`</userinput>
|
||||||
testdb=> <userinput>INSERT INTO my_table VALUES ('#content#');</userinput>
|
testdb=> <userinput>\set content "'${content}'"</userinput>
|
||||||
|
testdb=> <userinput>INSERT INTO my_table VALUES (:content);</userinput>
|
||||||
</programlisting>
|
</programlisting>
|
||||||
One possible problem with this approach is that <filename>my_file.txt</filename>
|
One possible problem with this approach is that <filename>my_file.txt</filename>
|
||||||
might contain single quotes. These need to be escaped so that
|
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>:
|
could be done with the program <application>sed</application>:
|
||||||
<programlisting>
|
<programlisting>
|
||||||
testdb=> <userinput>\set content `sed -e "s/'/\\\\\\'/g" < my_file.txt`</userinput>
|
testdb=> <userinput>\set content `sed -e "s/'/\\\\\\'/g" < my_file.txt`</userinput>
|
||||||
@ -2138,7 +2122,8 @@ Field separator is "oo".
|
|||||||
and attempting to get along with each other. Sometimes they do, sometimes
|
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
|
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>.
|
<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>
|
</para>
|
||||||
</listitem>
|
</listitem>
|
||||||
|
|
||||||
@ -2151,6 +2136,15 @@ Field separator is "oo".
|
|||||||
these limits sooner rather than later.
|
these limits sooner rather than later.
|
||||||
</para>
|
</para>
|
||||||
</listitem>
|
</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>
|
</itemizedlist>
|
||||||
|
|
||||||
</refsect2>
|
</refsect2>
|
||||||
@ -2169,9 +2163,9 @@ Field separator is "oo".
|
|||||||
<para>
|
<para>
|
||||||
The present version is the result of a major clean-up and re-write in 1999 by
|
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.
|
<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
|
Many people had again contributed their ideas. A bunch of features were stolen
|
||||||
to recognize the influence of <application>tcsh</application> at a number
|
from various shells (in case you hadn't noticed), in particular
|
||||||
of places.
|
<application>tcsh</application>.
|
||||||
</para>
|
</para>
|
||||||
</refsect2>
|
</refsect2>
|
||||||
|
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
#
|
#
|
||||||
#
|
#
|
||||||
# IDENTIFICATION
|
# 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 \
|
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 \
|
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
|
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
|
psql: $(OBJS) $(LIBPQDIR)/libpq.a
|
||||||
$(CC) -o psql -L$(LIBPQDIR) $(OBJS) -lpq $(LDFLAGS)
|
$(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
|
# sql_help.h is gone, for it needs the docs in the right place to be
|
||||||
# regenerated. -- (pe)
|
# regenerated. -- (pe)
|
||||||
|
|
||||||
distclean: clean
|
maintainer-clean: clean
|
||||||
rm -f sql_help.h
|
rm -f sql_help.h
|
||||||
|
|
||||||
ifeq (depend,$(wildcard depend))
|
ifeq (depend,$(wildcard depend))
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
/* functions for use in this file only */
|
/* functions for use in this file */
|
||||||
|
|
||||||
static backslashResult exec_command(const char *cmd,
|
static backslashResult exec_command(const char *cmd,
|
||||||
char *const * options,
|
char *const * options,
|
||||||
@ -41,16 +41,24 @@ static backslashResult exec_command(const char *cmd,
|
|||||||
PQExpBuffer query_buf,
|
PQExpBuffer query_buf,
|
||||||
PsqlSettings *pset);
|
PsqlSettings *pset);
|
||||||
|
|
||||||
static bool
|
static bool do_edit(const char *filename_arg, PQExpBuffer query_buf);
|
||||||
do_edit(const char *filename_arg, PQExpBuffer query_buf);
|
|
||||||
|
|
||||||
static char *
|
static char * unescape(const char *source, PsqlSettings *pset);
|
||||||
unescape(const char *source, PsqlSettings *pset);
|
|
||||||
|
|
||||||
static bool
|
static bool do_connect(const char *new_dbname,
|
||||||
do_shell(const char *command);
|
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:
|
* HandleSlashCmds:
|
||||||
@ -78,7 +86,7 @@ HandleSlashCmds(PsqlSettings *pset,
|
|||||||
{
|
{
|
||||||
backslashResult status = CMD_SKIP_LINE;
|
backslashResult status = CMD_SKIP_LINE;
|
||||||
char *my_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;
|
char *token;
|
||||||
const char *options_string = NULL;
|
const char *options_string = NULL;
|
||||||
const char *cmd;
|
const char *cmd;
|
||||||
@ -120,7 +128,7 @@ HandleSlashCmds(PsqlSettings *pset,
|
|||||||
i = 0;
|
i = 0;
|
||||||
token = strtokx(options_string, " \t", "\"'`", '\\', "e, &pos);
|
token = strtokx(options_string, " \t", "\"'`", '\\', "e, &pos);
|
||||||
|
|
||||||
for (i = 0; token && i < 16; i++)
|
for (i = 0; token && i < NR_OPTIONS; i++)
|
||||||
{
|
{
|
||||||
switch (quote)
|
switch (quote)
|
||||||
{
|
{
|
||||||
@ -194,14 +202,15 @@ HandleSlashCmds(PsqlSettings *pset,
|
|||||||
options[i] = xstrdup(interpolate_var(token + 1, pset));
|
options[i] = xstrdup(interpolate_var(token + 1, pset));
|
||||||
else
|
else
|
||||||
options[i] = xstrdup(token);
|
options[i] = xstrdup(token);
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (continue_parse)
|
if (continue_parse)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
token = strtokx(NULL, " \t", "\"'`", '\\', "e, &pos);
|
token = strtokx(NULL, " \t", "\"'`", '\\', "e, &pos);
|
||||||
}
|
} /* for */
|
||||||
|
|
||||||
|
options[i] = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd = my_line;
|
cmd = my_line;
|
||||||
@ -216,11 +225,11 @@ HandleSlashCmds(PsqlSettings *pset,
|
|||||||
* arguments to start immediately after the command, but that is
|
* arguments to start immediately after the command, but that is
|
||||||
* no longer encouraged.
|
* no longer encouraged.
|
||||||
*/
|
*/
|
||||||
const char *new_options[17];
|
const char *new_options[NR_OPTIONS+1];
|
||||||
char new_cmd[2];
|
char new_cmd[2];
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 1; i < 17; i++)
|
for (i = 1; i < NR_OPTIONS+1; i++)
|
||||||
new_options[i] = options[i - 1];
|
new_options[i] = options[i - 1];
|
||||||
new_options[0] = cmd + 1;
|
new_options[0] = cmd + 1;
|
||||||
|
|
||||||
@ -249,7 +258,7 @@ HandleSlashCmds(PsqlSettings *pset,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* clean up */
|
/* clean up */
|
||||||
for (i = 0; i < 16 && options[i]; i++)
|
for (i = 0; i < NR_OPTIONS && options[i]; i++)
|
||||||
free(options[i]);
|
free(options[i]);
|
||||||
|
|
||||||
free(my_line);
|
free(my_line);
|
||||||
@ -274,10 +283,7 @@ exec_command(const char *cmd,
|
|||||||
backslashResult status = CMD_SKIP_LINE;
|
backslashResult status = CMD_SKIP_LINE;
|
||||||
|
|
||||||
|
|
||||||
/*
|
/* \a -- toggle field alignment This makes little sense but we keep it around. */
|
||||||
* \a -- toggle field alignment This is deprecated and makes no sense,
|
|
||||||
* but we keep it around.
|
|
||||||
*/
|
|
||||||
if (strcmp(cmd, "a") == 0)
|
if (strcmp(cmd, "a") == 0)
|
||||||
{
|
{
|
||||||
if (pset->popt.topt.format != PRINT_ALIGNED)
|
if (pset->popt.topt.format != PRINT_ALIGNED)
|
||||||
@ -287,22 +293,19 @@ exec_command(const char *cmd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/* \C -- override table title (formerly change HTML caption) */
|
||||||
* \C -- override table title (formerly change HTML caption) This is
|
|
||||||
* deprecated.
|
|
||||||
*/
|
|
||||||
else if (strcmp(cmd, "C") == 0)
|
else if (strcmp(cmd, "C") == 0)
|
||||||
success = do_pset("title", options[0], &pset->popt, quiet);
|
success = do_pset("title", options[0], &pset->popt, quiet);
|
||||||
|
|
||||||
|
|
||||||
|
/*----------
|
||||||
/*
|
|
||||||
* \c or \connect -- connect to new database or as different user
|
* \c or \connect -- connect to new database or as different user
|
||||||
*
|
*
|
||||||
* \c foo bar : connect to db "foo" as user "bar" \c foo [-] :
|
* \c foo bar: connect to db "foo" as user "bar"
|
||||||
* connect to db "foo" as current user \c - bar : connect to
|
* \c foo [-]: connect to db "foo" as current user
|
||||||
* current db as user "bar" \c : connect to default db as
|
* \c - bar: connect to current db as user "bar"
|
||||||
* default user
|
* \c: connect to default db as default user
|
||||||
|
*----------
|
||||||
*/
|
*/
|
||||||
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
|
else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0)
|
||||||
{
|
{
|
||||||
@ -335,35 +338,39 @@ exec_command(const char *cmd,
|
|||||||
/* \d* commands */
|
/* \d* commands */
|
||||||
else if (cmd[0] == 'd')
|
else if (cmd[0] == 'd')
|
||||||
{
|
{
|
||||||
|
bool show_verbose = strchr(cmd, '+') ? true : false;
|
||||||
|
bool show_desc = strchr(cmd, '?') ? true : false;
|
||||||
|
|
||||||
switch (cmd[1])
|
switch (cmd[1])
|
||||||
{
|
{
|
||||||
case '\0':
|
case '\0':
|
||||||
|
case '?':
|
||||||
if (options[0])
|
if (options[0])
|
||||||
success = describeTableDetails(options[0], pset);
|
success = describeTableDetails(options[0], pset, show_desc);
|
||||||
else
|
else
|
||||||
success = listTables("tvs", NULL, pset); /* standard listing of
|
/* standard listing of interesting things */
|
||||||
* interesting things */
|
success = listTables("tvs", NULL, pset, show_desc);
|
||||||
break;
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
success = describeAggregates(options[0], pset);
|
success = describeAggregates(options[0], pset, show_verbose, show_desc);
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
success = objectDescription(options[0], pset);
|
success = objectDescription(options[0], pset);
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
success = describeFunctions(options[0], pset);
|
success = describeFunctions(options[0], pset, show_verbose, show_desc);
|
||||||
break;
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
success = do_lo_list(pset);
|
success = do_lo_list(pset, show_desc);
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
success = describeOperators(options[0], pset);
|
success = describeOperators(options[0], pset, show_verbose, show_desc);
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
success = permissionsList(options[0], pset);
|
success = permissionsList(options[0], pset);
|
||||||
break;
|
break;
|
||||||
case 'T':
|
case 'T':
|
||||||
success = describeTypes(options[0], pset);
|
success = describeTypes(options[0], pset, show_verbose, show_desc);
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
case 'v':
|
case 'v':
|
||||||
@ -371,9 +378,9 @@ exec_command(const char *cmd,
|
|||||||
case 's':
|
case 's':
|
||||||
case 'S':
|
case 'S':
|
||||||
if (cmd[1] == 'S' && cmd[2] == '\0')
|
if (cmd[1] == 'S' && cmd[2] == '\0')
|
||||||
success = listTables("Stvs", NULL, pset);
|
success = listTables("Stvs", NULL, pset, show_desc);
|
||||||
else
|
else
|
||||||
success = listTables(&cmd[1], options[0], pset);
|
success = listTables(&cmd[1], options[0], pset, show_desc);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
status = CMD_UNKNOWN;
|
status = CMD_UNKNOWN;
|
||||||
@ -399,14 +406,10 @@ exec_command(const char *cmd,
|
|||||||
fputs("\n", stdout);
|
fputs("\n", stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/* \f -- change field separator */
|
||||||
* \f -- change field separator (This is deprecated in favour of
|
|
||||||
* \pset.)
|
|
||||||
*/
|
|
||||||
else if (strcmp(cmd, "f") == 0)
|
else if (strcmp(cmd, "f") == 0)
|
||||||
success = do_pset("fieldsep", options[0], &pset->popt, quiet);
|
success = do_pset("fieldsep", options[0], &pset->popt, quiet);
|
||||||
|
|
||||||
|
|
||||||
/* \g means send query */
|
/* \g means send query */
|
||||||
else if (strcmp(cmd, "g") == 0)
|
else if (strcmp(cmd, "g") == 0)
|
||||||
{
|
{
|
||||||
@ -419,12 +422,27 @@ exec_command(const char *cmd,
|
|||||||
|
|
||||||
/* help */
|
/* help */
|
||||||
else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0)
|
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 */
|
/* HTML mode */
|
||||||
else if (strcmp(cmd, "H") == 0 || strcmp(cmd, "html") == 0)
|
else if (strcmp(cmd, "H") == 0 || strcmp(cmd, "html") == 0)
|
||||||
|
{
|
||||||
|
if (pset->popt.topt.format != PRINT_HTML)
|
||||||
success = do_pset("format", "html", &pset->popt, quiet);
|
success = do_pset("format", "html", &pset->popt, quiet);
|
||||||
|
else
|
||||||
|
success = do_pset("format", "aligned", &pset->popt, quiet);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* \i is include file */
|
/* \i is include file */
|
||||||
@ -439,9 +457,12 @@ exec_command(const char *cmd,
|
|||||||
success = process_file(options[0], pset);
|
success = process_file(options[0], pset);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* \l is list databases */
|
/* \l is list databases */
|
||||||
else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0)
|
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 */
|
/* large object things */
|
||||||
@ -470,7 +491,9 @@ exec_command(const char *cmd,
|
|||||||
}
|
}
|
||||||
|
|
||||||
else if (strcmp(cmd + 3, "list") == 0)
|
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)
|
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
|
* 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.
|
* 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)
|
do_connect(const char *new_dbname, const char *new_user, PsqlSettings *pset)
|
||||||
{
|
{
|
||||||
PGconn *oldconn = pset->db;
|
PGconn *oldconn = pset->db;
|
||||||
|
@ -29,10 +29,6 @@ backslashResult HandleSlashCmds(PsqlSettings *pset,
|
|||||||
PQExpBuffer query_buf,
|
PQExpBuffer query_buf,
|
||||||
const char **end_of_cmd);
|
const char **end_of_cmd);
|
||||||
|
|
||||||
bool do_connect(const char *new_dbname,
|
|
||||||
const char *new_user,
|
|
||||||
PsqlSettings *pset);
|
|
||||||
|
|
||||||
bool process_file(const char *filename,
|
bool process_file(const char *filename,
|
||||||
PsqlSettings *pset);
|
PsqlSettings *pset);
|
||||||
|
|
||||||
|
@ -3,11 +3,11 @@
|
|||||||
#include "common.h"
|
#include "common.h"
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
#ifdef HAVE_TERMIOS_H
|
#ifdef HAVE_TERMIOS_H
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#endif
|
#endif
|
||||||
#include <stdio.h>
|
|
||||||
#include <string.h>
|
|
||||||
#ifndef HAVE_STRDUP
|
#ifndef HAVE_STRDUP
|
||||||
#include <strdup.h>
|
#include <strdup.h>
|
||||||
#endif
|
#endif
|
||||||
@ -15,9 +15,12 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
#include <unistd.h> /* for write() */
|
#include <unistd.h> /* for write() */
|
||||||
|
#else
|
||||||
|
#include <io.h> /* for _write() */
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <libpq-fe.h>
|
#include <libpq-fe.h>
|
||||||
|
#include <postgres_ext.h>
|
||||||
#include <pqsignal.h>
|
#include <pqsignal.h>
|
||||||
#include <version.h>
|
#include <version.h>
|
||||||
|
|
||||||
@ -30,6 +33,7 @@
|
|||||||
#ifdef WIN32
|
#ifdef WIN32
|
||||||
#define popen(x,y) _popen(x,y)
|
#define popen(x,y) _popen(x,y)
|
||||||
#define pclose(x) _pclose(x)
|
#define pclose(x) _pclose(x)
|
||||||
|
#define write(a,b,c) _write(a,b,c)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@ -210,11 +214,13 @@ simple_prompt(const char *prompt, int maxlen, bool echo)
|
|||||||
/*
|
/*
|
||||||
* interpolate_var()
|
* interpolate_var()
|
||||||
*
|
*
|
||||||
* If the variable is a regular psql variable, just return its value.
|
* The idea here is that certain variables have a "magic" meaning, such as
|
||||||
* If it's a magic variable, return that value.
|
* 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
|
* This function only returns NULL if you feed in NULL's (don't do that).
|
||||||
* immediate consumption.
|
* Otherwise, the return value is ready for immediate consumption.
|
||||||
*/
|
*/
|
||||||
const char *
|
const char *
|
||||||
interpolate_var(const char *name, PsqlSettings *pset)
|
interpolate_var(const char *name, PsqlSettings *pset)
|
||||||
@ -229,14 +235,9 @@ interpolate_var(const char *name, PsqlSettings *pset)
|
|||||||
return NULL;
|
return NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (strspn(name, VALID_VARIABLE_CHARS) == strlen(name))
|
|
||||||
{
|
|
||||||
var = GetVariable(pset->vars, name);
|
var = GetVariable(pset->vars, name);
|
||||||
if (var)
|
if (var)
|
||||||
return var;
|
return var;
|
||||||
else
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
/* otherwise return magic variable */
|
/* otherwise return magic variable */
|
||||||
|
|
||||||
@ -279,11 +280,16 @@ interpolate_var(const char *name, PsqlSettings *pset)
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
if (strcmp(name, "LastOid") == 0)
|
||||||
* env vars (if env vars are all caps there should be no prob,
|
{
|
||||||
* otherwise you're on your own
|
static char buf[24];
|
||||||
*/
|
if (pset->lastOid == InvalidOid)
|
||||||
|
return "";
|
||||||
|
sprintf(buf, "%u", pset->lastOid);
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* env vars */
|
||||||
if ((var = getenv(name)))
|
if ((var = getenv(name)))
|
||||||
return var;
|
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
|
* Before we start a query, we enable a SIGINT signal catcher that sends a
|
||||||
* query that sends a cancel request to the backend.
|
* cancel request to the backend. Note that sending the cancel directly from
|
||||||
* Note that sending the cancel directly from the signal handler is safe
|
* the signal handler is safe because PQrequestCancel() is written to make it
|
||||||
* only because PQrequestCancel is carefully written to make it so. We
|
* so. We have to be very careful what else we do in the signal handler. This
|
||||||
* have to be very careful what else we do in the signal handler.
|
* includes using write() for output.
|
||||||
*
|
|
||||||
* 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().
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
PGconn *cancelConn;
|
static 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
|
|
||||||
|
|
||||||
|
#define write_stderr(String) write(fileno(stderr), String, strlen(String))
|
||||||
|
|
||||||
static void
|
static void
|
||||||
handle_sigint(SIGNAL_ARGS)
|
handle_sigint(SIGNAL_ARGS)
|
||||||
{
|
{
|
||||||
/* accept signal if no connection */
|
|
||||||
if (cancelConn == NULL)
|
if (cancelConn == NULL)
|
||||||
exit(1);
|
return;
|
||||||
/* Try to send cancel request */
|
/* Try to send cancel request */
|
||||||
if (PQrequestCancel(cancelConn))
|
if (PQrequestCancel(cancelConn))
|
||||||
safe_write_stderr("\nCANCEL request sent\n");
|
write_stderr("\nCancel request sent\n");
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
safe_write_stderr("\nCould not send cancel request: ");
|
write_stderr("\nCould not send cancel request: ");
|
||||||
safe_write_stderr(PQerrorMessage(cancelConn));
|
write_stderr(PQerrorMessage(cancelConn));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,7 +343,7 @@ PSQLexec(PsqlSettings *pset, const char *query)
|
|||||||
|
|
||||||
if (!pset->db)
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -367,7 +362,7 @@ PSQLexec(PsqlSettings *pset, const char *query)
|
|||||||
|
|
||||||
res = PQexec(pset->db, 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)
|
if (PQstatus(pset->db) == CONNECTION_BAD)
|
||||||
{
|
{
|
||||||
@ -393,7 +388,7 @@ PSQLexec(PsqlSettings *pset, const char *query)
|
|||||||
return res;
|
return res;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fprintf(stderr, "%s", PQerrorMessage(pset->db));
|
fputs(PQerrorMessage(pset->db), pset->queryFout);
|
||||||
PQclear(res);
|
PQclear(res);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@ -422,7 +417,7 @@ SendQuery(PsqlSettings *pset, const char *query)
|
|||||||
|
|
||||||
if (!pset->db)
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,8 +425,8 @@ SendQuery(PsqlSettings *pset, const char *query)
|
|||||||
{
|
{
|
||||||
char buf[3];
|
char buf[3];
|
||||||
|
|
||||||
fprintf(stdout, "***(Single step mode: Verify query)*********************************************\n"
|
printf("***(Single step mode: Verify query)*********************************************\n"
|
||||||
"QUERY: %s\n"
|
"%s\n"
|
||||||
"***(press return to proceed or enter x and return to cancel)********************\n",
|
"***(press return to proceed or enter x and return to cancel)********************\n",
|
||||||
query);
|
query);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
@ -492,11 +487,15 @@ SendQuery(PsqlSettings *pset, const char *query)
|
|||||||
break;
|
break;
|
||||||
case PGRES_COMMAND_OK:
|
case PGRES_COMMAND_OK:
|
||||||
success = true;
|
success = true;
|
||||||
|
pset->lastOid = PQoidValue(results);
|
||||||
|
if (!GetVariableBool(pset->vars, "quiet")) {
|
||||||
fprintf(pset->queryFout, "%s\n", PQcmdStatus(results));
|
fprintf(pset->queryFout, "%s\n", PQcmdStatus(results));
|
||||||
|
fflush(pset->queryFout);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case PGRES_COPY_OUT:
|
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:");
|
puts("Copy command returns:");
|
||||||
|
|
||||||
success = handleCopyOut(pset->db, pset->queryFout);
|
success = handleCopyOut(pset->db, pset->queryFout);
|
||||||
@ -516,6 +515,7 @@ SendQuery(PsqlSettings *pset, const char *query)
|
|||||||
case PGRES_BAD_RESPONSE:
|
case PGRES_BAD_RESPONSE:
|
||||||
success = false;
|
success = false;
|
||||||
fputs(PQerrorMessage(pset->db), pset->queryFout);
|
fputs(PQerrorMessage(pset->db), pset->queryFout);
|
||||||
|
fflush(pset->queryFout);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,42 +1,34 @@
|
|||||||
#ifndef DESCRIBE_H
|
#ifndef DESCRIBE_H
|
||||||
#define DESCRIBE_H
|
#define DESCRIBE_H
|
||||||
|
|
||||||
|
#include <c.h>
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
/* \da */
|
/* \da */
|
||||||
bool
|
bool describeAggregates(const char *name, PsqlSettings *pset, bool verbose, bool desc);
|
||||||
describeAggregates(const char *name, PsqlSettings *pset);
|
|
||||||
|
|
||||||
/* \df */
|
/* \df */
|
||||||
bool
|
bool describeFunctions(const char *name, PsqlSettings *pset, bool verbose, bool desc);
|
||||||
describeFunctions(const char *name, PsqlSettings *pset);
|
|
||||||
|
|
||||||
/* \dT */
|
/* \dT */
|
||||||
bool
|
bool describeTypes(const char *name, PsqlSettings *pset, bool verbose, bool desc);
|
||||||
describeTypes(const char *name, PsqlSettings *pset);
|
|
||||||
|
|
||||||
/* \do */
|
/* \do */
|
||||||
bool
|
bool describeOperators(const char *name, PsqlSettings *pset, bool verbose, bool desc);
|
||||||
describeOperators(const char *name, PsqlSettings *pset);
|
|
||||||
|
|
||||||
/* \dp (formerly \z) */
|
/* \z (or \dp) */
|
||||||
bool
|
bool permissionsList(const char *name, PsqlSettings *pset);
|
||||||
permissionsList(const char *name, PsqlSettings *pset);
|
|
||||||
|
|
||||||
/* \dd */
|
/* \dd */
|
||||||
bool
|
bool objectDescription(const char *object, PsqlSettings *pset);
|
||||||
objectDescription(const char *object, PsqlSettings *pset);
|
|
||||||
|
|
||||||
/* \d foo */
|
/* \d foo */
|
||||||
bool
|
bool describeTableDetails(const char *name, PsqlSettings *pset, bool desc);
|
||||||
describeTableDetails(const char *name, PsqlSettings *pset);
|
|
||||||
|
|
||||||
/* \l */
|
/* \l */
|
||||||
bool
|
bool listAllDbs(PsqlSettings *pset, bool desc);
|
||||||
listAllDbs(PsqlSettings *pset);
|
|
||||||
|
|
||||||
/* \dt, \di, \dS, etc. */
|
/* \dt, \di, \ds, \dS, etc. */
|
||||||
bool
|
bool listTables(const char *infotype, const char *name, PsqlSettings *pset, bool desc);
|
||||||
listTables(const char *infotype, const char *name, PsqlSettings *pset);
|
|
||||||
|
|
||||||
#endif /* DESCRIBE_H */
|
#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, " \\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, " \\copy [binary] <table> [with oids] {from|to} <fname>[using delimiters '<char>']\n");
|
||||||
fprintf(fout, " \\copyright -- show PostgreSQL copyright\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, " \\d{i|s|t|v|S}-- list only indices/sequences/tables/views/system tables\n");
|
||||||
fprintf(fout, " \\da -- list aggregates\n");
|
fprintf(fout, " \\da -- list aggregates\n");
|
||||||
fprintf(fout, " \\dd [object] -- list comment for table, type, function, or operator\n");
|
fprintf(fout, " \\dd [object] -- list comment for table, type, function, or operator\n");
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
#include <pqexpbuffer.h>
|
#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 */
|
/* Runtime options for turning off readline and history */
|
||||||
/* (of course there is no runtime command for doing that :) */
|
/* (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.
|
* The only "flag" right now is 1 for use readline & history.
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
initializeInput(int flags)
|
initializeInput(int flags, PsqlSettings *pset)
|
||||||
{
|
{
|
||||||
#ifdef USE_READLINE
|
#ifdef USE_READLINE
|
||||||
if (flags == 1)
|
if (flags == 1)
|
||||||
{
|
{
|
||||||
useReadline = true;
|
useReadline = true;
|
||||||
rl_readline_name = "psql";
|
rl_readline_name = "psql";
|
||||||
|
initialize_readline(&(pset->db));
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -37,20 +37,15 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
char *
|
char * gets_interactive(const char *prompt);
|
||||||
gets_interactive(const char *prompt);
|
|
||||||
|
|
||||||
char *
|
char * gets_fromFile(FILE *source);
|
||||||
gets_fromFile(FILE *source);
|
|
||||||
|
|
||||||
|
|
||||||
void
|
void initializeInput(int flags, PsqlSettings *pset);
|
||||||
initializeInput(int flags);
|
|
||||||
|
|
||||||
bool
|
bool saveHistory(const char *fname);
|
||||||
saveHistory(const char *fname);
|
|
||||||
|
|
||||||
void
|
void finishInput(void);
|
||||||
finishInput(void);
|
|
||||||
|
|
||||||
#endif
|
#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);
|
fprintf(pset->queryFout, "lo_import %d\n", loid);
|
||||||
|
pset->lastOid = loid;
|
||||||
|
|
||||||
return true;
|
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
|
* Show all large objects in database, with comments if desired
|
||||||
*/
|
*/
|
||||||
bool
|
bool
|
||||||
do_lo_list(PsqlSettings *pset)
|
do_lo_list(PsqlSettings *pset, bool desc)
|
||||||
{
|
{
|
||||||
PGresult *res;
|
PGresult *res;
|
||||||
char descbuf[512];
|
char buf[512];
|
||||||
printQueryOpt myopt = pset->popt;
|
printQueryOpt myopt = pset->popt;
|
||||||
|
|
||||||
descbuf[0] = '\0';
|
strcpy(buf, "SELECT usename as \"Owner\", substring(relname from 5) as \"ID\"");
|
||||||
strcat(descbuf, "SELECT usename as \"Owner\", substring(relname from 5) as \"ID\"");
|
if (desc)
|
||||||
if (GetVariableBool(pset->vars, "description"))
|
strcat(buf, ",\n obj_description(pg_class.oid) as \"Description\"");
|
||||||
strcat(descbuf, ",\n obj_description(pg_class.oid) as \"Description\"");
|
strcat(buf, "\nFROM pg_class, pg_user\n"
|
||||||
strcat(descbuf, "\nFROM pg_class, pg_user\n"
|
|
||||||
"WHERE usesysid = relowner AND relkind = 'l'\n"
|
"WHERE usesysid = relowner AND relkind = 'l'\n"
|
||||||
"ORDER BY \"ID\"");
|
"ORDER BY \"ID\"");
|
||||||
|
|
||||||
res = PSQLexec(pset, descbuf);
|
res = PSQLexec(pset, buf);
|
||||||
if (!res)
|
if (!res)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
#ifndef LARGE_OBJ_H
|
#ifndef LARGE_OBJ_H
|
||||||
#define LARGE_OBJ_H
|
#define LARGE_OBJ_H
|
||||||
|
|
||||||
|
#include <c.h>
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
|
||||||
bool do_lo_export(PsqlSettings *pset, const char *loid_arg, const char *filename_arg);
|
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_import(PsqlSettings *pset, const char *filename_arg, const char *comment_arg);
|
||||||
bool do_lo_unlink(PsqlSettings *pset, const char *loid_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 */
|
#endif /* LARGE_OBJ_H */
|
||||||
|
@ -16,19 +16,20 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/* MainLoop()
|
/*
|
||||||
* Main processing loop for reading lines of input
|
* Main processing loop for reading lines of input
|
||||||
* and sending them to the backend.
|
* and sending them to the backend.
|
||||||
*
|
*
|
||||||
* This loop is re-entrant. May be called by \i command
|
* This loop is re-entrant. May be called by \i command
|
||||||
* which reads input from a file.
|
* which reads input from a file.
|
||||||
|
*
|
||||||
|
* FIXME: rewrite this whole thing with flex
|
||||||
*/
|
*/
|
||||||
int
|
int
|
||||||
MainLoop(PsqlSettings *pset, FILE *source)
|
MainLoop(PsqlSettings *pset, FILE *source)
|
||||||
{
|
{
|
||||||
PQExpBuffer query_buf; /* buffer for query being accumulated */
|
PQExpBuffer query_buf; /* buffer for query being accumulated */
|
||||||
char *line; /* current line of input */
|
char *line; /* current line of input */
|
||||||
char *xcomment; /* start of extended comment */
|
|
||||||
int len; /* length of the line */
|
int len; /* length of the line */
|
||||||
int successResult = EXIT_SUCCESS;
|
int successResult = EXIT_SUCCESS;
|
||||||
backslashResult slashCmdStatus;
|
backslashResult slashCmdStatus;
|
||||||
@ -37,6 +38,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
|||||||
bool success;
|
bool success;
|
||||||
char in_quote; /* == 0 for no in_quote */
|
char in_quote; /* == 0 for no in_quote */
|
||||||
bool was_bslash; /* backslash */
|
bool was_bslash; /* backslash */
|
||||||
|
bool xcomment; /* in extended comment */
|
||||||
int paren_level;
|
int paren_level;
|
||||||
unsigned int query_start;
|
unsigned int query_start;
|
||||||
|
|
||||||
@ -49,7 +51,6 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
|||||||
bool prev_cmd_interactive;
|
bool prev_cmd_interactive;
|
||||||
|
|
||||||
bool die_on_error;
|
bool die_on_error;
|
||||||
const char *interpol_char;
|
|
||||||
|
|
||||||
|
|
||||||
/* Save old settings */
|
/* Save old settings */
|
||||||
@ -68,7 +69,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
|||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
xcomment = NULL;
|
xcomment = false;
|
||||||
in_quote = 0;
|
in_quote = 0;
|
||||||
paren_level = 0;
|
paren_level = 0;
|
||||||
slashCmdStatus = CMD_UNKNOWN; /* set default */
|
slashCmdStatus = CMD_UNKNOWN; /* set default */
|
||||||
@ -87,7 +88,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
|||||||
line = strdup(query_buf->data);
|
line = strdup(query_buf->data);
|
||||||
resetPQExpBuffer(query_buf);
|
resetPQExpBuffer(query_buf);
|
||||||
/* reset parsing state since we are rescanning whole query */
|
/* reset parsing state since we are rescanning whole query */
|
||||||
xcomment = NULL;
|
xcomment = false;
|
||||||
in_quote = 0;
|
in_quote = 0;
|
||||||
paren_level = 0;
|
paren_level = 0;
|
||||||
}
|
}
|
||||||
@ -106,7 +107,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
|||||||
prompt_status = PROMPT_SINGLEQUOTE;
|
prompt_status = PROMPT_SINGLEQUOTE;
|
||||||
else if (in_quote && in_quote == '"')
|
else if (in_quote && in_quote == '"')
|
||||||
prompt_status = PROMPT_DOUBLEQUOTE;
|
prompt_status = PROMPT_DOUBLEQUOTE;
|
||||||
else if (xcomment != NULL)
|
else if (xcomment)
|
||||||
prompt_status = PROMPT_COMMENT;
|
prompt_status = PROMPT_COMMENT;
|
||||||
else if (query_buf->len > 0)
|
else if (query_buf->len > 0)
|
||||||
prompt_status = PROMPT_CONTINUE;
|
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");
|
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
|
* query_buf holds query already accumulated. line is the
|
||||||
@ -144,11 +146,6 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* not currently inside an extended comment? */
|
|
||||||
if (xcomment)
|
|
||||||
xcomment = line;
|
|
||||||
|
|
||||||
|
|
||||||
/* strip trailing backslashes, they don't have a clear meaning */
|
/* strip trailing backslashes, they don't have a clear meaning */
|
||||||
while (1)
|
while (1)
|
||||||
{
|
{
|
||||||
@ -160,52 +157,6 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
|||||||
break;
|
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 */
|
/* nothing left on line? then ignore */
|
||||||
if (line[0] == '\0')
|
if (line[0] == '\0')
|
||||||
{
|
{
|
||||||
@ -213,6 +164,12 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
|||||||
continue;
|
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;
|
slashCmdStatus = CMD_UNKNOWN;
|
||||||
|
|
||||||
len = strlen(line);
|
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
|
* The current character is at line[i], the prior character at line[i
|
||||||
* - prevlen], the next character at line[i + thislen].
|
* - 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;
|
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? */
|
/* was the previous character a backslash? */
|
||||||
if (i > 0 && line[i - prevlen] == '\\')
|
was_bslash = (i > 0 && line[i - prevlen] == '\\');
|
||||||
was_bslash = true;
|
|
||||||
else
|
|
||||||
was_bslash = false;
|
|
||||||
|
|
||||||
|
|
||||||
/* in quote? */
|
/* in quote? */
|
||||||
if (in_quote)
|
if (in_quote)
|
||||||
@ -256,11 +204,11 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
|||||||
in_quote = line[i];
|
in_quote = line[i];
|
||||||
|
|
||||||
/* in extended comment? */
|
/* in extended comment? */
|
||||||
else if (xcomment != NULL)
|
else if (xcomment)
|
||||||
{
|
{
|
||||||
if (line[i] == '*' && line[i + thislen] == '/')
|
if (line[i] == '*' && line[i + thislen] == '/')
|
||||||
{
|
{
|
||||||
xcomment = NULL;
|
xcomment = false;
|
||||||
ADVANCE_1;
|
ADVANCE_1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -268,7 +216,7 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
|||||||
/* start of extended comment? */
|
/* start of extended comment? */
|
||||||
else if (line[i] == '/' && line[i + thislen] == '*')
|
else if (line[i] == '/' && line[i + thislen] == '*')
|
||||||
{
|
{
|
||||||
xcomment = &line[i];
|
xcomment = true;
|
||||||
ADVANCE_1;
|
ADVANCE_1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -287,8 +235,45 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
|||||||
else if (line[i] == ')' && paren_level > 0)
|
else if (line[i] == ')' && paren_level > 0)
|
||||||
paren_level--;
|
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 */
|
/* semicolon? then send query */
|
||||||
else if (line[i] == ';' && !was_bslash && paren_level == 0)
|
else if (line[i] == ';' && !was_bslash)
|
||||||
{
|
{
|
||||||
line[i] = '\0';
|
line[i] = '\0';
|
||||||
/* is there anything else on the line? */
|
/* is there anything else on the line? */
|
||||||
@ -312,6 +297,15 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
|||||||
query_start = i + thislen;
|
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 */
|
/* backslash command */
|
||||||
else if (was_bslash)
|
else if (was_bslash)
|
||||||
{
|
{
|
||||||
@ -355,7 +349,13 @@ MainLoop(PsqlSettings *pset, FILE *source)
|
|||||||
else
|
else
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
/* stop the script after error */
|
||||||
|
if (!success && die_on_error)
|
||||||
|
break;
|
||||||
|
|
||||||
|
} /* for (line) */
|
||||||
|
|
||||||
|
|
||||||
if (!success && die_on_error && !pset->cur_cmd_interactive)
|
if (!success && die_on_error && !pset->cur_cmd_interactive)
|
||||||
|
@ -371,11 +371,10 @@ print_aligned_vertical(const char *title, const char * const * headers,
|
|||||||
{
|
{
|
||||||
if (!opt_barebones)
|
if (!opt_barebones)
|
||||||
{
|
{
|
||||||
char *div_copy = strdup(divider);
|
|
||||||
char *record_str = malloc(32);
|
char *record_str = malloc(32);
|
||||||
size_t record_str_len;
|
size_t record_str_len;
|
||||||
|
|
||||||
if (!div_copy || !record_str)
|
if (!record_str)
|
||||||
{
|
{
|
||||||
perror("malloc");
|
perror("malloc");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
@ -386,29 +385,32 @@ print_aligned_vertical(const char *title, const char * const * headers,
|
|||||||
else
|
else
|
||||||
sprintf(record_str, "[ RECORD %d ]", record++);
|
sprintf(record_str, "[ RECORD %d ]", record++);
|
||||||
record_str_len = strlen(record_str);
|
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 (record_str_len + opt_border > strlen(divider))
|
||||||
if (!new)
|
fprintf(fout, "%.*s%s\n", opt_border, divider, record_str);
|
||||||
|
else
|
||||||
{
|
{
|
||||||
perror("realloc");
|
char *div_copy = strdup(divider);
|
||||||
|
|
||||||
|
if (!div_copy) {
|
||||||
|
perror("malloc");
|
||||||
exit(EXIT_FAILURE);
|
exit(EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
div_copy = new;
|
|
||||||
}
|
|
||||||
strncpy(div_copy + opt_border, record_str, record_str_len);
|
strncpy(div_copy + opt_border, record_str, record_str_len);
|
||||||
fprintf(fout, "%s\n", div_copy);
|
fprintf(fout, "%s\n", div_copy);
|
||||||
free(record_str);
|
|
||||||
free(div_copy);
|
free(div_copy);
|
||||||
}
|
}
|
||||||
else if (i != 0 && opt_border < 2)
|
free(record_str);
|
||||||
|
}
|
||||||
|
else if (i != 0 || opt_border == 2)
|
||||||
fprintf(fout, "%s\n", divider);
|
fprintf(fout, "%s\n", divider);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (opt_border == 2)
|
if (opt_border == 2)
|
||||||
fputs("| ", fout);
|
fputs("| ", fout);
|
||||||
fprintf(fout, "%-*s", hwidth, headers[i % col_count]);
|
fprintf(fout, "%-*s", hwidth, headers[i % col_count]);
|
||||||
|
|
||||||
if (opt_border > 0)
|
if (opt_border > 0)
|
||||||
fputs(" | ", fout);
|
fputs(" | ", fout);
|
||||||
else
|
else
|
||||||
|
@ -85,7 +85,7 @@ get_prompt(PsqlSettings *pset, promptStatus_t status)
|
|||||||
p && *p && strlen(destination) < MAX_PROMPT_SIZE;
|
p && *p && strlen(destination) < MAX_PROMPT_SIZE;
|
||||||
p++)
|
p++)
|
||||||
{
|
{
|
||||||
MemSet(buf, 0, MAX_PROMPT_SIZE + 1);
|
memset(buf, 0, MAX_PROMPT_SIZE + 1);
|
||||||
if (esc)
|
if (esc)
|
||||||
{
|
{
|
||||||
switch (*p)
|
switch (*p)
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
#ifndef SETTINGS_H
|
#ifndef SETTINGS_H
|
||||||
#define SETTINGS_H
|
#define SETTINGS_H
|
||||||
|
#include <config.h>
|
||||||
|
#include <c.h>
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <libpq-fe.h>
|
#include <libpq-fe.h>
|
||||||
#include <c.h>
|
#include <postgres_ext.h>
|
||||||
|
|
||||||
#include "variables.h"
|
#include "variables.h"
|
||||||
#include "print.h"
|
#include "print.h"
|
||||||
@ -41,6 +43,8 @@ typedef struct _psqlSettings
|
|||||||
|
|
||||||
bool has_client_encoding; /* was PGCLIENTENCODING set on
|
bool has_client_encoding; /* was PGCLIENTENCODING set on
|
||||||
* startup? */
|
* startup? */
|
||||||
|
Oid lastOid; /* saves oid from insert command
|
||||||
|
because people want it so badly */
|
||||||
} PsqlSettings;
|
} PsqlSettings;
|
||||||
|
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ main(int argc, char **argv)
|
|||||||
char *password = NULL;
|
char *password = NULL;
|
||||||
bool need_pass;
|
bool need_pass;
|
||||||
|
|
||||||
MemSet(&settings, 0, sizeof settings);
|
memset(&settings, 0, sizeof settings);
|
||||||
|
|
||||||
settings.cur_cmd_source = stdin;
|
settings.cur_cmd_source = stdin;
|
||||||
settings.cur_cmd_interactive = false;
|
settings.cur_cmd_interactive = false;
|
||||||
@ -161,7 +161,7 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
if (options.action == ACT_LIST_DB)
|
if (options.action == ACT_LIST_DB)
|
||||||
{
|
{
|
||||||
int success = listAllDbs(&settings);
|
int success = listAllDbs(&settings, false);
|
||||||
|
|
||||||
PQfinish(settings.db);
|
PQfinish(settings.db);
|
||||||
exit(!success);
|
exit(!success);
|
||||||
@ -187,7 +187,7 @@ main(int argc, char **argv)
|
|||||||
|
|
||||||
process_psqlrc(&settings);
|
process_psqlrc(&settings);
|
||||||
|
|
||||||
initializeInput(options.no_readline ? 0 : 1);
|
initializeInput(options.no_readline ? 0 : 1, &settings);
|
||||||
|
|
||||||
/* Now find something to do */
|
/* Now find something to do */
|
||||||
|
|
||||||
@ -270,7 +270,7 @@ parse_options(int argc, char *argv[], PsqlSettings *pset, struct adhoc_opts * op
|
|||||||
extern int optind;
|
extern int optind;
|
||||||
int c;
|
int c;
|
||||||
|
|
||||||
MemSet(options, 0, sizeof *options);
|
memset(options, 0, sizeof *options);
|
||||||
|
|
||||||
#ifdef HAVE_GETOPT_LONG
|
#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)
|
while ((c = getopt_long(argc, argv, "Ac:d:eEf:F:lh:Hno:p:P:qsStT:uU:v:VWx?", long_options, &optindex)) != -1)
|
||||||
|
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
|
#define VARIABLES_H
|
||||||
#include <c.h>
|
#include <c.h>
|
||||||
|
|
||||||
#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz0123456789_"
|
#define VALID_VARIABLE_CHARS "abcdefghijklmnopqrstuvwxyz"\
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" "0123456789_"
|
||||||
|
|
||||||
struct _variable
|
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