From 9ceb5d8a7bfc4a6315f37913afb5f3d6cefa651f Mon Sep 17 00:00:00 2001 From: Peter Eisentraut <peter_e@gmx.net> Date: Mon, 7 Feb 2000 23:10:11 +0000 Subject: [PATCH] Fixed psql double quoting of SQL ids Fixed libpq printing functions --- doc/src/sgml/libpq.sgml | 50 +- doc/src/sgml/ref/psql-ref.sgml | 167 ++--- src/bin/psql/command.c | 944 ++++++++++++++++++----------- src/bin/psql/common.c | 28 +- src/bin/psql/create_help.pl | 9 +- src/bin/psql/describe.c | 62 +- src/bin/psql/help.c | 115 ++-- src/bin/psql/input.c | 18 +- src/bin/psql/input.h | 6 +- src/bin/psql/large_obj.c | 6 +- src/bin/psql/mainloop.c | 10 +- src/bin/psql/print.c | 6 +- src/bin/psql/prompt.c | 19 +- src/bin/psql/prompt.h | 5 +- src/bin/psql/startup.c | 164 +++-- src/bin/psql/stringutils.c | 20 +- src/bin/psql/stringutils.h | 4 +- src/bin/psql/tab-complete.c | 6 +- src/interfaces/libpq/fe-auth.c | 6 +- src/interfaces/libpq/fe-connect.c | 19 +- src/interfaces/libpq/fe-exec.c | 26 +- src/interfaces/libpq/fe-lobj.c | 4 +- src/interfaces/libpq/fe-misc.c | 4 +- src/interfaces/libpq/fe-print.c | 21 +- src/interfaces/libpq/libpq-fe.h | 55 +- src/interfaces/libpq/libpq-int.h | 7 +- src/interfaces/libpq/pqexpbuffer.c | 16 +- src/interfaces/libpq/pqexpbuffer.h | 10 +- src/interfaces/libpq/win32.h | 4 +- 29 files changed, 1044 insertions(+), 767 deletions(-) diff --git a/doc/src/sgml/libpq.sgml b/doc/src/sgml/libpq.sgml index 46e90bbe88c..964dde8054b 100644 --- a/doc/src/sgml/libpq.sgml +++ b/doc/src/sgml/libpq.sgml @@ -494,7 +494,7 @@ soon.) <function>PQdb</function> Returns the database name of the connection. <synopsis> -const char *PQdb(const PGconn *conn) +char *PQdb(const PGconn *conn) </synopsis> PQdb and the next several functions return the values established at connection. These values are fixed for the life of the PGconn @@ -507,7 +507,7 @@ object. <function>PQuser</function> Returns the user name of the connection. <synopsis> -const char *PQuser(const PGconn *conn) +char *PQuser(const PGconn *conn) </synopsis> </para> </listitem> @@ -517,7 +517,7 @@ const char *PQuser(const PGconn *conn) <function>PQpass</function> Returns the password of the connection. <synopsis> -const char *PQpass(const PGconn *conn) +char *PQpass(const PGconn *conn) </synopsis> </para> </listitem> @@ -527,7 +527,7 @@ const char *PQpass(const PGconn *conn) <function>PQhost</function> Returns the server host name of the connection. <synopsis> -const char *PQhost(const PGconn *conn) +char *PQhost(const PGconn *conn) </synopsis> </para> </listitem> @@ -537,7 +537,7 @@ const char *PQhost(const PGconn *conn) <function>PQport</function> Returns the port of the connection. <synopsis> -const char *PQport(const PGconn *conn) +char *PQport(const PGconn *conn) </synopsis> </para> </listitem> @@ -547,7 +547,7 @@ const char *PQport(const PGconn *conn) <function>PQtty</function> Returns the debug tty of the connection. <synopsis> -const char *PQtty(const PGconn *conn) +char *PQtty(const PGconn *conn) </synopsis> </para> </listitem> @@ -557,7 +557,7 @@ const char *PQtty(const PGconn *conn) <function>PQoptions</function> Returns the backend options used in the connection. <synopsis> -const char *PQoptions(const PGconn *conn) +char *PQoptions(const PGconn *conn) </synopsis> </para> </listitem> @@ -602,7 +602,7 @@ ConnStatusType PQstatus(const PGconn *conn) Returns the error message most recently generated by an operation on the connection. <synopsis> -const char *PQerrorMessage(const PGconn* conn); +char *PQerrorMessage(const PGconn* conn); </synopsis> </para> @@ -790,7 +790,7 @@ exposes a bug in the client software. Converts the enumerated type returned by PQresultStatus into a string constant describing the status code. <synopsis> -const char *PQresStatus(ExecStatusType status); +char *PQresStatus(ExecStatusType status); </synopsis> </para> </listitem> @@ -801,7 +801,7 @@ const char *PQresStatus(ExecStatusType status); returns the error message associated with the query, or an empty string if there was no error. <synopsis> -const char *PQresultErrorMessage(const PGresult *res); +char *PQresultErrorMessage(const PGresult *res); </synopsis> Immediately following a <function>PQexec</function> or <function>PQgetResult</function> call, <function>PQerrorMessage</function> (on the connection) will return the same @@ -855,7 +855,7 @@ extracts data from a <acronym>BINARY</acronym> cursor. Returns the field (attribute) name associated with the given field index. Field indices start at 0. <synopsis> -const char *PQfname(const PGresult *res, +char *PQfname(const PGresult *res, int field_index); </synopsis> </para> @@ -931,9 +931,9 @@ int PQfmod(const PGresult *res, of a PGresult. Tuple and field indices start at 0. <synopsis> -const char* PQgetvalue(const PGresult *res, - int tup_num, - int field_num); +char* PQgetvalue(const PGresult *res, + int tup_num, + int field_num); </synopsis> For most queries, the value returned by <function>PQgetvalue</function> is a null-terminated <acronym>ASCII</acronym> string representation @@ -991,7 +991,7 @@ int PQgetisnull(const PGresult *res, Returns the command status string from the SQL command that generated the PGresult. <synopsis> -const char * PQcmdStatus(const PGresult *res); +char * PQcmdStatus(const PGresult *res); </synopsis> </para> </listitem> @@ -1001,7 +1001,7 @@ const char * PQcmdStatus(const PGresult *res); <function>PQcmdTuples</function> Returns the number of rows affected by the SQL command. <synopsis> -const char * PQcmdTuples(const PGresult *res); +char * PQcmdTuples(const PGresult *res); </synopsis> If the <acronym>SQL</acronym> command that generated the PGresult was INSERT, UPDATE or DELETE, this returns a @@ -1032,7 +1032,7 @@ Oid PQoidValue(const PGresult *res); inserted, if the <acronym>SQL</acronym> command was an INSERT. Otherwise, returns an empty string. <synopsis> -const char * PQoidStatus(const PGresult *res); +char * PQoidStatus(const PGresult *res); </synopsis> The function is deprecated in favor of <function>PQoidValue</function> and is not thread-safe. @@ -1050,25 +1050,25 @@ void PQprint(FILE* fout, /* output stream */ const PQprintOpt *po); struct { - int header; /* print output field headings and row count */ - int align; /* fill align the fields */ - int standard; /* old brain dead format */ - int html3; /* output html tables */ - int expanded; /* expand tables */ - int pager; /* use pager for output if needed */ + pqbool header; /* print output field headings and row count */ + pqbool align; /* fill align the fields */ + pqbool standard; /* old brain dead format */ + pqbool html3; /* output html tables */ + pqbool expanded; /* expand tables */ + pqbool pager; /* use pager for output if needed */ char *fieldSep; /* field separator */ char *tableOpt; /* insert to HTML <table ...> */ char *caption; /* HTML <caption> */ char **fieldName; /* null terminated array of replacement field names */ } PQprintOpt; </synopsis> - This function was formerly used by <application>psql</application> to print query results, but this is no longer the case and this -function is no longer supported. +function is no longer actively supported. </para> </listitem> +</listitem> <listitem> <para> <function>PQclear</function> diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml index 8f68d7ede49..4d0bf428c0a 100644 --- a/doc/src/sgml/ref/psql-ref.sgml +++ b/doc/src/sgml/ref/psql-ref.sgml @@ -1,5 +1,5 @@ <!-- -$Header: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.22 2000/01/29 16:58:27 petere Exp $ +$Header: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v 1.23 2000/02/07 23:10:03 petere Exp $ Postgres documentation --> @@ -64,8 +64,8 @@ Postgres documentation <para> <application>psql</application> is a regular <productname>PostgreSQL</productname> - client application. In order to connect to a database you need to determine - name of you target database, the hostname and port number of the server + client application. In order to connect to a database you need to know the + name of your target database, the hostname and port number of the server and what user name you want to connect as. <application>psql</application> can be told about those parameters via command line options, namely <option>-d</option>, <option>-h</option>, <option>-p</option>, and <option>-U</option> respectively. @@ -119,7 +119,7 @@ testdb=> Ordinarily, input lines are sent to the backend when a query-terminating semicolon is reached. An end of line does not terminate a query! Thus queries can be spread over serveral lines for clarity. If the query was sent and without - error the query results are displayed on the screen. + error, the query results are displayed on the screen. </para> <para> @@ -155,31 +155,35 @@ testdb=> </para> <para> - To include whitespace into an argument you must quote it with either single - or double quotes. Anything contained in single quotes (except for a - backslash-escaped single quote itself) is taken literally as the argument. - Anything contained in double quotes is furthermore subject to C-like - substitutions for <literal>\n</literal> (new line), <literal>\t</literal> (tab), - <literal>\</literal><replaceable>digits</replaceable>, + To include whitespace into an argument you must quote it with a single + quote. To include a single quote into such an argument, preceed it by + a backslash. Anything contained in single quotes is furthermore subject to + C-like substitutions for <literal>\n</literal> (new line), <literal>\t</literal> + (tab), <literal>\</literal><replaceable>digits</replaceable>, <literal>\0</literal><replaceable>digits</replaceable>, and <literal>\0x</literal><replaceable>digits</replaceable> (the character with the given decimal, octal, or hexadecimal code). </para> <para> - If an unquoted argument begins with a dollar sign (<literal>$</literal>), + If an unquoted argument begins with a colon (<literal>:</literal>), it is taken as a variable and the value of the variable is taken as the - argument instead. Inside double quotes, variable values can be substituted - by enclosing the name in a <literal>${...}</literal> sequence. See also under - <quote><xref linkend="APP-PSQL-variables" endterm="APP-PSQL-variables-title"></quote>. + argument instead. </para> <para> - Arguments that are quoted in <quote>back-ticks</quote> (<literal>`</literal>) - are taken as a command line - that is passed to the shell. The output of the command (with a trailing - newline removed) is taken as the argument value. Back-ticks are subject to - the same substitution rules as double-quotes. + Arguments that are quoted in <quote>backticks</quote> (<literal>`</literal>) + are taken as a command line that is passed to the shell. The output of the + command (with a trailing newline removed) is taken as the argument value. + The above escape sequences also apply in backticks. + </para> + + <para> + Some commands take the name of an <acronym>SQL</acronym> identifier (such as + a table name) as argument. These arguments follow the syntax rules of + <acronym>SQL</acronym> regarding double quotes: an identifier without + double quotes is coerced to lower-case. For all other commands + double quotes are not special and will become part of the argument. </para> <para> @@ -189,7 +193,7 @@ testdb=> (two backslashes) marks the end of arguments and continues parsing <acronym>SQL</acronym> queries, if any. That way <acronym>SQL</acronym> and <application>psql</application> commands can be freely mixed on a line. - In any case, the arguments of a meta-command cannot continue beyond the end + But in any case, the arguments of a meta-command cannot continue beyond the end of the line. </para> @@ -246,7 +250,7 @@ testdb=> <para> If the connection attempt failed (wrong username, access denied, etc.) the - previous connection will be kept, if and only if <application>psql</application> is + previous connection will be kept if and only if <application>psql</application> is in interactive mode. When executing a non-interactive script, processing will immediately stop with an error. This distinction was chosen as a user convenience against typos on the one hand, and a safety mechanism that @@ -286,7 +290,7 @@ testdb=> <para> This operation is not as efficient as the <acronym>SQL</acronym> <command>COPY</command> command because all data must pass through the - client/server IP or socket connection. For large amounts of data this other + client/server IP or socket connection. For large amounts of data the other technique may be preferable. </para> </tip> @@ -510,12 +514,15 @@ testdb=> <term><literal>\echo</literal> <replaceable class="parameter">text</replaceable> [ ... ]</term> <listitem> <para> - Prints the arguments to the standard output. This can be useful to + Prints the arguments to the standard output, separated by one space and + followed by a newline. This can be useful to intersperse information in the output of scripts. For example: <programlisting> => <userinput>\echo `date`</userinput> Tue Oct 26 21:40:57 CEST 1999 </programlisting> + If the first argument is an unquoted <literal>-n</literal> the the trailing + newline is not written. </para> <tip> @@ -602,7 +609,7 @@ Tue Oct 26 21:40:57 CEST 1999 <note> <para> If you want to see the lines on the screen as they are read you must set - the variable <envar>echo</envar>. + the variable <envar>ECHO</envar> to <literal>full</literal>. </para> </note> </listitem> @@ -658,7 +665,7 @@ Tue Oct 26 21:40:57 CEST 1999 Stores the file into a <productname>PostgreSQL</productname> <quote>large object</quote>. Optionally, it associates the given comment with the object. Example: <programlisting> -foo=> <userinput>\lo_import '/home/me/pictures/photo.xcf' 'a picture of me'</userinput> +foo=> <userinput>\lo_import '/home/peter/pictures/photo.xcf' 'a picture of me'</userinput> lo_import 152801 </programlisting> The response indicates that the large object received object id 152801 @@ -1006,12 +1013,13 @@ lo_import 152801 <varlistentry> - <term><literal>\set</literal> [ <replaceable class="parameter">name</replaceable> [ <replaceable class="parameter">value</replaceable> ]]</term> + <term><literal>\set</literal> [ <replaceable class="parameter">name</replaceable> [ <replaceable class="parameter">value</replaceable> [ ... ]]]</term> <listitem> <para> 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> or, if more than one + value is given, to the concatenation of all of them. If no second argument is given, the variable is just set with not value. To unset a variable, use the <command>\unset</command> command. </para> @@ -1145,9 +1153,7 @@ Access permissions for database "test" <para> Escapes to a separate Unix shell or executes the Unix command <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>). + are not further interpreted, the shell will see them as is. </para> </listitem> </varlistentry> @@ -1200,7 +1206,7 @@ Access permissions for database "test" <para> Specifies that <application>psql</application> is to execute one query string, <replaceable class="parameter">query</replaceable>, - and then exit. This is useful for shell scripts. + and then exit. This is useful in shell scripts. </para> <para> <replaceable class="parameter">query</replaceable> must be either a query string @@ -1208,8 +1214,8 @@ Access permissions for database "test" specific features), or it is a single backslash command. Thus you cannot mix <acronym>SQL</acronym> and <application>psql</application> meta-commands. To achieve this you could pipe the string into - <application>psql</application> and finish it with a a <literal>\q</literal>, - like so: <literal>echo "select * from foo; \q" | psql</literal>. + <application>psql</application>, like so: + <literal>echo "\x \\ select * from foo;" | psql</literal>. </para> </listitem> </varlistentry> @@ -1260,6 +1266,16 @@ Access permissions for database "test" After the file is processed, <application>psql</application> terminates. This in many ways equivalent to the internal command <command>\i</command>. </para> + <para> + Using this option is subtly different from writing + <literal>psql < <replaceable class="parameter">filename</replaceable></literal>. + In general, both will do what you expect, but using <literal>-f</literal> + enables some nice features such as error messages with line numbers. + There is also a slight chance that using this option will reduce + the startup overhead. On the other hand, the variant using the shell's + input redirection is (in theory) guaranteed to yield exactly the same + output that you would have gotten had you entered everything by hand. + </para> </listitem> </varlistentry> @@ -1311,16 +1327,6 @@ Access permissions for database "test" </varlistentry> - <varlistentry> - <term>-n, --no-readline</term> - <listitem> - <para> - Do not use readline for line editing and do not use the history. - </para> - </listitem> - </varlistentry> - - <varlistentry> <term>-o, --output <replaceable class="parameter">filename</replaceable></term> <listitem> @@ -1372,6 +1378,17 @@ Access permissions for database "test" </listitem> </varlistentry> + + <varlistentry> + <term>-R, --record-separator <replaceable class="parameter">separator</replaceable></term> + <listitem> + <para> + Use <replaceable class="parameter">separator</replaceable> as the record separator. + This is equivalent to the <command>\pset recordsep</command> command. + </para> + </listitem> + </varlistentry> + <varlistentry> <term>-s, --single-step</term> @@ -1492,7 +1509,7 @@ Access permissions for database "test" <para> As of version 7.0, <application>psql</application> automatically issues a password prompt whenever the backend requests password authentication. - Because this is currently based on a <quote>hack</quote> the automatic + Because this is currently based on a <quote>hack</quote>, the automatic recognition might mysteriously fail, hence this option to force a prompt. If no password prompt is issued and the backend requires password authentication the connection attempt will fail. @@ -1539,46 +1556,39 @@ Access permissions for database "test" <para> <application>psql</application> provides variable substitution features - similar to common Unix command shells. Variables are simply name/value + similar to common Unix command shells. This feature is new and not very + sophisticated, yet, but there are plans to expand it in the future. + Variables are simply name/value pairs, where the value can be any string of any length. To set variables, use the <application>psql</application> meta-command <command>\set</command>: <programlisting> testdb=> <userinput>\set foo bar</userinput> </programlisting> sets the variable <quote>foo</quote> to the value <quote>bar</quote>. To retrieve - the content of the variable, precede the name with a dollar-sign and use it + the content of the variable, precede the name with a colon and use it as the argument of any slash command: <programlisting> -testdb=> <userinput>\echo $foo</userinput> +testdb=> <userinput>\echo :foo</userinput> bar </programlisting> - Alternatively, the value can also be interpolated into a double-quoted (or backtick-quoted) - string, like so: -<programlisting> -testdb=> <userinput>\echo "foo is now ${foo}."</userinput> -foo is now bar. -</programlisting> - (The curly braces are required.) No variable substitution - will be performed in single-quoted strings or in any of the backslash commands - that have special parsing rules (e.g., <command>\copy</command>). </para> <note> <para> The arguments of <command>\set</command> are subject to the same substitution rules as with other commands. Thus you can construct interesting references - such as <literal>\set "${foo}bar" 'something'</literal> and get <quote>soft + such as <literal>\set :foo 'something'</literal> and get <quote>soft links</quote> or <quote>variable variables</quote> of <productname>Perl</productname> or <productname><acronym>PHP</acronym></productname> fame, respectively. Unfortunately (or fortunately?), there is not way to do anything useful - with these constructs. (<literal>\echo ${${foo}}</literal> doesn't work.) On the - other hand, <literal>\set bar $foo</literal> is a perfectly valid way to copy + with these constructs. On the + other hand, <literal>\set bar :foo</literal> is a perfectly valid way to copy a variable. </para> </note> <para> - If you call <command>\set</command> without an argument, the variable is simply + If you call <command>\set</command> without a second argument, the variable is simply set, but has no value. To unset (or delete) a variable, use the command <command>\unset</command>. </para> @@ -1830,7 +1840,7 @@ foo is now bar. <para> An additional useful feature of <application>psql</application> variables is that you can substitute (<quote>interpolate</quote>) them into - regular <acronym>SQL</acronym> statements. The syntax for this is to prepend + regular <acronym>SQL</acronym> statements. The syntax for this is again to prepend the variable name with a colon (<literal>:</literal>). <programlisting> testdb=> <userinput>\set foo 'my_table'</userinput> @@ -1850,8 +1860,7 @@ testdb=> <userinput>SELECT * FROM :foo;</userinput> Another possible use of this mechanism is to copy the contents of a file into a field. First load the file into a variable and then proceed as above. <programlisting> -testdb=> <userinput>\set content `cat my_file.txt`</userinput> -testdb=> <userinput>\set content "'${content}'"</userinput> +testdb=> <userinput>\set content '\'' `cat my_file.txt` '\''</userinput> testdb=> <userinput>INSERT INTO my_table VALUES (:content);</userinput> </programlisting> One possible problem with this approach is that <filename>my_file.txt</filename> @@ -1877,11 +1886,12 @@ testdb=> <userinput>\set content `sed -e "s/'/\\\\\\'/g" < my_file.txt`</userinp <para> Since colons may legally appear in queries, the following rule applies: If the variable - is not set, the character sequence <quote>colon-name</quote> is not changed. In any + is not set, the character sequence <quote>colon name</quote> is not changed. In any case you can escape a colon with a backslash to protect it from interpretation. (The colon syntax for variables is standard <acronym>SQL</acronym> for embedded query languages, such as <application>ecpg</application>. The colon syntax for - array slices and type casts are <productname>PostgreSQL</productname> extensions.) + array slices and type casts are <productname>PostgreSQL</productname> extensions, + hence the conflict.) </para> </refsect2> @@ -1975,7 +1985,7 @@ testdb=> <userinput>\set content `sed -e "s/'/\\\\\\'/g" < my_file.txt`</userinp </varlistentry> <varlistentry> - <term><literal>%$</literal><replaceable class="parameter">name</replaceable><literal>$</literal></term> + <term><literal>%:</literal><replaceable class="parameter">name</replaceable><literal>:</literal></term> <listitem><para> The value of the <application>psql</application>, <quote>magic</quote>, or environment variable <replaceable class="parameter">name</replaceable>. See the section @@ -2032,13 +2042,22 @@ testdb=> <userinput>\set content `sed -e "s/'/\\\\\\'/g" < my_file.txt`</userinp <para> <application>psql</application> supports the readline and history libraries for - convenienent line editing and retrieval. The command history is stored in a file + convenient line editing and retrieval. The command history is stored in a file named <filename>.psql_history</filename> in your home directory and is reloaded when <application>psql</application> starts up. Tab-completion is also supported, although the completion logic makes no claim to be an <acronym>SQL</acronym> parser. When available, <application>psql</application> is automatically built to use these - features. + features. If for some reason you do not like the tab completion, you can turn if off + by putting this in a file named <filename>.inputrc</filename> in your + home directory: +<programlisting> +$if psql +set disable-completion on +$endif +</programlisting> + (This is not a <application>psql</application> but a <application>readline</application> + feature. Read its documentation for further details.) </para> <para> @@ -2053,7 +2072,7 @@ testdb=> <userinput>\set content `sed -e "s/'/\\\\\\'/g" < my_file.txt`</userinp you have the library and header files installed in an obscure place you must tell <filename>configure</filename> about them, for example: <programlisting> -$ ./configure --with-includes=/opt/gnu/include --with-libraries=/opt/gnu/lib ... +$ ./configure --with-includes=/opt/gnu/include --with-libs=/opt/gnu/lib ... </programlisting> Then you have to recompile <application>psql</application> (not necessarily the entire code tree). @@ -2105,7 +2124,7 @@ testdb=> <userinput>\d my_table</userinput> At this point you decide to change the prompt to something more interesting: <programlisting> -testdb=> <userinput>\set prompt1 '%n@%m %~%R%# '</userinput> +testdb=> <userinput>\set PROMPT1 '%n@%m %~%R%# '</userinput> peter@localhost testdb=> </programlisting> Let's assume you have filled the table with data and want to take a look at it: @@ -2221,14 +2240,6 @@ Field separator is "oo". </para> </listitem> - <listitem> - <para> - The number of options for a backslash command is limited, probably to 16. - You can easily change this in the source code, and perhaps I will get around - to fixing this one day. Not that there is any command - that actually uses that many options though. - </para> - </listitem> </itemizedlist> </refsect2> diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c index 63ac6fbff42..ba198d7d914 100644 --- a/src/bin/psql/command.c +++ b/src/bin/psql/command.c @@ -3,12 +3,13 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/command.c,v 1.17 2000/02/05 12:27:56 ishii Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/command.c,v 1.18 2000/02/07 23:10:04 petere Exp $ */ #include <c.h> #include "command.h" #include <errno.h> +#include <assert.h> #include <string.h> #include <stdio.h> #include <stdlib.h> @@ -17,54 +18,40 @@ #include <sys/types.h> /* for umask() */ #include <sys/stat.h> /* for umask(), stat() */ #include <unistd.h> /* for geteuid(), getpid(), stat() */ +#else +#include <win32.h> #endif -#include <assert.h> #include <libpq-fe.h> #include <pqexpbuffer.h> -#include "stringutils.h" -#include "mainloop.h" +#include "common.h" #include "copy.h" +#include "describe.h" #include "help.h" -#include "settings.h" -#include "common.h" +#include "input.h" #include "large_obj.h" +#include "mainloop.h" #include "print.h" -#include "describe.h" -#include "input.h" +#include "settings.h" #include "variables.h" -#ifdef WIN32 -#include "../../interfaces/libpq/win32.h" -#define popen(x,y) _popen(x,y) -#define pclose(x) _pclose(x) -#endif - /* functions for use in this file */ static backslashResult exec_command(const char *cmd, - char *const * options, const char *options_string, + const char ** continue_parse, PQExpBuffer query_buf); -static bool do_edit(const char *filename_arg, PQExpBuffer query_buf); - -static char * unescape(const char *source); - -static bool do_connect(const char *new_dbname, - const char *new_user); - +enum option_type { OT_NORMAL, OT_SQLID }; +static char * scan_option(char ** string, enum option_type type, char * quote); +static char * unescape(const unsigned char *source, size_t len); +static bool do_edit(const char *filename_arg, PQExpBuffer query_buf); +static bool do_connect(const char *new_dbname, const char *new_user); 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 /*---------- @@ -79,7 +66,7 @@ static bool do_shell(const char *command); * * 'query_buf' contains the query-so-far, which may be modified by * execution of the backslash command (for example, \r clears it) - * query_buf can be NULL if there is no query-so-far. + * query_buf can be NULL if there is no query so far. * * Returns a status code indicating what action is desired, see command.h. *---------- @@ -92,18 +79,13 @@ HandleSlashCmds(const char *line, { backslashResult status = CMD_SKIP_LINE; char *my_line; - char *options[NR_OPTIONS+1]; - char *token; - const char *options_string = NULL; - const char *cmd; + char *options_string = NULL; size_t blank_loc; - int i; const char *continue_parse = NULL; /* tell the mainloop where the * backslash command ended */ #ifdef USE_ASSERT_CHECKING assert(line); - assert(query_buf); assert(end_of_cmd); #endif @@ -129,145 +111,37 @@ HandleSlashCmds(const char *line, my_line[blank_loc] = '\0'; } - options[0] = NULL; - - if (options_string) - { - char quote; - unsigned int pos; - - options_string = &options_string[strspn(options_string, " \t")]; /* skip leading - * whitespace */ - - i = 0; - token = strtokx(options_string, " \t", "\"'`", '\\', "e, &pos, pset.encoding); - - for (i = 0; token && i < NR_OPTIONS; i++) - { - switch (quote) - { - case '"': - options[i] = unescape(token); - break; - case '\'': - options[i] = xstrdup(token); - break; - case '`': - { - bool error = false; - FILE *fd = NULL; - char *file = unescape(token); - PQExpBufferData output; - char buf[512]; - size_t result; - - fd = popen(file, "r"); - if (!fd) - { - psql_error("%s: %s\n", file, strerror(errno)); - error = true; - } - - if (!error) - { - initPQExpBuffer(&output); - - do - { - result = fread(buf, 1, 512, fd); - if (ferror(fd)) - { - psql_error("%s: %s\n", file, strerror(errno)); - error = true; - break; - } - appendBinaryPQExpBuffer(&output, buf, result); - } while (!feof(fd)); - appendPQExpBufferChar(&output, '\0'); - - if (pclose(fd) == -1) - { - psql_error("%s: %s\n", file, strerror(errno)); - error = true; - } - } - - if (!error) - { - if (output.data[strlen(output.data) - 1] == '\n') - output.data[strlen(output.data) - 1] = '\0'; - } - - free(file); - if (!error) - options[i] = output.data; - else - { - options[i] = xstrdup(""); - termPQExpBuffer(&output); - } - break; - } - case 0: - default: - if (token[0] == '\\') - continue_parse = options_string + pos; - else if (token[0] == '$') - { - const char * value = GetVariable(pset.vars, token+1); - if (!value) - value = ""; - options[i] = xstrdup(value); - } - else - options[i] = xstrdup(token); - } - - if (continue_parse) - break; - - token = strtokx(NULL, " \t", "\"'`", '\\', "e, &pos, pset.encoding); - } /* for */ - - options[i] = NULL; - } - - cmd = my_line; - status = exec_command(cmd, options, options_string, query_buf); + status = exec_command(my_line, options_string, &continue_parse, query_buf); if (status == CMD_UNKNOWN) { - /* - * If the command was not recognized, try inserting a space after - * the first letter and call again. The one letter commands allow - * arguments to start immediately after the command, but that is - * no longer encouraged. + * If the command was not recognized, try inserting a space after the + * first letter and call again. The one letter commands allow arguments + * to start immediately after the command, but that is no longer + * encouraged. */ - const char *new_options[NR_OPTIONS+1]; char new_cmd[2]; - int i; - - for (i = 1; i < NR_OPTIONS+1; i++) - new_options[i] = options[i - 1]; - new_options[0] = cmd + 1; - new_cmd[0] = cmd[0]; + new_cmd[0] = my_line[0]; new_cmd[1] = '\0'; - status = exec_command(new_cmd, (char *const *) new_options, my_line + 2, query_buf); + status = exec_command(new_cmd, my_line + 1, &continue_parse, query_buf); + + if (status != CMD_UNKNOWN && isalpha(new_cmd[0])) + psql_error("Warning: this syntax is deprecated\n"); } if (status == CMD_UNKNOWN) { if (pset.cur_cmd_interactive) - fprintf(stderr, "Invalid command \\%s. Try \\? for help.\n", cmd); + fprintf(stderr, "Invalid command \\%s. Try \\? for help.\n", my_line); else - psql_error("invalid command \\%s\n", cmd); + psql_error("invalid command \\%s\n", my_line); status = CMD_ERROR; } - if (continue_parse && *(continue_parse + 1) == '\\') + if (continue_parse && *continue_parse && *(continue_parse + 1) == '\\') continue_parse += 2; @@ -276,10 +150,6 @@ HandleSlashCmds(const char *line, else *end_of_cmd = line + strlen(line); - /* clean up */ - for (i = 0; i < NR_OPTIONS && options[i]; i++) - free(options[i]); - free(my_line); return status; @@ -287,19 +157,26 @@ HandleSlashCmds(const char *line, - static backslashResult exec_command(const char *cmd, - char *const * options, const char *options_string, + const char ** continue_parse, PQExpBuffer query_buf) { bool success = true; /* indicate here if the command ran ok or * failed */ bool quiet = QUIET(); - backslashResult status = CMD_SKIP_LINE; + char *string, *string_cpy; + /* + * The 'string' variable will be overwritten to point to the next token, + * hence we need an extra pointer so we can free this at the end. + */ + if (options_string) + string = string_cpy = xstrdup(options_string); + else + string = string_cpy = NULL; /* \a -- toggle field alignment This makes little sense but we keep it around. */ if (strcmp(cmd, "a") == 0) @@ -310,44 +187,53 @@ exec_command(const char *cmd, success = do_pset("format", "unaligned", &pset.popt, quiet); } - /* \C -- override table title (formerly change HTML caption) */ else if (strcmp(cmd, "C") == 0) - success = do_pset("title", options[0], &pset.popt, quiet); - + { + char * opt = scan_option(&string, OT_NORMAL, NULL); + success = do_pset("title", opt, &pset.popt, quiet); + free(opt); + } /*---------- * \c or \connect -- connect to new database or as different user * - * \c foo bar: connect to db "foo" as user "bar" - * \c foo [-]: connect to db "foo" as current user - * \c - bar: connect to current db as user "bar" - * \c: connect to default db as default user + * \c foo bar connect to db "foo" as user "bar" + * \c foo [-] connect to db "foo" as current user + * \c - bar connect to current db as user "bar" + * \c connect to default db as default user *---------- */ else if (strcmp(cmd, "c") == 0 || strcmp(cmd, "connect") == 0) { - if (options[1]) + char *opt1, *opt2; + char opt1q, opt2q; + + opt1 = scan_option(&string, OT_NORMAL, &opt1q); + opt2 = scan_option(&string, OT_NORMAL, &opt2q); + + if (opt2) /* gave username */ - success = do_connect(options[0], options[1]); - else - { - if (options[0]) - /* gave database name */ - success = do_connect(options[0], ""); /* empty string is same - * username as before, - * NULL would mean libpq - * default */ - else - /* connect to default db as default user */ - success = do_connect(NULL, NULL); - } - } + success = do_connect(!opt1q && (strcmp(opt1, "-")==0 || strcmp(opt1, "")==0) ? "" : opt1, + !opt2q && (strcmp(opt2, "-")==0 || strcmp(opt2, "")==0) ? "" : opt2); + else if (opt1) + /* gave database name */ + success = do_connect(!opt1q && (strcmp(opt1, "-")==0 || strcmp(opt1, "")==0) ? "" : opt1, ""); + else + /* connect to default db as default user */ + success = do_connect(NULL, NULL); + free(opt1); + free(opt2); + } /* \copy */ else if (strcasecmp(cmd, "copy") == 0) + { success = do_copy(options_string); + if (options_string) + string += strlen(string); + } /* \copyright */ else if (strcmp(cmd, "copyright") == 0) @@ -356,38 +242,42 @@ exec_command(const char *cmd, /* \d* commands */ else if (cmd[0] == 'd') { - bool show_verbose = strchr(cmd, '+') ? true : false; + char * name; + bool show_verbose; + + name = scan_option(&string, OT_SQLID, NULL); + show_verbose = strchr(cmd, '+') ? true : false; switch (cmd[1]) { case '\0': case '+': - if (options[0]) - success = describeTableDetails(options[0], show_verbose); + if (name) + success = describeTableDetails(name, show_verbose); else /* standard listing of interesting things */ success = listTables("tvs", NULL, show_verbose); break; case 'a': - success = describeAggregates(options[0]); + success = describeAggregates(name); break; case 'd': - success = objectDescription(options[0]); + success = objectDescription(name); break; case 'f': - success = describeFunctions(options[0], show_verbose); + success = describeFunctions(name, show_verbose); break; case 'l': success = do_lo_list(); break; case 'o': - success = describeOperators(options[0]); + success = describeOperators(name); break; case 'p': - success = permissionsList(options[0]); + success = permissionsList(name); break; case 'T': - success = describeTypes(options[0], show_verbose); + success = describeTypes(name, show_verbose); break; case 't': case 'v': @@ -397,11 +287,12 @@ exec_command(const char *cmd, if (cmd[1] == 'S' && cmd[2] == '\0') success = listTables("Stvs", NULL, show_verbose); else - success = listTables(&cmd[1], options[0], show_verbose); + success = listTables(&cmd[1], name, show_verbose); break; default: status = CMD_UNKNOWN; } + free(name); } @@ -410,46 +301,81 @@ exec_command(const char *cmd, * the query buffer */ else if (strcmp(cmd, "e") == 0 || strcmp(cmd, "edit") == 0) - status = do_edit(options[0], query_buf) ? CMD_NEWEDIT : CMD_ERROR; + { + char * fname; + if (!query_buf) + { + psql_error("no query buffer"); + status = CMD_ERROR; + } + else + { + fname = scan_option(&string, OT_NORMAL, NULL); + status = do_edit(fname, query_buf) ? CMD_NEWEDIT : CMD_ERROR; + free(fname); + } + } - /* \echo */ - else if (strcmp(cmd, "echo") == 0) + /* \echo and \qecho */ + else if (strcmp(cmd, "echo") == 0 || strcmp(cmd, "qecho")==0) { - int i; + char * value; + char quoted; + bool no_newline = false; + bool first = true; + FILE * fout; + + if (strcmp(cmd, "qecho")==0) + fout = pset.queryFout; + else + fout = stdout; - for (i = 0; i < 16 && options[i]; i++) - fputs(options[i], stdout); - fputs("\n", stdout); + while((value = scan_option(&string, OT_NORMAL, "ed))) + { + if (!quoted && strcmp(value, "-n")==0) + no_newline = true; + else + { + if (first) + first = false; + else + fputc(' ', fout); + fputs(value, fout); + } + free(value); + } + if (!no_newline) + fputs("\n", fout); } /* \f -- change field separator */ else if (strcmp(cmd, "f") == 0) - success = do_pset("fieldsep", options[0], &pset.popt, quiet); + { + char * fname = scan_option(&string, OT_NORMAL, NULL); + success = do_pset("fieldsep", fname, &pset.popt, quiet); + free(fname); + } /* \g means send query */ else if (strcmp(cmd, "g") == 0) { - if (!options[0]) + char * fname = scan_option(&string, OT_NORMAL, NULL); + if (!fname) pset.gfname = NULL; else - pset.gfname = xstrdup(options[0]); + pset.gfname = xstrdup(fname); + free(fname); status = CMD_SEND; } /* help */ else if (strcmp(cmd, "h") == 0 || strcmp(cmd, "help") == 0) { - 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); + helpSQL(options_string ? &options_string[strspn(options_string, " \t")] : NULL); + /* set pointer to end of line */ + if (string) + string += strlen(string); } /* HTML mode */ @@ -465,46 +391,55 @@ exec_command(const char *cmd, /* \i is include file */ else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0) { - if (!options[0]) + char * fname = scan_option(&string, OT_NORMAL, NULL); + if (!fname) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else - success = process_file(options[0]); + { + success = process_file(fname); + free (fname); + } } - /* \l is list databases */ else if (strcmp(cmd, "l") == 0 || strcmp(cmd, "list") == 0) success = listAllDbs(false); else if (strcmp(cmd, "l+") == 0 || strcmp(cmd, "list+") == 0) success = listAllDbs(true); - - /* large object things */ + /* + * large object things + */ else if (strncmp(cmd, "lo_", 3) == 0) { + char *opt1, *opt2; + + opt1 = scan_option(&string, OT_NORMAL, NULL); + opt2 = scan_option(&string, OT_NORMAL, NULL); + if (strcmp(cmd + 3, "export") == 0) { - if (!options[1]) + if (!opt2) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else - success = do_lo_export(options[0], options[1]); + success = do_lo_export(opt1, opt2); } else if (strcmp(cmd + 3, "import") == 0) { - if (!options[0]) + if (!opt1) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else - success = do_lo_import(options[0], options[1]); + success = do_lo_import(opt1, opt2); } else if (strcmp(cmd + 3, "list") == 0) @@ -512,23 +447,30 @@ exec_command(const char *cmd, else if (strcmp(cmd + 3, "unlink") == 0) { - if (!options[0]) + if (!opt1) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else - success = do_lo_unlink(options[0]); + success = do_lo_unlink(opt1); } else status = CMD_UNKNOWN; + + free(opt1); + free(opt2); } + /* \o -- set query output */ else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0) - success = setQFout(options[0]); - + { + char * fname = scan_option(&string, OT_NORMAL, NULL); + success = setQFout(fname); + free(fname); + } /* \p prints the current query buffer */ else if (strcmp(cmd, "p") == 0 || strcmp(cmd, "print") == 0) @@ -543,29 +485,24 @@ exec_command(const char *cmd, /* \pset -- set printing parameters */ else if (strcmp(cmd, "pset") == 0) { - if (!options[0]) + char * opt0 = scan_option(&string, OT_NORMAL, NULL); + char * opt1 = scan_option(&string, OT_NORMAL, NULL); + if (!opt0) { psql_error("\\%s: missing required argument\n", cmd); success = false; } else - success = do_pset(options[0], options[1], &pset.popt, quiet); + success = do_pset(opt0, opt1, &pset.popt, quiet); + + free(opt0); + free(opt1); } /* \q or \quit */ else if (strcmp(cmd, "q") == 0 || strcmp(cmd, "quit") == 0) status = CMD_TERMINATE; - /* \qecho */ - else if (strcmp(cmd, "qecho") == 0) - { - int i; - - for (i = 0; i < 16 && options[i]; i++) - fputs(options[i], pset.queryFout); - fputs("\n", pset.queryFout); - } - /* reset(clear) the buffer */ else if (strcmp(cmd, "r") == 0 || strcmp(cmd, "reset") == 0) { @@ -574,34 +511,31 @@ exec_command(const char *cmd, puts("Query buffer reset (cleared)."); } - /* \s save history in a file or show it on the screen */ else if (strcmp(cmd, "s") == 0) { - const char *fname; + char *fname = scan_option(&string, OT_NORMAL, NULL); - if (!options[0]) - fname = "/dev/tty"; - else - fname = options[0]; - - success = saveHistory(fname); + success = saveHistory(fname ? fname : "/dev/tty"); - if (success && !quiet && options[0]) + if (success && !quiet && fname) printf("Wrote history to %s.\n", fname); + free(fname); } - - /* \set -- generalized set option command */ + /* \set -- generalized set variable/option command */ else if (strcmp(cmd, "set") == 0) { - if (!options[0]) + char * opt0 = scan_option(&string, OT_NORMAL, NULL); + + if (!opt0) { /* list all variables */ /* - * (This is in utter violation of the GetVariable abstraction, - * but I have not dreamt up a better way.) + * XXX + * This is in utter violation of the GetVariable abstraction, but I + * have not bothered to do it better. */ struct _variable *ptr; @@ -611,15 +545,36 @@ exec_command(const char *cmd, } else { - const char * val = options[1]; - if (!val) - val = ""; - if (!SetVariable(pset.vars, options[0], val)) + /* + * Set variable to the concatenation of the arguments. + */ + char * newval = NULL; + char * opt; + + opt = scan_option(&string, OT_NORMAL, NULL); + newval = xstrdup(opt ? opt : ""); + free(opt); + + while ((opt = scan_option(&string, OT_NORMAL, NULL))) + { + newval = realloc(newval, strlen(newval) + strlen(opt) + 1); + if (!newval) + { + psql_error("out of memory"); + exit(EXIT_FAILURE); + } + strcat(newval, opt); + free(opt); + } + + if (!SetVariable(pset.vars, opt0, newval)) { psql_error("\\%s: error\n", cmd); success = false; } + free(newval); } + free(opt0); } /* \t -- turn off headers and row count */ @@ -629,48 +584,67 @@ exec_command(const char *cmd, /* \T -- define html <table ...> attributes */ else if (strcmp(cmd, "T") == 0) - success = do_pset("tableattr", options[0], &pset.popt, quiet); + { + char * value = scan_option(&string, OT_NORMAL, NULL); + success = do_pset("tableattr", value, &pset.popt, quiet); + free(value); + } /* \unset */ else if (strcmp(cmd, "unset") == 0) { - if (!SetVariable(pset.vars, options[0], NULL)) + char * opt = scan_option(&string, OT_NORMAL, NULL); + if (!opt) + { + psql_error("\\%s: missing required argument", cmd); + success = false; + } + if (!SetVariable(pset.vars, opt, NULL)) { psql_error("\\%s: error\n", cmd); - success = false; - } + } + free(opt); } /* \w -- write query buffer to file */ else if (strcmp(cmd, "w") == 0 || strcmp(cmd, "write") == 0) { FILE *fd = NULL; - bool pipe = false; + bool is_pipe = false; + char *fname = NULL; - if (!options[0]) - { - psql_error("\\%s: missing required argument\n", cmd); - success = false; - } - else - { - if (options[0][0] == '|') - { - pipe = true; - fd = popen(&options[0][1], "w"); - } - else - { - fd = fopen(options[0], "w"); - } + if (!query_buf) + { + psql_error("no query buffer"); + status = CMD_ERROR; + } + else + { + fname = scan_option(&string, OT_NORMAL, NULL); - if (!fd) - { - psql_error("%s: %s\n", options[0], strerror(errno)); - success = false; - } - } + if (!fname) + { + psql_error("\\%s: missing required argument\n", cmd); + success = false; + } + else + { + if (fname[0] == '|') + { + is_pipe = true; + fd = popen(&fname[1], "w"); + } + else + fd = fopen(fname, "w"); + + if (!fd) + { + psql_error("%s: %s\n", fname, strerror(errno)); + success = false; + } + } + } if (fd) { @@ -679,17 +653,19 @@ exec_command(const char *cmd, if (query_buf && query_buf->len > 0) fprintf(fd, "%s\n", query_buf->data); - if (pipe) + if (is_pipe) result = pclose(fd); else result = fclose(fd); if (result == EOF) { - psql_error("%s: %s\n", options[0], strerror(errno)); + psql_error("%s: %s\n", fname, strerror(errno)); success = false; } } + + free(fname); } /* \x -- toggle expanded table representation */ @@ -697,30 +673,43 @@ exec_command(const char *cmd, success = do_pset("expanded", NULL, &pset.popt, quiet); - /* list table rights (grant/revoke) */ + /* \z -- list table rights (grant/revoke) */ else if (strcmp(cmd, "z") == 0) - success = permissionsList(options[0]); - + { + char * opt = scan_option(&string, OT_SQLID, NULL); + success = permissionsList(opt); + free(opt); + } + /* \! -- shell escape */ else if (strcmp(cmd, "!") == 0) + { success = do_shell(options_string); + /* wind pointer to end of line */ + if (string) + string += strlen(string); + } + /* \? -- slash command help */ else if (strcmp(cmd, "?") == 0) slashUsage(); - -#if 0 +#if 1 /* * These commands don't do anything. I just use them to test the * parser. */ else if (strcmp(cmd, "void") == 0 || strcmp(cmd, "#") == 0) { - int i; + int i = 0; + char *value; - fprintf(stderr, "+ optline = |%s|\n", options_string); - for (i = 0; options[i]; i++) - fprintf(stderr, "+ opt%d = |%s|\n", i, options[i]); + fprintf(stderr, "+ optstr = |%s|\n", options_string); + while((value = scan_option(&string, OT_NORMAL, NULL))) + { + fprintf(stderr, "+ opt(%d) = |%s|\n", i++, value); + free(value); + } } #endif @@ -729,23 +718,300 @@ exec_command(const char *cmd, if (!success) status = CMD_ERROR; + + /* eat the rest of the options string */ + while(scan_option(&string, OT_NORMAL, NULL)) ; + + if (options_string && continue_parse) + *continue_parse = options_string + (string - string_cpy); + free(string_cpy); + return status; } +/* + * scan_option() + */ +static char * +scan_option(char ** string, enum option_type type, char * quote) +{ + unsigned int pos = 0; + char * options_string; + char * return_val; + + if (quote) + *quote = 0; + + if (!string || !(*string)) + return NULL; + + options_string = *string; + /* skip leading whitespace */ + pos += strspn(options_string+pos, " \t"); + + switch (options_string[pos]) + { + /* + * Double quoted string + */ + case '"': + { + unsigned int jj; + unsigned short int bslash_count = 0; + + /* scan for end of quote */ + for (jj = pos+1; options_string[jj]; jj += PQmblen(&options_string[jj], pset.encoding)) + { + if (options_string[jj] == '"' && bslash_count % 2 == 0) + break; + + if (options_string[jj] == '\\') + bslash_count++; + else + bslash_count=0; + } + + if (options_string[jj] == 0) + { + psql_error("parse error at end of line\n"); + *string = &options_string[jj]; + return NULL; + } + + return_val = malloc(jj-pos+2); + if (!return_val) + { + psql_error("out of memory\n"); + exit(EXIT_FAILURE); + } + + if (type == OT_NORMAL) + { + strncpy(return_val, &options_string[pos], jj-pos+1); + return_val[jj-pos+1] = '\0'; + } + /* + * If this is expected to be an SQL identifier like option + * then we strip out the double quotes + */ + else if (type == OT_SQLID) + { + unsigned int k, cc; + + bslash_count = 0; + cc = 0; + for (k = pos+1; options_string[k]; k += PQmblen(&options_string[k], pset.encoding)) + { + if (options_string[k] == '"' && bslash_count % 2 == 0) + break; + + if (options_string[jj] == '\\') + bslash_count++; + else + bslash_count=0; + + return_val[cc++] = options_string[k]; + } + return_val[cc] = '\0'; + } + + *string = options_string + jj+1; + if (quote) + *quote = '"'; + + return return_val; + } + + /* + * A single quote has a psql internal meaning, such as + * for delimiting file names, and it also allows for such + * escape sequences as \t. + */ + case '\'': + { + unsigned int jj; + unsigned short int bslash_count = 0; + + for (jj = pos+1; options_string[jj]; jj += PQmblen(&options_string[jj], pset.encoding)) + { + if (options_string[jj] == '\'' && bslash_count % 2 == 0) + break; + + if (options_string[jj] == '\\') + bslash_count++; + else + bslash_count=0; + } + + if (options_string[jj] == 0) + { + psql_error("parse error at end of line\n"); + *string = &options_string[jj]; + return NULL; + } + + return_val = unescape(&options_string[pos+1], jj-pos-1); + *string = &options_string[jj + 1]; + if (quote) + *quote = '\''; + return return_val; + } + + /* + * Backticks are for command substitution, like in shells + */ + case '`': + { + bool error = false; + FILE *fd = NULL; + char *file; + PQExpBufferData output; + char buf[512]; + size_t result, len; + + len = strcspn(options_string + pos + 1, "`"); + if (options_string[pos + 1 + len] == 0) + { + psql_error("parse error at end of line\n"); + *string = &options_string[pos + 1 + len]; + return NULL; + } + + options_string[pos + 1 + len] = '\0'; + file = options_string + pos + 1; + + fd = popen(file, "r"); + if (!fd) + { + psql_error("%s: %s\n", file, strerror(errno)); + error = true; + } + + if (!error) + { + initPQExpBuffer(&output); + + do + { + result = fread(buf, 1, 512, fd); + if (ferror(fd)) + { + psql_error("%s: %s\n", file, strerror(errno)); + error = true; + break; + } + appendBinaryPQExpBuffer(&output, buf, result); + } while (!feof(fd)); + appendPQExpBufferChar(&output, '\0'); + + if (pclose(fd) == -1) + { + psql_error("%s: %s\n", file, strerror(errno)); + error = true; + } + } + + if (!error) + { + if (output.data[strlen(output.data) - 1] == '\n') + output.data[strlen(output.data) - 1] = '\0'; + } + + if (!error) + return_val = output.data; + else + { + return_val = xstrdup(""); + termPQExpBuffer(&output); + } + options_string[pos + 1 + len] = '`'; + *string = options_string + pos + len + 2; + if (quote) + *quote = '`'; + return return_val; + } + + /* + * end of line + */ + case 0: + *string = &options_string[pos]; + return NULL; + + /* + * Variable substitution + */ + case ':': + { + size_t token_end; + const char * value; + char save_char; + + token_end = strcspn(&options_string[pos+1], " \t"); + save_char = options_string[pos+token_end+1]; + options_string[pos+token_end+1] = '\0'; + value = GetVariable(pset.vars, options_string+pos+1); + if (!value) + value = ""; + return_val = xstrdup(value); + options_string[pos+token_end+1] = save_char; + *string = &options_string[pos + token_end+1]; + return return_val; + } + + /* + * Next command + */ + case '\\': + *string = options_string + pos; + return NULL; + break; + + /* + * A normal word + */ + default: + { + size_t token_end; + char * cp; + + token_end = strcspn(&options_string[pos], " \t"); + return_val = malloc(token_end + 1); + if (!return_val) + { + psql_error("out of memory\n"); + exit(EXIT_FAILURE); + } + strncpy(return_val, &options_string[pos], token_end); + return_val[token_end] = 0; + + if (type == OT_SQLID) + for (cp = return_val; *cp; cp += PQmblen(cp, pset.encoding)) + if (isascii(*cp)) + *cp = tolower(*cp); + + *string = &options_string[pos+token_end]; + return return_val; + } + + } +} + + + /* * unescape * * Replaces \n, \t, and the like. - * Also interpolates ${variables}. * * The return value is malloc()'ed. */ static char * -unescape(const char *source) +unescape(const unsigned char *source, size_t len) { - unsigned char *p; + const unsigned char *p; bool esc = false; /* Last character we saw was the escape * character */ char *destination, @@ -756,16 +1022,16 @@ unescape(const char *source) assert(source); #endif - length = strlen(source) + 1; + length = Min(len, strlen(source)) + 1; - tmp = destination = (char *) malloc(length); + tmp = destination = malloc(length); if (!tmp) { psql_error("out of memory\n"); exit(EXIT_FAILURE); } - for (p = (char *) source; *p; p += PQmblen(p, pset.encoding)) + for (p = source; p-source < len && *p; p += PQmblen(p, pset.encoding)) { if (esc) { @@ -776,12 +1042,15 @@ unescape(const char *source) case 'n': c = '\n'; break; - case 'r': - c = '\r'; - break; case 't': c = '\t'; break; + case 'b': + c = '\b'; + break; + case 'r': + c = '\r'; + break; case 'f': c = '\f'; break; @@ -814,44 +1083,6 @@ unescape(const char *source) else if (*p == '\\') esc = true; - else if (*p == '$') - { - if (*(p + 1) == '{') - { - unsigned int len; - char *copy; - const char *value; -#ifndef WIN32 - void *new; -#else - char *new; -#endif - - len = strcspn(p + 2, "}"); - copy = xstrdup(p + 2); - copy[len] = '\0'; - value = GetVariable(pset.vars, copy); - if (!value) - value = ""; - length += strlen(value) - (len + 3); - new = realloc(destination, length); - if (!new) - { - psql_error("out of memory\n"); - exit(EXIT_FAILURE); - } - tmp = new + (tmp - destination); - destination = new; - - strcpy(tmp, value); - tmp += strlen(value); - p += len + 2; - free(copy); - } - else - *tmp++ = '$'; - } - else { *tmp++ = *p; @@ -865,7 +1096,6 @@ unescape(const char *source) - /* do_connect * -- handler for \connect * @@ -893,8 +1123,8 @@ do_connect(const char *new_dbname, const char *new_user) SetVariable(pset.vars, "HOST", NULL); SetVariable(pset.vars, "PORT", NULL); - /* If dbname is "-" then use old name, else new one (even if NULL) */ - if (oldconn && new_dbname && PQdb(oldconn) && strcmp(new_dbname, "-") == 0) + /* If dbname is "" then use old name, else new one (even if NULL) */ + if (oldconn && new_dbname && PQdb(oldconn) && strcmp(new_dbname, "") == 0) dbparam = PQdb(oldconn); else dbparam = new_dbname; @@ -1001,7 +1231,7 @@ do_connect(const char *new_dbname, const char *new_user) /* * Test if the given user is a database superuser. - * (Used to set up the prompt right.) + * (Is used to set up the prompt right.) */ bool test_superuser(const char * username) @@ -1037,7 +1267,7 @@ test_superuser(const char * username) static bool editFile(const char *fname) { - char *editorName; + const char *editorName; char *sys; int result; @@ -1063,7 +1293,7 @@ editFile(const char *fname) sprintf(sys, "exec %s %s", editorName, fname); result = system(sys); if (result == -1) - psql_error("could not start editor\n"); + psql_error("could not start editor %s\n", editorName); else if (result == 127) psql_error("could not start /bin/sh\n"); free(sys); @@ -1087,14 +1317,6 @@ do_edit(const char *filename_arg, PQExpBuffer query_buf) #endif -#ifdef USE_ASSERT_CHECKING - assert(query_buf); -#else - if (!query_buf) - return false; -#endif - - if (filename_arg) fname = filename_arg; @@ -1233,11 +1455,7 @@ process_file(char *filename) if (!filename) return false; -#ifdef __CYGWIN32__ - fd = fopen(filename, "rb"); -#else fd = fopen(filename, "r"); -#endif if (!fd) { @@ -1454,7 +1672,7 @@ do_pset(const char *param, const char *value, printQueryOpt * popt, bool quiet) -#define DEFAULT_SHELL "/bin/sh" +#define DEFAULT_SHELL "/bin/sh" static bool do_shell(const char *command) @@ -1464,7 +1682,7 @@ do_shell(const char *command) if (!command) { char *sys; - char *shellName; + const char *shellName; shellName = getenv("SHELL"); if (shellName == NULL) diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c index 700542cd4d1..084d8790e6f 100644 --- a/src/bin/psql/common.c +++ b/src/bin/psql/common.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/common.c,v 1.11 2000/01/29 16:58:48 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/common.c,v 1.12 2000/02/07 23:10:06 petere Exp $ */ #include <c.h> #include "common.h" @@ -24,6 +24,7 @@ #include <unistd.h> /* for write() */ #else #include <io.h> /* for _write() */ +#include <win32.h> #endif #include <libpq-fe.h> @@ -37,18 +38,10 @@ #include "prompt.h" #include "print.h" -#ifdef WIN32 -#define popen(x,y) _popen(x,y) -#define pclose(x) _pclose(x) -#define write(a,b,c) _write(a,b,c) -#endif - -/* xstrdup() - * +/* * "Safe" wrapper around strdup() - * (Using this also avoids writing #ifdef HAVE_STRDUP in every file :) */ char * xstrdup(const char *string) @@ -57,7 +50,8 @@ xstrdup(const char *string) if (!string) { - fprintf(stderr, "%s: xstrdup: cannot duplicate null pointer\n", pset.progname); + fprintf(stderr, "%s: xstrdup: cannot duplicate null pointer (internal error)\n", + pset.progname); exit(EXIT_FAILURE); } tmp = strdup(string); @@ -133,7 +127,7 @@ setQFout(const char *fname) /* * Error reporting for scripts. Errors should look like - * filename:lineno: message + * psql:filename:lineno: message * */ void @@ -152,8 +146,11 @@ psql_error(const char *fmt, ...) va_end(ap); } -/* for backend NOTICES */ + +/* + * for backend NOTICES + */ void NoticeProcessor(void * arg, const char * message) { @@ -184,7 +181,6 @@ simple_prompt(const char *prompt, int maxlen, bool echo) #ifdef HAVE_TERMIOS_H struct termios t_orig, t; - #endif destination = (char *) malloc(maxlen + 2); @@ -240,8 +236,8 @@ simple_prompt(const char *prompt, int maxlen, bool echo) * Before we start a query, we enable a SIGINT signal catcher that sends a * cancel request to the backend. Note that sending the cancel directly from * the signal handler is safe because PQrequestCancel() is written to make it - * so. We have to be very careful what else we do in the signal handler. This - * includes using write() for output. + * so. We use write() to print to stdout because it's better to use simple + * facilities in a signal handler. */ static PGconn *cancelConn; diff --git a/src/bin/psql/create_help.pl b/src/bin/psql/create_help.pl index f802d5ed791..638b96600f8 100644 --- a/src/bin/psql/create_help.pl +++ b/src/bin/psql/create_help.pl @@ -5,7 +5,7 @@ # # Copyright 2000 by PostgreSQL Global Development Group # -# $Header: /cvsroot/pgsql/src/bin/psql/create_help.pl,v 1.3 2000/01/29 16:58:48 petere Exp $ +# $Header: /cvsroot/pgsql/src/bin/psql/create_help.pl,v 1.4 2000/02/07 23:10:06 petere Exp $ ################################################################# # @@ -56,6 +56,8 @@ struct _helpStruct static struct _helpStruct QL_HELP[] = { "; +$count = 0; + foreach $file (sort readdir DIR) { my ($cmdname, $cmddesc, $cmdsynopsis); $file =~ /\.sgml$/ || next; @@ -84,6 +86,7 @@ foreach $file (sort readdir DIR) { $cmdsynopsis =~ s/\"/\\"/g; print OUT " { \"$cmdname\",\n \"$cmddesc\",\n \"$cmdsynopsis\" },\n\n"; + $count++; } else { print STDERR "$0: parsing file '$file' failed at or near line $. (N='$cmdname' D='$cmddesc')\n"; @@ -94,6 +97,10 @@ print OUT " { NULL, NULL, NULL } /* End of list marker */ }; + +#define QL_HELP_COUNT $count + + #endif /* $define */ "; diff --git a/src/bin/psql/describe.c b/src/bin/psql/describe.c index bb259b31d37..6ab7c2f519e 100644 --- a/src/bin/psql/describe.c +++ b/src/bin/psql/describe.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.15 2000/01/29 16:58:48 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/describe.c,v 1.16 2000/02/07 23:10:06 petere Exp $ */ #include <c.h> #include "describe.h" @@ -56,7 +56,7 @@ describeAggregates(const char *name) if (name) { - strcat(buf, " AND a.aggname ~* '"); + strcat(buf, " AND a.aggname ~* '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "'\n"); } @@ -71,7 +71,7 @@ describeAggregates(const char *name) if (name) { - strcat(buf, " AND a.aggname ~* '"); + strcat(buf, " AND a.aggname ~* '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "'\n"); } @@ -125,7 +125,7 @@ describeFunctions(const char *name, bool verbose) if (name) { - strcat(buf, " AND p.proname ~* '"); + strcat(buf, " AND p.proname ~* '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "'\n"); } @@ -169,7 +169,7 @@ describeTypes(const char *name, bool verbose) if (name) { - strcat(buf, " AND t.typname ~* '"); + strcat(buf, " AND t.typname ~* '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "' "); } @@ -215,7 +215,7 @@ describeOperators(const char *name) " o.oprright = t2.oid\n"); if (name) { - strcat(buf, " AND o.oprname ~ '"); + strcat(buf, " AND o.oprname ~ '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "'\n"); } @@ -233,7 +233,7 @@ describeOperators(const char *name) " o.oprright = t1.oid\n"); if (name) { - strcat(buf, "AND o.oprname ~ '"); + strcat(buf, "AND o.oprname ~ '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "'\n"); } @@ -251,7 +251,7 @@ describeOperators(const char *name) " o.oprleft = t1.oid\n"); if (name) { - strcat(buf, "AND o.oprname ~ '"); + strcat(buf, "AND o.oprname ~ '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "'\n"); } @@ -346,7 +346,7 @@ permissionsList(const char *name) " relname !~ '^pg_'\n"); if (name) { - strcat(descbuf, " AND relname ~ '"); + strcat(descbuf, " AND relname ~ '^"); strncat(descbuf, name, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -391,7 +391,7 @@ objectDescription(const char *object) "WHERE a.oid = d.objoid\n"); if (object) { - strcat(descbuf, " AND a.aggname ~* '"); + strcat(descbuf, " AND a.aggname ~* '^"); strncat(descbuf, object, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -403,7 +403,7 @@ objectDescription(const char *object) "WHERE p.oid = d.objoid AND (p.pronargs = 0 or oidvectortypes(p.proargtypes) != '')\n"); if (object) { - strcat(descbuf, " AND p.proname ~* '"); + strcat(descbuf, " AND p.proname ~* '^"); strncat(descbuf, object, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -416,7 +416,7 @@ objectDescription(const char *object) "WHERE RegprocToOid(o.oprcode) = d.objoid\n"); if (object) { - strcat(descbuf, " AND o.oprname ~ '"); + strcat(descbuf, " AND o.oprname ~ '^"); strncat(descbuf, object, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -428,7 +428,7 @@ objectDescription(const char *object) "WHERE t.oid = d.objoid\n"); if (object) { - strcat(descbuf, " AND t.typname ~* '"); + strcat(descbuf, " AND t.typname ~* '^"); strncat(descbuf, object, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -440,7 +440,7 @@ objectDescription(const char *object) "WHERE c.oid = d.objoid\n"); if (object) { - strcat(descbuf, " AND c.relname ~* '"); + strcat(descbuf, " AND c.relname ~* '^"); strncat(descbuf, object, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -452,7 +452,7 @@ objectDescription(const char *object) "WHERE r.oid = d.objoid AND r.rulename !~ '^_RET'\n"); if (object) { - strcat(descbuf, " AND r.rulename ~* '"); + strcat(descbuf, " AND r.rulename ~* '^"); strncat(descbuf, object, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -464,7 +464,7 @@ objectDescription(const char *object) "WHERE t.oid = d.objoid\n"); if (object) { - strcat(descbuf, " AND t.tgname ~* '"); + strcat(descbuf, " AND t.tgname ~* '^"); strncat(descbuf, object, REGEXP_CUTOFF); strcat(descbuf, "'\n"); } @@ -504,7 +504,7 @@ xmalloc(size_t size) tmp = malloc(size); if (!tmp) { - psql_error("out of memory"); + psql_error("out of memory\n"); exit(EXIT_FAILURE); } return tmp; @@ -626,7 +626,7 @@ describeTableDetails(const char *name, bool desc) bool isarray = false; /* Name */ - cells[i * cols + 0] = (char *)PQgetvalue(res, i, 0); /* don't free this afterwards */ + cells[i * cols + 0] = PQgetvalue(res, i, 0); /* don't free this afterwards */ /* Type */ if (attype[0] == '_') @@ -701,7 +701,7 @@ describeTableDetails(const char *name, bool desc) /* Description */ if (desc) - cells[i * cols + cols-1] = (char*)PQgetvalue(res, i, 7); + cells[i * cols + cols-1] = PQgetvalue(res, i, 7); } /* Make title */ @@ -960,7 +960,7 @@ listTables(const char *infotype, const char *name, bool desc) strcat(buf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); if (name) { - strcat(buf, " AND c.relname ~ '"); + strcat(buf, " AND c.relname ~ '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "'\n"); } @@ -976,7 +976,7 @@ listTables(const char *infotype, const char *name, bool desc) strcat(buf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); if (name) { - strcat(buf, " AND c.relname ~ '"); + strcat(buf, " AND c.relname ~ '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "'\n"); } @@ -997,7 +997,7 @@ listTables(const char *infotype, const char *name, bool desc) strcat(buf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); if (name) { - strcat(buf, " AND c.relname ~ '"); + strcat(buf, " AND c.relname ~ '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "'\n"); } @@ -1013,7 +1013,7 @@ listTables(const char *infotype, const char *name, bool desc) strcat(buf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); if (name) { - strcat(buf, " AND c.relname ~ '"); + strcat(buf, " AND c.relname ~ '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "'\n"); } @@ -1049,7 +1049,7 @@ listTables(const char *infotype, const char *name, bool desc) strcat(buf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); if (name) { - strcat(buf, " AND c.relname ~ '"); + strcat(buf, " AND c.relname ~ '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "'\n"); } @@ -1079,7 +1079,7 @@ listTables(const char *infotype, const char *name, bool desc) strcat(buf, showSystem ? " AND c.relname ~ '^pg_'\n" : " AND c.relname !~ '^pg_'\n"); if (name) { - strcat(buf, " AND c.relname ~ '"); + strcat(buf, " AND c.relname ~ '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "'\n"); } @@ -1098,7 +1098,7 @@ listTables(const char *infotype, const char *name, bool desc) "WHERE c.relowner = u.usesysid AND c.relkind = 's'\n"); if (name) { - strcat(buf, " AND c.relname ~ '"); + strcat(buf, " AND c.relname ~ '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "'\n"); } @@ -1112,7 +1112,7 @@ listTables(const char *infotype, const char *name, bool desc) " AND not exists (select 1 from pg_user where usesysid = c.relowner)"); if (name) { - strcat(buf, " AND c.relname ~ '"); + strcat(buf, " AND c.relname ~ '^"); strncat(buf, name, REGEXP_CUTOFF); strcat(buf, "'\n"); } @@ -1127,10 +1127,10 @@ listTables(const char *infotype, const char *name, bool desc) if (PQntuples(res) == 0 && !QUIET()) { - if (name) - fprintf(pset.queryFout, "No matching relations found.\n"); - else - fprintf(pset.queryFout, "No relations found.\n"); + if (name) + fprintf(pset.queryFout, "No matching relations found.\n"); + else + fprintf(pset.queryFout, "No relations found.\n"); } else { diff --git a/src/bin/psql/help.c b/src/bin/psql/help.c index 70100c7791f..261f1344203 100644 --- a/src/bin/psql/help.c +++ b/src/bin/psql/help.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/help.c,v 1.15 2000/01/29 16:58:48 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/help.c,v 1.16 2000/02/07 23:10:06 petere Exp $ */ #include <c.h> #include "help.h" @@ -11,6 +11,7 @@ #include <stdio.h> #include <stdlib.h> #include <signal.h> +#include <errno.h> #ifndef WIN32 #include <sys/ioctl.h> /* for ioctl() */ @@ -20,9 +21,7 @@ #include <sys/types.h> /* (ditto) */ #include <unistd.h> /* for getuid() */ #else -#define strcasecmp(x,y) stricmp(x,y) -#define popen(x,y) _popen(x,y) -#define pclose(x) _pclose(x) +#include <win32.h> #endif #include <pqsignal.h> @@ -48,7 +47,6 @@ usage(void) #ifndef WIN32 struct passwd *pw = NULL; - #endif /* Find default user, in case we need it. */ @@ -61,7 +59,7 @@ usage(void) user = pw->pw_name; else { - perror("getpwuid()"); + psql_error("could not get current user name: %s", strerror(errno)); exit(EXIT_FAILURE); } #else @@ -70,10 +68,11 @@ usage(void) } /* If this " is the start of the string then it ought to end there to fit in 80 columns >> " */ - puts( "This is psql, the PostgreSQL interactive terminal."); - puts( "\nUsage:"); - puts( " psql [options] [dbname [username]]"); - puts( "\nOptions:"); + puts( "This is psql, the PostgreSQL interactive terminal.\n"); + puts( "Usage:"); + puts( " psql [options] [dbname [username]]\n"); + puts( "Options:"); +/* puts( " -a Echo all input from script");*/ puts( " -A Unaligned table output mode (-P format=unaligned)"); puts( " -c <query> Run only single query (or slash command) and exit"); @@ -83,7 +82,7 @@ usage(void) env = user; printf(" -d <dbname> Specify database name to connect to (default: %s)\n", env); - puts( " -e Echo all input in non-interactive mode"); + puts( " -e Echo queries sent to backend"); puts( " -E Display queries that internal commands generate"); puts( " -f <filename> Execute queries from file, then exit"); puts( " -F <string> Set field separator (default: \"" DEFAULT_FIELD_SEP "\") (-P fieldsep=)"); @@ -99,7 +98,6 @@ usage(void) puts( " -H HTML table output mode (-P format=html)"); puts( " -l List available databases, then exit"); - puts( " -n Do not use readline or history"); puts( " -o <filename> Send query output to filename (or |pipe)"); /* Display default port */ @@ -109,6 +107,7 @@ usage(void) puts( " -P var[=arg] Set printing option 'var' to 'arg' (see \\pset command)"); puts( " -q Run quietly (no messages, only query output)"); + puts( " -R <string> Set record separator (default: newline) (-P recordsep=)"); puts( " -s Single step mode (confirm each query)"); puts( " -S Single line mode (newline terminates query)"); puts( " -t Don't print headings and row count (-P tuples_only)"); @@ -118,7 +117,7 @@ usage(void) env = getenv("PGUSER"); if (!env) env = user; - printf(" -U <username> Specifiy username, \"?\"=prompt (default user: %s)\n", env); + printf(" -U <username> Specify database username (default: %s)\n", env); puts( " -x Turn on expanded table output (-P expanded)"); puts( " -v name=val Set psql variable 'name' to 'value'"); @@ -190,10 +189,9 @@ slashUsage(void) fout = stdout; /* if you add/remove a line here, change the row test above */ - fprintf(fout, " \\? help\n"); - fprintf(fout, " \\c[onnect] [dbname|- [user|?]]\n" + fprintf(fout, " \\c[onnect] [dbname|- [user]]\n" " connect to new database (currently '%s')\n", PQdb(pset.db)); - fprintf(fout, " \\copy ... perform SQL COPY with data stream to the client machine"); + fprintf(fout, " \\copy ... perform SQL COPY with data stream to the client machine\n"); fprintf(fout, " \\copyright show PostgreSQL usage and distribution terms\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"); @@ -209,10 +207,11 @@ slashUsage(void) fprintf(fout, " \\i <fname> read and execute queries from filename\n"); fprintf(fout, " \\l list all databases\n"); fprintf(fout, " \\lo_export, \\lo_import, \\lo_list, \\lo_unlink\n" - " large object operations\n"); + " large object operations\n"); fprintf(fout, " \\o [fname] send all query results to <fname>, or |pipe\n"); fprintf(fout, " \\p show the content of the current query buffer\n"); - fprintf(fout, " \\pset [opt] set table output options\n"); + fprintf(fout, " \\pset {format|border|expanded|fieldsep|recordsep|tuples_only|title|tableattr\n" + " |pager} set table output options\n"); fprintf(fout, " \\q quit psql\n"); fprintf(fout, " \\qecho <text> write text to query output stream (see \\o)\n"); fprintf(fout, " \\r reset (clear) the query buffer\n"); @@ -243,36 +242,24 @@ slashUsage(void) void helpSQL(const char *topic) { +#define VALUE_OR_NULL(a) ((a) ? (a) : "") + if (!topic || strlen(topic) == 0) { - char left_center_right; /* Which column we're displaying */ - int i; /* Index into QL_HELP[] */ + int i; + int items_per_column = (QL_HELP_COUNT + 2)/3; puts("Available help:"); - left_center_right = 'L';/* Start with left column */ - i = 0; - while (QL_HELP[i].cmd != NULL) + for (i = 0; i < items_per_column; i++) { - switch (left_center_right) - { - case 'L': - printf(" %-25s", QL_HELP[i].cmd); - left_center_right = 'C'; - break; - case 'C': - printf("%-25s", QL_HELP[i].cmd); - left_center_right = 'R'; - break; - case 'R': - printf("%-25s\n", QL_HELP[i].cmd); - left_center_right = 'L'; - break; - } - i++; + printf(" %-25s%-25s%-25s\n", + VALUE_OR_NULL(QL_HELP[i].cmd), + VALUE_OR_NULL(QL_HELP[i + items_per_column].cmd), + VALUE_OR_NULL(QL_HELP[i + 2*items_per_column].cmd) + ); } - if (left_center_right != 'L') - puts("\n"); + putc('\n', stdout); } else @@ -286,8 +273,10 @@ helpSQL(const char *topic) strcmp(topic, "*") == 0) { help_found = true; - printf("Command: %s\nDescription: %s\nSyntax:\n%s\n\n", - QL_HELP[i].cmd, QL_HELP[i].help, QL_HELP[i].syntax); + printf("Command: %s\n" + "Description: %s\n" + "Syntax:\n%s\n\n", + QL_HELP[i].cmd, QL_HELP[i].help, QL_HELP[i].syntax); } } @@ -298,28 +287,28 @@ helpSQL(const char *topic) - void print_copyright(void) { puts( - "PostgreSQL Data Base Management System\n\n" - "Portions Copyright (c) 1996-2000, PostgreSQL, Inc\n\n" - "This software is based on Postgres95, formerly known as Postgres, which\n" - "contains the following notice:\n\n" - "Portions Copyright(c) 1994 - 7 Regents of the University of California\n\n" - "Permission to use, copy, modify, and distribute this software and its\n" - "documentation for any purpose, without fee, and without a written agreement\n" - "is hereby granted, provided that the above copyright notice and this paragraph\n" - "and the following two paragraphs appear in all copies.\n\n" - "IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR\n" - "DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST\n" - "PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF\n" - "THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH\n" - "DAMAGE.\n\n" - "THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,\n" - "BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\n" - "PARTICULAR PURPOSE.THE SOFTWARE PROVIDED HEREUNDER IS ON AN \"AS IS\" BASIS,\n" - "AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,\n" - "SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS."); + "PostgreSQL Data Base Management System\n\n" + "Portions Copyright (c) 1996-2000, PostgreSQL, Inc\n\n" + "This software is based on Postgres95, formerly known as Postgres, which\n" + "contains the following notice:\n\n" + "Portions Copyright(c) 1994 - 7 Regents of the University of California\n\n" + "Permission to use, copy, modify, and distribute this software and its\n" + "documentation for any purpose, without fee, and without a written agreement\n" + "is hereby granted, provided that the above copyright notice and this paragraph\n" + "and the following two paragraphs appear in all copies.\n\n" + "IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR\n" + "DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, INCLUDING LOST\n" + "PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF\n" + "THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH\n" + "DAMAGE.\n\n" + "THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,\n" + "BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A\n" + "PARTICULAR PURPOSE.THE SOFTWARE PROVIDED HEREUNDER IS ON AN \"AS IS\" BASIS,\n" + "AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE,\n" + "SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS." + ); } diff --git a/src/bin/psql/input.c b/src/bin/psql/input.c index 2adff9d3422..560d5b2c382 100644 --- a/src/bin/psql/input.c +++ b/src/bin/psql/input.c @@ -3,15 +3,18 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/input.c,v 1.8 2000/01/29 16:58:48 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/input.c,v 1.9 2000/02/07 23:10:06 petere Exp $ */ #include <c.h> #include "input.h" +#include <errno.h> + #include <pqexpbuffer.h> #include "settings.h" #include "tab-complete.h" +#include "common.h" /* Runtime options for turning off readline and history */ /* (of course there is no runtime command for doing that :) */ @@ -32,7 +35,7 @@ static bool useHistory; * The result is malloced. */ char * -gets_interactive(const char *prompt) +gets_interactive(char *prompt) { char *s; #ifdef USE_HISTORY @@ -42,7 +45,7 @@ gets_interactive(const char *prompt) #ifdef USE_READLINE if (useReadline) - s = readline((char *) prompt); + s = readline(prompt); else { #endif @@ -120,7 +123,6 @@ initializeInput(int flags) if (flags == 1) { useReadline = true; - rl_readline_name = "psql"; initialize_readline(); } #endif @@ -152,14 +154,14 @@ initializeInput(int flags) bool -saveHistory(const char *fname) +saveHistory(char *fname) { #ifdef USE_HISTORY - if (useHistory) + if (useHistory && fname) { - if (write_history((char *) fname) != 0) + if (write_history(fname) != 0) { - perror(fname); + psql_error("could not save history to %s: %s", fname, strerror(errno)); return false; } return true; diff --git a/src/bin/psql/input.h b/src/bin/psql/input.h index 6e078cbfbea..d12b45bfa30 100644 --- a/src/bin/psql/input.h +++ b/src/bin/psql/input.h @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/input.h,v 1.6 2000/01/29 16:58:48 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/input.h,v 1.7 2000/02/07 23:10:06 petere Exp $ */ #ifndef INPUT_H #define INPUT_H @@ -37,11 +37,11 @@ # endif #endif -char * gets_interactive(const char *prompt); +char * gets_interactive(char *prompt); char * gets_fromFile(FILE *source); void initializeInput(int flags); -bool saveHistory(const char *fname); +bool saveHistory(char *fname); void finishInput(void); #endif /* INPUT_H */ diff --git a/src/bin/psql/large_obj.c b/src/bin/psql/large_obj.c index 164956fded9..add2f77cfc1 100644 --- a/src/bin/psql/large_obj.c +++ b/src/bin/psql/large_obj.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/large_obj.c,v 1.7 2000/01/29 16:58:48 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/large_obj.c,v 1.8 2000/02/07 23:10:06 petere Exp $ */ #include <c.h> #include "large_obj.h" @@ -113,7 +113,7 @@ do_lo_export(const char *loid_arg, const char *filename_arg) PQclear(res); } - status = lo_export(pset.db, atol(loid_arg), (char *) filename_arg); + status = lo_export(pset.db, atol(loid_arg), filename_arg); if (status != 1) { /* of course this status is documented * nowhere :( */ @@ -182,7 +182,7 @@ do_lo_import(const char *filename_arg, const char *comment_arg) PQclear(res); } - loid = lo_import(pset.db, (char *) filename_arg); + loid = lo_import(pset.db, filename_arg); if (loid == InvalidOid) { fputs(PQerrorMessage(pset.db), stderr); diff --git a/src/bin/psql/mainloop.c b/src/bin/psql/mainloop.c index cf07b42d31f..325a6b5ed85 100644 --- a/src/bin/psql/mainloop.c +++ b/src/bin/psql/mainloop.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/mainloop.c,v 1.16 2000/01/24 19:34:17 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/mainloop.c,v 1.17 2000/02/07 23:10:06 petere Exp $ */ #include <c.h> #include "mainloop.h" @@ -47,10 +47,10 @@ MainLoop(FILE *source) bool xcomment; /* in extended comment */ int paren_level; unsigned int query_start; - int count_eof; + int count_eof = 0; const char *var; bool was_bslash; - unsigned int bslash_count; + unsigned int bslash_count = 0; int i, prevlen, @@ -123,6 +123,8 @@ MainLoop(FILE *source) prompt_status = PROMPT_DOUBLEQUOTE; else if (xcomment) prompt_status = PROMPT_COMMENT; + else if (paren_level) + prompt_status = PROMPT_PAREN; else if (query_buf->len > 0) prompt_status = PROMPT_CONTINUE; else @@ -251,7 +253,7 @@ MainLoop(FILE *source) } /* start of quote */ - else if (line[i] == '\'' || line[i] == '"') + else if (!was_bslash && (line[i] == '\'' || line[i] == '"')) in_quote = line[i]; /* in extended comment? */ diff --git a/src/bin/psql/print.c b/src/bin/psql/print.c index 3ff0d27c227..44faad5850f 100644 --- a/src/bin/psql/print.c +++ b/src/bin/psql/print.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/print.c,v 1.9 2000/01/29 16:58:49 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/print.c,v 1.10 2000/02/07 23:10:06 petere Exp $ */ #include <c.h> #include "print.h" @@ -266,7 +266,9 @@ print_aligned_text(const char *title, const char * const * headers, for (i = 0; i < col_count; i++) { /* centered */ - fprintf(fout, "%-*s%s%-*s", (int) floor((widths[i] - strlen(headers[i])) / 2.0), "", headers[i], (int) ceil((widths[i] - strlen(headers[i])) / 2.0), ""); + fprintf(fout, "%-*s%s%-*s", + (int) floor((widths[i] - strlen(headers[i])) / 2.0), "", + headers[i], (int) ceil((widths[i] - strlen(headers[i])) / 2.0), ""); if (i < col_count - 1) { diff --git a/src/bin/psql/prompt.c b/src/bin/psql/prompt.c index a06a712e401..1149fd65abc 100644 --- a/src/bin/psql/prompt.c +++ b/src/bin/psql/prompt.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/prompt.c,v 1.7 2000/01/29 16:58:49 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/prompt.c,v 1.8 2000/02/07 23:10:06 petere Exp $ */ #include <c.h> #include "prompt.h" @@ -19,8 +19,8 @@ #include "variables.h" #ifdef WIN32 -#define popen(x,y) _popen(x,y) -#define pclose(x) _pclose(x) +#include <io.h> +#include <win32.h> #endif @@ -53,14 +53,14 @@ * * %`command` - The result of executing command in /bin/sh with trailing * newline stripped. - * %$name$ - The value of the psql variable 'name' + * %:name: - The value of the psql variable 'name' * (those will not be rescanned for more escape sequences!) * * If the application-wide prompts became NULL somehow, the returned string * will be empty (not NULL!). *-------------------------- */ -const char * +char * get_prompt(promptStatus_t status) { #define MAX_PROMPT_SIZE 256 @@ -72,7 +72,7 @@ get_prompt(promptStatus_t status) if (status == PROMPT_READY) prompt_string = GetVariable(pset.vars, "PROMPT1"); - else if (status == PROMPT_CONTINUE || status == PROMPT_SINGLEQUOTE || status == PROMPT_DOUBLEQUOTE || status == PROMPT_COMMENT) + else if (status == PROMPT_CONTINUE || status == PROMPT_SINGLEQUOTE || status == PROMPT_DOUBLEQUOTE || status == PROMPT_COMMENT || status == PROMPT_PAREN) prompt_string = GetVariable(pset.vars, "PROMPT2"); else if (status == PROMPT_COPY) prompt_string = GetVariable(pset.vars, "PROMPT3"); @@ -183,6 +183,9 @@ get_prompt(promptStatus_t status) case PROMPT_COMMENT: buf[0] = '*'; break; + case PROMPT_PAREN: + buf[0] = '('; + break; default: buf[0] = '\0'; break; @@ -226,14 +229,14 @@ get_prompt(promptStatus_t status) } /* interpolate variable */ - case '$': + case ':': { char *name; const char *val; int nameend; name = strdup(p + 1); - nameend = strcspn(name, "$"); + nameend = strcspn(name, ":"); name[nameend] = '\0'; val = GetVariable(pset.vars, name); if (val) diff --git a/src/bin/psql/prompt.h b/src/bin/psql/prompt.h index f70a334a54d..61b4b9f543a 100644 --- a/src/bin/psql/prompt.h +++ b/src/bin/psql/prompt.h @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/prompt.h,v 1.5 2000/01/29 16:58:49 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/prompt.h,v 1.6 2000/02/07 23:10:06 petere Exp $ */ #ifndef PROMPT_H #define PROMPT_H @@ -17,9 +17,10 @@ typedef enum _promptStatus PROMPT_COMMENT, PROMPT_SINGLEQUOTE, PROMPT_DOUBLEQUOTE, + PROMPT_PAREN, PROMPT_COPY } promptStatus_t; -const char *get_prompt(promptStatus_t status); +char *get_prompt(promptStatus_t status); #endif /* PROMPT_H */ diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c index 478356deb55..fa6e9fbf202 100644 --- a/src/bin/psql/startup.c +++ b/src/bin/psql/startup.c @@ -3,53 +3,48 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/startup.c,v 1.21 2000/02/05 12:27:56 ishii Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/startup.c,v 1.22 2000/02/07 23:10:06 petere Exp $ */ #include <c.h> -#include <signal.h> -#include <errno.h> #include <sys/types.h> #include <stdlib.h> #include <string.h> #include <stdio.h> -#include <assert.h> -#ifdef WIN32 +#ifndef WIN32 +#include <unistd.h> +#else /* WIN32 */ #include <io.h> #include <windows.h> -#else -#include <unistd.h> -#endif +#include <win32.h> +#endif /* WIN32 */ #ifdef HAVE_GETOPT_H #include <getopt.h> #endif #include <libpq-fe.h> -#include <pqsignal.h> #include <version.h> -#include "settings.h" #include "command.h" -#include "help.h" -#include "mainloop.h" #include "common.h" +#include "describe.h" +#include "help.h" #include "input.h" -#include "variables.h" +#include "mainloop.h" #include "print.h" -#include "describe.h" +#include "settings.h" +#include "variables.h" +/* + * Global psql options + */ PsqlSettings pset; -static void -process_psqlrc(void); -static void -showVersion(void); - - -/* Structures to pass information between the option parsing routine +/* + * Structures to pass information between the option parsing routine * and the main function */ enum _actions @@ -75,15 +70,21 @@ struct adhoc_opts static void parse_options(int argc, char *argv[], struct adhoc_opts * options); +static void +process_psqlrc(void); + +static void +showVersion(void); + /* * - * main() + * main * */ int -main(int argc, char **argv) +main(int argc, char *argv[]) { struct adhoc_opts options; int successResult; @@ -92,8 +93,6 @@ main(int argc, char **argv) char *password = NULL; bool need_pass; - memset(&pset, 0, sizeof pset); - if (!strrchr(argv[0], SEP_CHAR)) pset.progname = argv[0]; else @@ -104,21 +103,21 @@ main(int argc, char **argv) pset.encoding = PQenv2encoding(); pset.vars = CreateVariableSpace(); + if (!pset.vars) + { + fprintf(stderr, "%s: out of memory\n", pset.progname); + exit(EXIT_FAILURE); + } pset.popt.topt.format = PRINT_ALIGNED; pset.queryFout = stdout; - pset.popt.topt.fieldSep = xstrdup(DEFAULT_FIELD_SEP); - pset.popt.topt.recordSep = xstrdup(DEFAULT_RECORD_SEP); pset.popt.topt.border = 1; pset.popt.topt.pager = true; - SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1); - SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2); - SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3); SetVariable(pset.vars, "VERSION", PG_VERSION_STR); pset.notty = (!isatty(fileno(stdin)) || !isatty(fileno(stdout))); - /* This is obsolete and will be removed very soon. */ + /* This is obsolete and should be removed sometime. */ #ifdef PSQL_ALWAYS_GET_PASSWORDS pset.getPassword = true; #else @@ -127,11 +126,18 @@ main(int argc, char **argv) parse_options(argc, argv, &options); - if (options.action == ACT_LIST_DB) - options.dbname = "template1"; + if (!pset.popt.topt.fieldSep) + pset.popt.topt.fieldSep = xstrdup(DEFAULT_FIELD_SEP); + if (!pset.popt.topt.recordSep) + pset.popt.topt.recordSep = xstrdup(DEFAULT_RECORD_SEP); if (options.username) { + /* + * The \001 is a hack to support the deprecated -u option which issues + * a username prompt. The recommended option is -U followed by the name + * on the command line. + */ if (strcmp(options.username, "\001") == 0) username = simple_prompt("Username: ", 100, true); else @@ -145,7 +151,9 @@ main(int argc, char **argv) do { need_pass = false; - pset.db = PQsetdbLogin(options.host, options.port, NULL, NULL, options.dbname, username, password); + pset.db = PQsetdbLogin(options.host, options.port, NULL, NULL, + options.action == ACT_LIST_DB ? "template1" : options.dbname, + username, password); if (PQstatus(pset.db) == CONNECTION_BAD && strcmp(PQerrorMessage(pset.db), "fe_sendauth: no password supplied\n") == 0) @@ -162,8 +170,7 @@ main(int argc, char **argv) if (PQstatus(pset.db) == CONNECTION_BAD) { - fprintf(stderr, "%s: %s", - pset.progname, PQerrorMessage(pset.db)); + fprintf(stderr, "%s: %s", pset.progname, PQerrorMessage(pset.db)); PQfinish(pset.db); exit(EXIT_BADCONN); } @@ -188,44 +195,62 @@ main(int argc, char **argv) SetVariable(pset.vars, "HOST", PQhost(pset.db)); SetVariable(pset.vars, "PORT", PQport(pset.db)); - pset.issuper = test_superuser(PQuser(pset.db)); - - if (!QUIET() && !pset.notty && !options.action) - { - printf("Welcome to %s, the PostgreSQL interactive terminal.\n\n" - "Type: \\copyright for distribution terms\n" - " \\h for help with SQL commands\n" - " \\? for help on internal slash commands\n" - " \\g or terminate with semicolon to execute query\n" - " \\q to quit\n", pset.progname); - } - - /* Now find something to do */ + /* + * Now find something to do + */ - /* process file given by -f */ + /* + * process file given by -f + */ if (options.action == ACT_FILE) successResult = process_file(options.action_string) ? 0 : 1; - /* process slash command if one was given to -c */ + /* + * process slash command if one was given to -c + */ else if (options.action == ACT_SINGLE_SLASH) { - if (GetVariable(pset.vars, "ECHO") && strcmp(GetVariable(pset.vars, "ECHO"), "full")==0) + const char * value; + + if ((value = GetVariable(pset.vars, "ECHO")) && strcmp(value, "full")==0) puts(options.action_string); successResult = HandleSlashCmds(options.action_string, NULL, NULL) != CMD_ERROR ? 0 : 1; } - /* If the query given to -c was a normal one, send it */ + /* + * If the query given to -c was a normal one, send it + */ else if (options.action == ACT_SINGLE_QUERY) { - if (GetVariable(pset.vars, "ECHO") && strcmp(GetVariable(pset.vars, "ECHO"), "full")==0) + const char * value; + + if ((value = GetVariable(pset.vars, "ECHO")) && strcmp(value, "full")==0) puts(options.action_string); - successResult = SendQuery( options.action_string) ? 0 : 1; + successResult = SendQuery(options.action_string) ? 0 : 1; } - /* or otherwise enter interactive main loop */ + /* + * or otherwise enter interactive main loop + */ else { + pset.issuper = test_superuser(PQuser(pset.db)); + if (!QUIET() && !pset.notty) + { + printf("Welcome to %s, the PostgreSQL interactive terminal.\n\n" + "Type: \\copyright for distribution terms\n" + " \\h for help with SQL commands\n" + " \\? for help on internal slash commands\n" + " \\g or terminate with semicolon to execute query\n" + " \\q to quit\n\n", pset.progname); + } + + SetVariable(pset.vars, "PROMPT1", DEFAULT_PROMPT1); + SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2); + SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3); process_psqlrc(); - initializeInput(options.no_readline ? 0 : 1); + if (!pset.notty) + initializeInput(options.no_readline ? 0 : 1); successResult = MainLoop(stdin); - finishInput(); + if (!pset.notty) + finishInput(); } /* clean up */ @@ -253,7 +278,8 @@ static void parse_options(int argc, char *argv[], struct adhoc_opts * options) { #ifdef HAVE_GETOPT_LONG - static struct option long_options[] = { + static struct option long_options[] = + { {"no-align", no_argument, NULL, 'A'}, {"command", required_argument, NULL, 'c'}, {"dbname", required_argument, NULL, 'd'}, @@ -269,6 +295,7 @@ parse_options(int argc, char *argv[], struct adhoc_opts * options) {"port", required_argument, NULL, 'p'}, {"pset", required_argument, NULL, 'P'}, {"quiet", no_argument, NULL, 'q'}, + {"record-separator", required_argument, NULL, 'R'}, {"single-step", no_argument, NULL, 's'}, {"single-line", no_argument, NULL, 'S'}, {"tuples-only", no_argument, NULL, 't'}, @@ -283,7 +310,7 @@ parse_options(int argc, char *argv[], struct adhoc_opts * options) }; int optindex; -#endif +#endif /* HAVE_GETOPT_LONG */ extern char *optarg; extern int optind; @@ -293,15 +320,15 @@ parse_options(int argc, char *argv[], struct adhoc_opts * options) memset(options, 0, sizeof *options); #ifdef HAVE_GETOPT_LONG - while ((c = getopt_long(argc, argv, "Ac:d:eEf:F:lh:Hno:p:P:qsStT:uU:v:VWx?", long_options, &optindex)) != -1) -#else + while ((c = getopt_long(argc, argv, "Ac:d:eEf:F:lh:Hno:p:P:qRsStT:uU:v:VWx?", long_options, &optindex)) != -1) +#else /* not HAVE_GETOPT_LONG */ /* * Be sure to leave the '-' in here, so we can catch accidental long * options. */ - while ((c = getopt(argc, argv, "Ac:d:eEf:F:lh:Hno:p:P:qsStT:uU:v:VWx?-")) != -1) -#endif + while ((c = getopt(argc, argv, "Ac:d:eEf:F:lh:Hno:p:P:qRsStT:uU:v:VWx?-")) != -1) +#endif /* not HAVE_GETOPT_LONG */ { switch (c) { @@ -329,7 +356,7 @@ parse_options(int argc, char *argv[], struct adhoc_opts * options) options->action_string = optarg; break; case 'F': - pset.popt.topt.fieldSep = strdup(optarg); + pset.popt.topt.fieldSep = xstrdup(optarg); break; case 'h': options->host = optarg; @@ -377,6 +404,9 @@ parse_options(int argc, char *argv[], struct adhoc_opts * options) case 'q': SetVariable(pset.vars, "QUIET", ""); break; + case 'R': + pset.popt.topt.recordSep = xstrdup(optarg); + break; case 's': SetVariable(pset.vars, "SINGLESTEP", ""); break; @@ -507,7 +537,7 @@ process_psqlrc(void) if (home) { - psqlrc = (char *) malloc(strlen(home) + 20); + psqlrc = malloc(strlen(home) + 20); if (!psqlrc) { fprintf(stderr, "%s: out of memory\n", pset.progname); @@ -570,7 +600,7 @@ showVersion(void) #endif puts("Portions Copyright (c) 1996-2000, PostgreSQL, Inc"); - puts("Portions Copyright (C) 1996 Regents of the University of California"); + puts("Portions Copyright (c) 1996 Regents of the University of California"); puts("Read the file COPYRIGHT or use the command \\copyright to see the"); puts("usage and distribution terms."); } diff --git a/src/bin/psql/stringutils.c b/src/bin/psql/stringutils.c index 5ba48dc454c..a0d3769fd23 100644 --- a/src/bin/psql/stringutils.c +++ b/src/bin/psql/stringutils.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/stringutils.c,v 1.23 2000/01/29 16:58:49 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/stringutils.c,v 1.24 2000/02/07 23:10:06 petere Exp $ */ #include <c.h> #include "stringutils.h" @@ -12,19 +12,13 @@ #include <stdlib.h> #include <string.h> #include <assert.h> - #include <stdio.h> -#include <postgres.h> -#ifndef HAVE_STRDUP -#include <strdup.h> -#endif #include <libpq-fe.h> -static void - unescape_quotes(char *source, char quote, char escape); +static void unescape_quotes(char *source, int quote, int escape); /* @@ -45,7 +39,7 @@ char * strtokx(const char *s, const char *delim, const char *quote, - char escape, + int escape, char *was_quoted, unsigned int *token_pos, int encoding) @@ -60,6 +54,10 @@ strtokx(const char *s, char *start; char *cp = NULL; +#ifndef MULTIBYTE + (void)encoding; /*not used*/ +#endif + if (s) { free(storage); @@ -160,7 +158,7 @@ strtokx(const char *s, * Resolves escaped quotes. Used by strtokx above. */ static void -unescape_quotes(char *source, char quote, char escape) +unescape_quotes(char *source, int quote, int escape) { char *p; char *destination, @@ -170,7 +168,7 @@ unescape_quotes(char *source, char quote, char escape) assert(source); #endif - destination = (char *) calloc(1, strlen(source) + 1); + destination = calloc(1, strlen(source) + 1); if (!destination) { perror("calloc"); diff --git a/src/bin/psql/stringutils.h b/src/bin/psql/stringutils.h index 914871d94eb..4201fd53001 100644 --- a/src/bin/psql/stringutils.h +++ b/src/bin/psql/stringutils.h @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/stringutils.h,v 1.13 2000/01/29 16:58:49 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/stringutils.h,v 1.14 2000/02/07 23:10:07 petere Exp $ */ #ifndef STRINGUTILS_H #define STRINGUTILS_H @@ -13,7 +13,7 @@ extern char *strtokx(const char *s, const char *delim, const char *quote, - char escape, + int escape, char *was_quoted, unsigned int *token_pos, int encoding); diff --git a/src/bin/psql/tab-complete.c b/src/bin/psql/tab-complete.c index 92112cead07..50a15fc7d0b 100644 --- a/src/bin/psql/tab-complete.c +++ b/src/bin/psql/tab-complete.c @@ -3,7 +3,7 @@ * * Copyright 2000 by PostgreSQL Global Development Group * - * $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.9 2000/01/29 16:58:49 petere Exp $ + * $Header: /cvsroot/pgsql/src/bin/psql/tab-complete.c,v 1.10 2000/02/07 23:10:07 petere Exp $ */ /*----------- @@ -121,9 +121,7 @@ pgsql_thing_t words_after_create[] = { { "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. */ + { "VIEW", "SELECT viewname FROM pg_views WHERE substr(viewname,1,%d)='%s'" }, { NULL, NULL } /* end of list */ }; diff --git a/src/interfaces/libpq/fe-auth.c b/src/interfaces/libpq/fe-auth.c index 96589497d30..7a3bff45e92 100644 --- a/src/interfaces/libpq/fe-auth.c +++ b/src/interfaces/libpq/fe-auth.c @@ -10,7 +10,7 @@ * exceed INITIAL_EXPBUFFER_SIZE (currently 256 bytes). * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.36 2000/01/26 05:58:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-auth.c,v 1.37 2000/02/07 23:10:08 petere Exp $ * *------------------------------------------------------------------------- */ @@ -474,6 +474,10 @@ int fe_sendauth(AuthRequest areq, PGconn *conn, const char *hostname, const char *password, char *PQerrormsg) { +#if !defined(KRB4) && !defined(KRB5) + (void)hostname; /*not used*/ +#endif + switch (areq) { case AUTH_REQ_OK: diff --git a/src/interfaces/libpq/fe-connect.c b/src/interfaces/libpq/fe-connect.c index 92d32917ef2..94bf6bf0b86 100644 --- a/src/interfaces/libpq/fe-connect.c +++ b/src/interfaces/libpq/fe-connect.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.117 2000/02/05 12:33:22 ishii Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-connect.c,v 1.118 2000/02/07 23:10:09 petere Exp $ * *------------------------------------------------------------------------- */ @@ -2291,7 +2291,7 @@ conninfo_free() } /* =========== accessor functions for PGconn ========= */ -const char * +char * PQdb(const PGconn *conn) { if (!conn) @@ -2299,7 +2299,7 @@ PQdb(const PGconn *conn) return conn->dbName; } -const char * +char * PQuser(const PGconn *conn) { if (!conn) @@ -2307,7 +2307,7 @@ PQuser(const PGconn *conn) return conn->pguser; } -const char * +char * PQpass(const PGconn *conn) { if (!conn) @@ -2315,7 +2315,7 @@ PQpass(const PGconn *conn) return conn->pgpass; } -const char * +char * PQhost(const PGconn *conn) { if (!conn) @@ -2323,7 +2323,7 @@ PQhost(const PGconn *conn) return conn->pghost; } -const char * +char * PQport(const PGconn *conn) { if (!conn) @@ -2331,7 +2331,7 @@ PQport(const PGconn *conn) return conn->pgport; } -const char * +char * PQtty(const PGconn *conn) { if (!conn) @@ -2339,7 +2339,7 @@ PQtty(const PGconn *conn) return conn->pgtty; } -const char * +char * PQoptions(const PGconn *conn) { if (!conn) @@ -2355,7 +2355,7 @@ PQstatus(const PGconn *conn) return conn->status; } -const char * +char * PQerrorMessage(const PGconn *conn) { static char noConn[] = "PQerrorMessage: conn pointer is NULL\n"; @@ -2478,6 +2478,7 @@ PQsetNoticeProcessor(PGconn *conn, PQnoticeProcessor proc, void *arg) static void defaultNoticeProcessor(void *arg, const char *message) { + (void)arg; /*not used*/ /* Note: we expect the supplied string to end with a newline already. */ fprintf(stderr, "%s", message); } diff --git a/src/interfaces/libpq/fe-exec.c b/src/interfaces/libpq/fe-exec.c index 406ab499030..8bba82e788e 100644 --- a/src/interfaces/libpq/fe-exec.c +++ b/src/interfaces/libpq/fe-exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.89 2000/01/26 05:58:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-exec.c,v 1.90 2000/02/07 23:10:10 petere Exp $ * *------------------------------------------------------------------------- */ @@ -27,7 +27,7 @@ #endif /* keep this in same order as ExecStatusType in libpq-fe.h */ -const char *const pgresStatus[] = { +char * const pgresStatus[] = { "PGRES_EMPTY_QUERY", "PGRES_COMMAND_OK", "PGRES_TUPLES_OK", @@ -1760,15 +1760,15 @@ PQresultStatus(const PGresult *res) return res->resultStatus; } -const char * +char * PQresStatus(ExecStatusType status) { - if ((int)status < 0 || (size_t)status >= sizeof pgresStatus / sizeof pgresStatus[0]) + if (status < 0 || status >= sizeof pgresStatus / sizeof pgresStatus[0]) return "Invalid ExecStatusType code"; return pgresStatus[status]; } -const char * +char * PQresultErrorMessage(const PGresult *res) { if (!res || !res->errMsg) @@ -1862,7 +1862,7 @@ check_tuple_field_number(const char *routineName, const PGresult *res, /* returns NULL if the field_num is invalid */ -const char * +char * PQfname(const PGresult *res, int field_num) { if (!check_field_number("PQfname", res, field_num)) @@ -1947,8 +1947,8 @@ PQfmod(const PGresult *res, int field_num) return 0; } -const char * -PQcmdStatus(const PGresult *res) +char * +PQcmdStatus(PGresult *res) { if (!res) return NULL; @@ -1960,7 +1960,7 @@ PQcmdStatus(const PGresult *res) if the last command was an INSERT, return the oid string if not, return "" */ -const char * +char * PQoidStatus(const PGresult *res) { /* @@ -2011,8 +2011,8 @@ PQoidValue(const PGresult *res) if the last command was an INSERT/UPDATE/DELETE, return number of inserted/affected tuples, if not, return "" */ -const char * -PQcmdTuples(const PGresult *res) +char * +PQcmdTuples(PGresult *res) { char noticeBuf[128]; @@ -2023,7 +2023,7 @@ PQcmdTuples(const PGresult *res) strncmp(res->cmdStatus, "DELETE", 6) == 0 || strncmp(res->cmdStatus, "UPDATE", 6) == 0) { - const char *p = res->cmdStatus + 6; + char *p = res->cmdStatus + 6; if (*p == 0) { @@ -2067,7 +2067,7 @@ PQcmdTuples(const PGresult *res) if res is not binary, a null-terminated ASCII string is returned. */ -const char * +char * PQgetvalue(const PGresult *res, int tup_num, int field_num) { if (!check_tuple_field_number("PQgetvalue", res, tup_num, field_num)) diff --git a/src/interfaces/libpq/fe-lobj.c b/src/interfaces/libpq/fe-lobj.c index e31b66a28b3..6a7fd71f440 100644 --- a/src/interfaces/libpq/fe-lobj.c +++ b/src/interfaces/libpq/fe-lobj.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-lobj.c,v 1.27 2000/01/26 05:58:45 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-lobj.c,v 1.28 2000/02/07 23:10:11 petere Exp $ * *------------------------------------------------------------------------- */ @@ -168,7 +168,7 @@ lo_read(PGconn *conn, int fd, char *buf, size_t len) * */ int -lo_write(PGconn *conn, int fd, const char *buf, size_t len) +lo_write(PGconn *conn, int fd, char *buf, size_t len) { PQArgBlock argv[2]; PGresult *res; diff --git a/src/interfaces/libpq/fe-misc.c b/src/interfaces/libpq/fe-misc.c index ff6acefea8f..d171c457d96 100644 --- a/src/interfaces/libpq/fe-misc.c +++ b/src/interfaces/libpq/fe-misc.c @@ -25,7 +25,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.38 2000/01/29 16:58:51 petere Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-misc.c,v 1.39 2000/02/07 23:10:11 petere Exp $ * *------------------------------------------------------------------------- */ @@ -782,6 +782,8 @@ PQenv2encoding(void) int PQmblen(const unsigned char *s, int encoding) { + (void)s; + (void)encoding; return 1; } int diff --git a/src/interfaces/libpq/fe-print.c b/src/interfaces/libpq/fe-print.c index e54167cc1fe..3b9102d4d84 100644 --- a/src/interfaces/libpq/fe-print.c +++ b/src/interfaces/libpq/fe-print.c @@ -10,7 +10,7 @@ * didn't really belong there. * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-print.c,v 1.34 2000/02/05 12:33:22 ishii Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/fe-print.c,v 1.35 2000/02/07 23:10:11 petere Exp $ * *------------------------------------------------------------------------- */ @@ -59,7 +59,7 @@ static char *do_header(FILE *fout, const PQprintOpt *po, const int nFields, static void output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields, unsigned char *fieldNotNum, int *fieldMax, char *border, const int row_index); - +static void fill(int length, int max, char filler, FILE *fp); /* * PQprint() @@ -440,7 +440,6 @@ do_header(FILE *fout, const PQprintOpt *po, const int nFields, int *fieldMax, fputs("<tr>", fout); else { - int j; /* for loop index */ int tot = 0; int n = 0; char *p = NULL; @@ -557,7 +556,6 @@ output_row(FILE *fout, const PQprintOpt *po, const int nFields, char **fields, -#if 0 /* * really old printing routines */ @@ -728,4 +726,17 @@ PQprintTuples(const PGresult *res, } } } -#endif + + + +/* simply send out max-length number of filler characters to fp */ + +static void +fill(int length, int max, char filler, FILE *fp) +{ + int count; + + count = max - length; + while (count-- >= 0) + putc(filler, fp); +} diff --git a/src/interfaces/libpq/libpq-fe.h b/src/interfaces/libpq/libpq-fe.h index 8acb622ad16..66437a903fd 100644 --- a/src/interfaces/libpq/libpq-fe.h +++ b/src/interfaces/libpq/libpq-fe.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-fe.h,v 1.59 2000/02/05 12:33:22 ishii Exp $ + * $Id: libpq-fe.h,v 1.60 2000/02/07 23:10:11 petere Exp $ * *------------------------------------------------------------------------- */ @@ -74,11 +74,6 @@ extern "C" PGRES_FATAL_ERROR } ExecStatusType; -/* String descriptions of the ExecStatusTypes. - * NB: direct use of this array is now deprecated; call PQresStatus() instead. - */ - extern const char *const pgresStatus[]; - /* PGconn encapsulates a connection to the backend. * The contents of this struct are not supposed to be known to applications. */ @@ -115,16 +110,17 @@ extern "C" typedef void (*PQnoticeProcessor) (void *arg, const char *message); /* Print options for PQprint() */ + typedef char pqbool; typedef struct _PQprintOpt { - int header; /* print output field headings and row + pqbool header; /* print output field headings and row * count */ - int align; /* fill align the fields */ - int standard; /* old brain dead format */ - int html3; /* output html tables */ - int expanded; /* expand tables */ - int pager; /* use pager for output if needed */ + pqbool align; /* fill align the fields */ + pqbool standard; /* old brain dead format */ + pqbool html3; /* output html tables */ + pqbool expanded; /* expand tables */ + pqbool pager; /* use pager for output if needed */ char *fieldSep; /* field separator */ char *tableOpt; /* insert to HTML <table ...> */ char *caption; /* HTML <caption> */ @@ -207,15 +203,15 @@ extern "C" extern int PQrequestCancel(PGconn *conn); /* Accessor functions for PGconn objects */ - extern const char *PQdb(const PGconn *conn); - extern const char *PQuser(const PGconn *conn); - extern const char *PQpass(const PGconn *conn); - extern const char *PQhost(const PGconn *conn); - extern const char *PQport(const PGconn *conn); - extern const char *PQtty(const PGconn *conn); - extern const char *PQoptions(const PGconn *conn); + extern char *PQdb(const PGconn *conn); + extern char *PQuser(const PGconn *conn); + extern char *PQpass(const PGconn *conn); + extern char *PQhost(const PGconn *conn); + extern char *PQport(const PGconn *conn); + extern char *PQtty(const PGconn *conn); + extern char *PQoptions(const PGconn *conn); extern ConnStatusType PQstatus(const PGconn *conn); - extern const char *PQerrorMessage(const PGconn *conn); + extern char *PQerrorMessage(const PGconn *conn); extern int PQsocket(const PGconn *conn); extern int PQbackendPID(const PGconn *conn); extern int PQclientEncoding(const PGconn *conn); @@ -279,21 +275,21 @@ extern "C" /* Accessor functions for PGresult objects */ extern ExecStatusType PQresultStatus(const PGresult *res); - extern const char *PQresStatus(ExecStatusType status); - extern const char *PQresultErrorMessage(const PGresult *res); + extern char *PQresStatus(ExecStatusType status); + extern char *PQresultErrorMessage(const PGresult *res); extern int PQntuples(const PGresult *res); extern int PQnfields(const PGresult *res); extern int PQbinaryTuples(const PGresult *res); - extern const char *PQfname(const PGresult *res, int field_num); + extern char *PQfname(const PGresult *res, int field_num); extern int PQfnumber(const PGresult *res, const char *field_name); extern Oid PQftype(const PGresult *res, int field_num); extern int PQfsize(const PGresult *res, int field_num); extern int PQfmod(const PGresult *res, int field_num); - extern const char *PQcmdStatus(const PGresult *res); - extern const char *PQoidStatus(const PGresult *res); /* old and ugly */ + extern char *PQcmdStatus(PGresult *res); + extern char *PQoidStatus(const PGresult *res); /* old and ugly */ extern Oid PQoidValue(const PGresult *res); /* new and improved */ - extern const char *PQcmdTuples(const PGresult *res); - extern const char *PQgetvalue(const PGresult *res, int tup_num, int field_num); + extern char *PQcmdTuples(PGresult *res); + extern char *PQgetvalue(const PGresult *res, int tup_num, int field_num); extern int PQgetlength(const PGresult *res, int tup_num, int field_num); extern int PQgetisnull(const PGresult *res, int tup_num, int field_num); @@ -313,7 +309,6 @@ extern "C" const PGresult *res, const PQprintOpt *ps); /* option structure */ -#if 0 /* * really old printing routines */ @@ -330,7 +325,7 @@ extern "C" int terseOutput, /* delimiter bars */ int width); /* width of column, if * 0, use variable width */ -#endif + /* === in fe-lobj.c === */ @@ -338,7 +333,7 @@ extern "C" extern int lo_open(PGconn *conn, Oid lobjId, int mode); extern int lo_close(PGconn *conn, int fd); extern int lo_read(PGconn *conn, int fd, char *buf, size_t len); - extern int lo_write(PGconn *conn, int fd, const char *buf, size_t len); + extern int lo_write(PGconn *conn, int fd, char *buf, size_t len); extern int lo_lseek(PGconn *conn, int fd, int offset, int whence); extern Oid lo_creat(PGconn *conn, int mode); extern int lo_tell(PGconn *conn, int fd); diff --git a/src/interfaces/libpq/libpq-int.h b/src/interfaces/libpq/libpq-int.h index bb6f193bfe8..16555d98852 100644 --- a/src/interfaces/libpq/libpq-int.h +++ b/src/interfaces/libpq/libpq-int.h @@ -12,7 +12,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: libpq-int.h,v 1.18 2000/01/26 05:58:46 momjian Exp $ + * $Id: libpq-int.h,v 1.19 2000/02/07 23:10:11 petere Exp $ * *------------------------------------------------------------------------- */ @@ -244,6 +244,11 @@ struct pg_conn int client_encoding; /* encoding id */ }; +/* String descriptions of the ExecStatusTypes. + * direct use of this array is deprecated; call PQresStatus() instead. + */ +extern char *const pgresStatus[]; + /* ---------------- * Internal functions of libpq * Functions declared here need to be visible across files of libpq, diff --git a/src/interfaces/libpq/pqexpbuffer.c b/src/interfaces/libpq/pqexpbuffer.c index dc006e55c8e..f70d9139c2e 100644 --- a/src/interfaces/libpq/pqexpbuffer.c +++ b/src/interfaces/libpq/pqexpbuffer.c @@ -17,7 +17,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Header: /cvsroot/pgsql/src/interfaces/libpq/pqexpbuffer.c,v 1.4 2000/01/26 05:58:46 momjian Exp $ + * $Header: /cvsroot/pgsql/src/interfaces/libpq/pqexpbuffer.c,v 1.5 2000/02/07 23:10:11 petere Exp $ * *------------------------------------------------------------------------- */ @@ -124,9 +124,9 @@ resetPQExpBuffer(PQExpBuffer str) * Returns 1 if OK, 0 if failed to enlarge buffer. */ int -enlargePQExpBuffer(PQExpBuffer str, int needed) +enlargePQExpBuffer(PQExpBuffer str, size_t needed) { - int newlen; + size_t newlen; char *newdata; needed += str->len + 1; /* total space required now */ @@ -164,8 +164,8 @@ void printfPQExpBuffer(PQExpBuffer str, const char *fmt,...) { va_list args; - int avail, - nprinted; + size_t avail; + int nprinted; resetPQExpBuffer(str); @@ -214,8 +214,8 @@ void appendPQExpBuffer(PQExpBuffer str, const char *fmt,...) { va_list args; - int avail, - nprinted; + size_t avail; + int nprinted; for (;;) { @@ -286,7 +286,7 @@ appendPQExpBufferChar(PQExpBuffer str, char ch) * if necessary. */ void -appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, int datalen) +appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen) { /* Make more room if needed */ if (! enlargePQExpBuffer(str, datalen)) diff --git a/src/interfaces/libpq/pqexpbuffer.h b/src/interfaces/libpq/pqexpbuffer.h index b70170d61a5..014254465aa 100644 --- a/src/interfaces/libpq/pqexpbuffer.h +++ b/src/interfaces/libpq/pqexpbuffer.h @@ -18,7 +18,7 @@ * Portions Copyright (c) 1996-2000, PostgreSQL, Inc * Portions Copyright (c) 1994, Regents of the University of California * - * $Id: pqexpbuffer.h,v 1.3 2000/01/26 05:58:46 momjian Exp $ + * $Id: pqexpbuffer.h,v 1.4 2000/02/07 23:10:11 petere Exp $ * *------------------------------------------------------------------------- */ @@ -40,8 +40,8 @@ typedef struct PQExpBufferData { char *data; - int len; - int maxlen; + size_t len; + size_t maxlen; } PQExpBufferData; typedef PQExpBufferData *PQExpBuffer; @@ -113,7 +113,7 @@ extern void resetPQExpBuffer(PQExpBuffer str); * * Returns 1 if OK, 0 if failed to enlarge buffer. */ -extern int enlargePQExpBuffer(PQExpBuffer str, int needed); +extern int enlargePQExpBuffer(PQExpBuffer str, size_t needed); /*------------------------ * printfPQExpBuffer @@ -153,6 +153,6 @@ extern void appendPQExpBufferChar(PQExpBuffer str, char ch); * if necessary. */ extern void appendBinaryPQExpBuffer(PQExpBuffer str, - const char *data, int datalen); + const char *data, size_t datalen); #endif /* PQEXPBUFFER_H */ diff --git a/src/interfaces/libpq/win32.h b/src/interfaces/libpq/win32.h index 6988bcd98fe..91f7df1a2d3 100644 --- a/src/interfaces/libpq/win32.h +++ b/src/interfaces/libpq/win32.h @@ -11,9 +11,11 @@ * Some compat functions */ #define open(a,b,c) _open(a,b,c) +#define close(a) _close(a) #define read(a,b,c) _read(a,b,c) #define write(a,b,c) _write(a,b,c) - +#define popen(a,b) _popen(a,b) +#define pclose(a) _pclose(a) /* * crypt not available (yet) -- GitLab