Trailing semicolons in psql patch

Started by Greg Sabino Mullanealmost 25 years ago10 messagespatches
Jump to latest
#1Greg Sabino Mullane
greg@turnstep.com

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

How about a patch to strip semicolons in psql? I *know*
some of you have done this before:

\d mytable;

(Did not find any relation named "mytable;")

Since there is no reason to have a table named "mytable;"
why not just have psql do the smart thing and silently
strip the trailing semicolons? The attached patch only addresses
a few backslash commands as a proof of concept, but some others
could probably use it without harm. The patch affects:

\C
\c
\d
\e
\i
\o
\s
\z

Greg Sabino Mullane
greg@turnstep.com
PGP Key: 0x14964AC8 200109271811

P.S. Re: microtiming: EXPLAIN ANALYZE sounds interesting, and as
soon as cvs is fixed so that anoncvs/postgresql works, I'll take
a look at it. :)

-----BEGIN PGP SIGNATURE-----
Comment: http://www.turnstep.com/pgp.html

iQA/AwUBO7OkzrybkGcUlkrIEQIPrgCgurQBAqZLt6m5JvbKOJyVIOx3ZQAAn0Wg
KD097oaniENYAeikbSM7LdZg
=erHC
-----END PGP SIGNATURE-----

#2Bruce Momjian
bruce@momjian.us
In reply to: Greg Sabino Mullane (#1)
Re: Trailing semicolons in psql patch

I like it. Several people have complained because they forget and add
the semicolon. I like that you made it per-command so commands that
need to see the semicolon see it. I will change it slightly to use
true/false rather than 0/1.

Your patch has been added to the PostgreSQL unapplied patches list at:

http://candle.pha.pa.us/cgi-bin/pgpatches

I will try to apply it within the next 48 hours.

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

How about a patch to strip semicolons in psql? I *know*
some of you have done this before:

\d mytable;

(Did not find any relation named "mytable;")

Since there is no reason to have a table named "mytable;"
why not just have psql do the smart thing and silently
strip the trailing semicolons? The attached patch only addresses
a few backslash commands as a proof of concept, but some others
could probably use it without harm. The patch affects:

\C
\c
\d
\e
\i
\o
\s
\z

Greg Sabino Mullane
greg@turnstep.com
PGP Key: 0x14964AC8 200109271811

P.S. Re: microtiming: EXPLAIN ANALYZE sounds interesting, and as
soon as cvs is fixed so that anoncvs/postgresql works, I'll take
a look at it. :)

-----BEGIN PGP SIGNATURE-----
Comment: http://www.turnstep.com/pgp.html

iQA/AwUBO7OkzrybkGcUlkrIEQIPrgCgurQBAqZLt6m5JvbKOJyVIOx3ZQAAn0Wg
KD097oaniENYAeikbSM7LdZg
=erHC
-----END PGP SIGNATURE-----

*** ./src/bin/psql/command.c.orig	Tue Sep 25 13:21:50 2001
--- ./src/bin/psql/command.c	Tue Sep 25 18:06:37 2001
***************
*** 55,61 ****
{
OT_NORMAL, OT_SQLID, OT_FILEPIPE
};
! 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);
--- 55,61 ----
{
OT_NORMAL, OT_SQLID, OT_FILEPIPE
};
! static char *scan_option(char **string, enum option_type type, char *quote, bool semicolon);
static char *unescape(const unsigned char *source, size_t len);

static bool do_edit(const char *filename_arg, PQExpBuffer query_buf);
***************
*** 216,222 ****
/* \C -- override table title (formerly change HTML caption) */
else if (strcmp(cmd, "C") == 0)
{
! char *opt = scan_option(&string, OT_NORMAL, NULL);

success = do_pset("title", opt, &pset.popt, quiet);
free(opt);
--- 216,222 ----
/* \C -- override table title (formerly change HTML caption) */
else if (strcmp(cmd, "C") == 0)
{
! 		char	   *opt = scan_option(&string, OT_NORMAL, NULL, 1);

success = do_pset("title", opt, &pset.popt, quiet);
free(opt);
***************
*** 238,245 ****
char opt1q,
opt2q;

! opt1 = scan_option(&string, OT_NORMAL, &opt1q);
! opt2 = scan_option(&string, OT_NORMAL, &opt2q);

if (opt2)
/* gave username */
--- 238,245 ----
char		opt1q,
opt2q;

! opt1 = scan_option(&string, OT_NORMAL, &opt1q, 1);
! opt2 = scan_option(&string, OT_NORMAL, &opt2q, 1);

if (opt2)
/* gave username */
***************
*** 273,280 ****
{
char *name;
bool show_verbose;

- name = scan_option(&string, OT_SQLID, NULL);
show_verbose = strchr(cmd, '+') ? true : false;

switch (cmd[1])
--- 273,280 ----
{
char	   *name;
bool		show_verbose;
+ 		name = scan_option(&string, OT_SQLID, NULL, 1);

show_verbose = strchr(cmd, '+') ? true : false;

switch (cmd[1])
***************
*** 337,343 ****
}
else
{
! 			fname = scan_option(&string, OT_NORMAL, NULL);
status = do_edit(fname, query_buf) ? CMD_NEWEDIT : CMD_ERROR;
free(fname);
}
--- 337,343 ----
}
else
{
! 			fname = scan_option(&string, OT_NORMAL, NULL, 1);
status = do_edit(fname, query_buf) ? CMD_NEWEDIT : CMD_ERROR;
free(fname);
}
***************
*** 356,363 ****
fout = pset.queryFout;
else
fout = stdout;
! 
! 		while ((value = scan_option(&string, OT_NORMAL, &quoted)))
{
if (!quoted && strcmp(value, "-n") == 0)
no_newline = true;
--- 356,363 ----
fout = pset.queryFout;
else
fout = stdout;
! 	
! 		while ((value = scan_option(&string, OT_NORMAL, &quoted, 0)))
{
if (!quoted && strcmp(value, "-n") == 0)
no_newline = true;
***************
*** 378,384 ****
/* \encoding -- set/show client side encoding */
else if (strcmp(cmd, "encoding") == 0)
{
! 		char	   *encoding = scan_option(&string, OT_NORMAL, NULL);
if (!encoding)
/* show encoding */
--- 378,384 ----
/* \encoding -- set/show client side encoding */
else if (strcmp(cmd, "encoding") == 0)
{
! 		char	   *encoding = scan_option(&string, OT_NORMAL, NULL, 0);

if (!encoding)
/* show encoding */
***************
*** 406,412 ****
/* \f -- change field separator */
else if (strcmp(cmd, "f") == 0)
{
! char *fname = scan_option(&string, OT_NORMAL, NULL);

success = do_pset("fieldsep", fname, &pset.popt, quiet);
free(fname);
--- 406,412 ----
/* \f -- change field separator */
else if (strcmp(cmd, "f") == 0)
{
! 		char	   *fname = scan_option(&string, OT_NORMAL, NULL, 0);

success = do_pset("fieldsep", fname, &pset.popt, quiet);
free(fname);
***************
*** 415,421 ****
/* \g means send query */
else if (strcmp(cmd, "g") == 0)
{
! char *fname = scan_option(&string, OT_FILEPIPE, NULL);

if (!fname)
pset.gfname = NULL;
--- 415,421 ----
/* \g means send query */
else if (strcmp(cmd, "g") == 0)
{
! 		char	   *fname = scan_option(&string, OT_FILEPIPE, NULL, 0);

if (!fname)
pset.gfname = NULL;
***************
*** 447,453 ****
/* \i is include file */
else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0)
{
! char *fname = scan_option(&string, OT_NORMAL, NULL);

if (!fname)
{
--- 447,453 ----
/* \i is include file */
else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0)
{
! 		char	   *fname = scan_option(&string, OT_NORMAL, NULL, 1);

if (!fname)
{
***************
*** 475,482 ****
char *opt1,
*opt2;

! opt1 = scan_option(&string, OT_NORMAL, NULL);
! opt2 = scan_option(&string, OT_NORMAL, NULL);

if (strcmp(cmd + 3, "export") == 0)
{
--- 475,482 ----
char	   *opt1,
*opt2;

! opt1 = scan_option(&string, OT_NORMAL, NULL, 1);
! opt2 = scan_option(&string, OT_NORMAL, NULL, 1);

if (strcmp(cmd + 3, "export") == 0)
{
***************
*** 528,534 ****
/* \o -- set query output */
else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0)
{
! char *fname = scan_option(&string, OT_FILEPIPE, NULL);

success = setQFout(fname);
free(fname);
--- 528,534 ----
/* \o -- set query output */
else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0)
{
! 		char	   *fname = scan_option(&string, OT_FILEPIPE, NULL, 1);

success = setQFout(fname);
free(fname);
***************
*** 547,554 ****
/* \pset -- set printing parameters */
else if (strcmp(cmd, "pset") == 0)
{
! char *opt0 = scan_option(&string, OT_NORMAL, NULL);
! char *opt1 = scan_option(&string, OT_NORMAL, NULL);

if (!opt0)
{
--- 547,554 ----
/* \pset -- set printing parameters */
else if (strcmp(cmd, "pset") == 0)
{
! 		char	   *opt0 = scan_option(&string, OT_NORMAL, NULL, 0);
! 		char	   *opt1 = scan_option(&string, OT_NORMAL, NULL, 0);

if (!opt0)
{
***************
*** 577,583 ****
/* \s save history in a file or show it on the screen */
else if (strcmp(cmd, "s") == 0)
{
! char *fname = scan_option(&string, OT_NORMAL, NULL);

success = saveHistory(fname ? fname : "/dev/tty");

--- 577,583 ----
/* \s save history in a file or show it on the screen */
else if (strcmp(cmd, "s") == 0)
{
! 		char	   *fname = scan_option(&string, OT_NORMAL, NULL, 1);

success = saveHistory(fname ? fname : "/dev/tty");

***************
*** 589,595 ****
/* \set -- generalized set variable/option command */
else if (strcmp(cmd, "set") == 0)
{
! char *opt0 = scan_option(&string, OT_NORMAL, NULL);

if (!opt0)
{
--- 589,595 ----
/* \set -- generalized set variable/option command */
else if (strcmp(cmd, "set") == 0)
{
! 		char	   *opt0 = scan_option(&string, OT_NORMAL, NULL, 0);

if (!opt0)
{
***************
*** 614,624 ****
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)
--- 614,624 ----
char	   *newval = NULL;
char	   *opt;

! opt = scan_option(&string, OT_NORMAL, NULL, 0);
newval = xstrdup(opt ? opt : "");
free(opt);

! while ((opt = scan_option(&string, OT_NORMAL, NULL, 0)))
{
newval = realloc(newval, strlen(newval) + strlen(opt) + 1);
if (!newval)
***************
*** 648,654 ****
/* \T -- define html <table ...> attributes */
else if (strcmp(cmd, "T") == 0)
{
! char *value = scan_option(&string, OT_NORMAL, NULL);

success = do_pset("tableattr", value, &pset.popt, quiet);
free(value);
--- 648,654 ----
/* \T -- define html <table ...> attributes */
else if (strcmp(cmd, "T") == 0)
{
! 		char	   *value = scan_option(&string, OT_NORMAL, NULL, 0);

success = do_pset("tableattr", value, &pset.popt, quiet);
free(value);
***************
*** 657,663 ****
/* \unset */
else if (strcmp(cmd, "unset") == 0)
{
! char *opt = scan_option(&string, OT_NORMAL, NULL);

if (!opt)
{
--- 657,663 ----
/* \unset */
else if (strcmp(cmd, "unset") == 0)
{
! 		char	   *opt = scan_option(&string, OT_NORMAL, NULL, 0);

if (!opt)
{
***************
*** 686,692 ****
}
else
{
! fname = scan_option(&string, OT_FILEPIPE, NULL);

if (!fname)
{
--- 686,692 ----
}
else
{
! 			fname = scan_option(&string, OT_FILEPIPE, NULL, 1);

if (!fname)
{
***************
*** 741,747 ****
/* \z -- list table rights (grant/revoke) */
else if (strcmp(cmd, "z") == 0)
{
! char *opt = scan_option(&string, OT_SQLID, NULL);

success = permissionsList(opt);
free(opt);
--- 741,747 ----
/* \z -- list table rights (grant/revoke) */
else if (strcmp(cmd, "z") == 0)
{
! 		char	   *opt = scan_option(&string, OT_SQLID, NULL, 1);

success = permissionsList(opt);
free(opt);
***************
*** 772,778 ****
char *value;

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);
--- 772,778 ----
char	   *value;

fprintf(stderr, "+ optstr = |%s|\n", options_string);
! while ((value = scan_option(&string, OT_NORMAL, NULL, 1)))
{
fprintf(stderr, "+ opt(%d) = |%s|\n", i++, value);
free(value);
***************
*** 787,793 ****
status = CMD_ERROR;

/* eat the rest of the options string */
! 	while ((val = scan_option(&string, OT_NORMAL, NULL)))
{
if (status != CMD_UNKNOWN)
psql_error("\\%s: extra argument '%s' ignored\n", cmd, val);
--- 787,793 ----
status = CMD_ERROR;
/* eat the rest of the options string */
! 	while ((val = scan_option(&string, OT_NORMAL, NULL, 0)))
{
if (status != CMD_UNKNOWN)
psql_error("\\%s: extra argument '%s' ignored\n", cmd, val);
***************
*** 806,812 ****
* scan_option()
*/
static char *
! scan_option(char **string, enum option_type type, char *quote)
{
unsigned int pos = 0;
char	   *options_string;
--- 806,812 ----
* scan_option()
*/
static char *
! scan_option(char **string, enum option_type type, char *quote, bool semicolon)
{
unsigned int pos = 0;
char	   *options_string;
***************
*** 822,827 ****
--- 822,835 ----
/* skip leading whitespace */
pos += strspn(options_string + pos, " \t\n\r");
+ 
+ 	/* Strip any trailing semi-colons for some types */
+ 	if (semicolon) {
+ 		int i;
+ 		for (i = strlen(options_string)-1; i && options_string[i]==';'; i--);
+ 		if (i<strlen(options_string)-1) options_string[i+1]='\0';
+ 	}
+ 
switch (options_string[pos])
{
***************
*** 863,868 ****
--- 871,877 ----
* If this is expected to be an SQL identifier like option
* then we strip out the double quotes
*/
+ 
if (type == OT_SQLID)
{
unsigned int k,
***************
*** 894,900 ****
*string = options_string + jj + 1;
if (quote)
*quote = '"';
! 
return return_val;
}
--- 903,909 ----
*string = options_string + jj + 1;
if (quote)
*quote = '"';
! 			
return return_val;
}

---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
(send "unregister YourEmailAddressHere" to majordomo@postgresql.org)

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026
#3Peter Eisentraut
peter_e@gmx.net
In reply to: Greg Sabino Mullane (#1)
Re: Trailing semicolons in psql patch

greg@turnstep.com writes:

Since there is no reason to have a table named "mytable;"
why not just have psql do the smart thing and silently
strip the trailing semicolons?

"Stripping" semicolons is one thing, accepting semicolons as command
separators is another. Your patch seems to handle the case of

\d mytable;

but it doesn't handle any of

\d mytable; \d yourtable
\d mytable;<space>
\d mytable; select * from mytable;

Anything short of that plus...

The attached patch only addresses
a few backslash commands as a proof of concept,

...is just going to create some other confusion in place of the current
one.

--
Peter Eisentraut peter_e@gmx.net http://funkturm.homeip.net/~peter

#4Bruce Momjian
bruce@momjian.us
In reply to: Peter Eisentraut (#3)
Re: Trailing semicolons in psql patch

greg@turnstep.com writes:

Since there is no reason to have a table named "mytable;"
why not just have psql do the smart thing and silently
strip the trailing semicolons?

"Stripping" semicolons is one thing, accepting semicolons as command
separators is another. Your patch seems to handle the case of

\d mytable;

but it doesn't handle any of

\d mytable; \d yourtable
\d mytable;<space>
\d mytable; select * from mytable;

Anything short of that plus...

The attached patch only addresses
a few backslash commands as a proof of concept,

...is just going to create some other confusion in place of the current
one.

My assumption is that the semicolons used with backslash commands are
just knee-jerk adding of semicolons to the end, not attempts to actually
string backslash commands togeter. His patch does catch those cases.
The basic issue is that I think we can conclude that any trailing
semicolon on a line with a backslash command can be dropped. In fact, I
wouldn't want to catch any of the items you listed above. Let them fail.

We all do this by accident, don't we? I know I do.

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026
#5Tom Lane
tgl@sss.pgh.pa.us
In reply to: Bruce Momjian (#4)
Re: Trailing semicolons in psql patch

Bruce Momjian <pgman@candle.pha.pa.us> writes:

The basic issue is that I think we can conclude that any trailing
semicolon on a line with a backslash command can be dropped.

I agree with this premise ... but stated that way, the patch does
entirely the wrong thing, because it's stripping trailing semis at
the word level not the line level. I think it's a lot more surprising
to drop the semi in

\x foo; bar

than when it's actually at the end of the line.

How about stripping trailing semis (perhaps whitespace too?) before
the line is broken into words?

regards, tom lane

#6Greg Sabino Mullane
greg@turnstep.com
In reply to: Tom Lane (#5)
Re: Trailing semicolons in psql patch

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I agree with this premise ... but stated that way, the patch does
entirely the wrong thing, because it's stripping trailing semis at
the word level not the line level. I think it's a lot more surprising
to drop the semi in

\x foo; bar

than when it's actually at the end of the line.

Ah...so what we want then is for all of these cases to work:

\d foo \d bar
\d foo; \d bar
\d foo; \d bar;
\d foo; \d bar

Makes sense...I never combine backslash commands on the same line,
but it is possible. In that case, how about just moving this section:

if (semicolon) {
int i;
for (i = strlen(options_string)-1; i && options_string[i]==';'; i--);
if (i<strlen(options_string)-1) options_string[i+1]='\0';
}

to right after here, where we find a "normal word"?

strncpy(return_val, &options_string[pos], token_end);
return_val[token_end] = 0;

(and changing options_string to return_val)

that should allow us to catch all the cases above, if I am
understanding the objection above correctly.

Greg Sabino Mullane
greg@turnstep.com PGP Key: 0x14964AC8 200110011738

-----BEGIN PGP SIGNATURE-----
Comment: http://www.turnstep.com/pgp.html

iQA/AwUBO7jijLybkGcUlkrIEQIMWwCfYDNNqC/UYKKKaqj5MYrEcg2bGhcAoPUP
cqeF4yXAuPedUpQPLVFsw1lO
=4dGu
-----END PGP SIGNATURE-----

#7Bruce Momjian
bruce@momjian.us
In reply to: Greg Sabino Mullane (#6)
Re: Trailing semicolons in psql patch

OK, any chance of getting an updated patch. Your patch email is at:

http://candle.pha.pa.us/cgi-bin/pgpatches

I agree with this premise ... but stated that way, the patch does
entirely the wrong thing, because it's stripping trailing semis at
the word level not the line level. I think it's a lot more surprising
to drop the semi in

\x foo; bar

than when it's actually at the end of the line.

Ah...so what we want then is for all of these cases to work:

\d foo \d bar
\d foo; \d bar
\d foo; \d bar;
\d foo; \d bar

Makes sense...I never combine backslash commands on the same line,
but it is possible. In that case, how about just moving this section:

if (semicolon) {
int i;
for (i = strlen(options_string)-1; i && options_string[i]==';'; i--);
if (i<strlen(options_string)-1) options_string[i+1]='\0';
}

to right after here, where we find a "normal word"?

strncpy(return_val, &options_string[pos], token_end);
return_val[token_end] = 0;

(and changing options_string to return_val)

that should allow us to catch all the cases above, if I am
understanding the objection above correctly.

Greg Sabino Mullane
greg@turnstep.com PGP Key: 0x14964AC8 200110011738

-- End of PGP signed section.

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026
#8Greg Sabino Mullane
greg@turnstep.com
In reply to: Bruce Momjian (#7)
Re: Trailing semicolons in psql patch

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Attached is the updated version of the patch, which matches
on words as opposed to lines, which means that all of the
following work in psql:

\d foo \d bar
\d foo; \d bar
\d foo \d bar;;
\d foo; <space>

This one also uses "true and false" and strips semicolons
for the following backslash commands: \C \c \d \e \i \o \s \z

Thanks to Bruce Momjian, Peter Eisentraut, and Tom Lane for
the suggestions.

Greg Sabino Mullane
greg@turnstep.com
PGP Key: 0x14964AC8 200110040743

-----BEGIN PGP SIGNATURE-----
Comment: http://www.turnstep.com/pgp.html

iQA+AwUBO7xNp7ybkGcUlkrIEQKzwgCXcaQGh16cXELnjKDs7lpg7BP7twCg1jHW
lDE1BVxPTrLXQZeQRn+ieu4=
=PK53
-----END PGP SIGNATURE-----

#9Bruce Momjian
bruce@momjian.us
In reply to: Greg Sabino Mullane (#8)
Re: Trailing semicolons in psql patch

Your patch has been added to the PostgreSQL unapplied patches list at:

http://candle.pha.pa.us/cgi-bin/pgpatches

I will try to apply it within the next 48 hours.

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Attached is the updated version of the patch, which matches
on words as opposed to lines, which means that all of the
following work in psql:

\d foo \d bar
\d foo; \d bar
\d foo \d bar;;
\d foo; <space>

This one also uses "true and false" and strips semicolons
for the following backslash commands: \C \c \d \e \i \o \s \z

Thanks to Bruce Momjian, Peter Eisentraut, and Tom Lane for
the suggestions.

Greg Sabino Mullane
greg@turnstep.com
PGP Key: 0x14964AC8 200110040743

-----BEGIN PGP SIGNATURE-----
Comment: http://www.turnstep.com/pgp.html

iQA+AwUBO7xNp7ybkGcUlkrIEQKzwgCXcaQGh16cXELnjKDs7lpg7BP7twCg1jHW
lDE1BVxPTrLXQZeQRn+ieu4=
=PK53
-----END PGP SIGNATURE-----

*** ./src/bin/psql/command.c.orig	Thu Oct  4 07:26:30 2001
--- ./src/bin/psql/command.c	Thu Oct  4 07:30:37 2001
***************
*** 55,61 ****
{
OT_NORMAL, OT_SQLID, OT_FILEPIPE
};
! 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);
--- 55,61 ----
{
OT_NORMAL, OT_SQLID, OT_FILEPIPE
};
! static char *scan_option(char **string, enum option_type type, char *quote, bool semicolon);
static char *unescape(const unsigned char *source, size_t len);

static bool do_edit(const char *filename_arg, PQExpBuffer query_buf);
***************
*** 216,222 ****
/* \C -- override table title (formerly change HTML caption) */
else if (strcmp(cmd, "C") == 0)
{
! char *opt = scan_option(&string, OT_NORMAL, NULL);

success = do_pset("title", opt, &pset.popt, quiet);
free(opt);
--- 216,222 ----
/* \C -- override table title (formerly change HTML caption) */
else if (strcmp(cmd, "C") == 0)
{
! 		char	   *opt = scan_option(&string, OT_NORMAL, NULL, true);

success = do_pset("title", opt, &pset.popt, quiet);
free(opt);
***************
*** 238,245 ****
char opt1q,
opt2q;

! opt1 = scan_option(&string, OT_NORMAL, &opt1q);
! opt2 = scan_option(&string, OT_NORMAL, &opt2q);

if (opt2)
/* gave username */
--- 238,245 ----
char		opt1q,
opt2q;

! opt1 = scan_option(&string, OT_NORMAL, &opt1q, true);
! opt2 = scan_option(&string, OT_NORMAL, &opt2q, true);

if (opt2)
/* gave username */
***************
*** 273,280 ****
{
char *name;
bool show_verbose;

- name = scan_option(&string, OT_SQLID, NULL);
show_verbose = strchr(cmd, '+') ? true : false;

switch (cmd[1])
--- 273,280 ----
{
char	   *name;
bool		show_verbose;
+ 		name = scan_option(&string, OT_SQLID, NULL, true);

show_verbose = strchr(cmd, '+') ? true : false;

switch (cmd[1])
***************
*** 337,343 ****
}
else
{
! 			fname = scan_option(&string, OT_NORMAL, NULL);
status = do_edit(fname, query_buf) ? CMD_NEWEDIT : CMD_ERROR;
free(fname);
}
--- 337,343 ----
}
else
{
! 			fname = scan_option(&string, OT_NORMAL, NULL, true);
status = do_edit(fname, query_buf) ? CMD_NEWEDIT : CMD_ERROR;
free(fname);
}
***************
*** 356,363 ****
fout = pset.queryFout;
else
fout = stdout;
! 
! 		while ((value = scan_option(&string, OT_NORMAL, &quoted)))
{
if (!quoted && strcmp(value, "-n") == 0)
no_newline = true;
--- 356,363 ----
fout = pset.queryFout;
else
fout = stdout;
! 	
! 		while ((value = scan_option(&string, OT_NORMAL, &quoted, false)))
{
if (!quoted && strcmp(value, "-n") == 0)
no_newline = true;
***************
*** 378,384 ****
/* \encoding -- set/show client side encoding */
else if (strcmp(cmd, "encoding") == 0)
{
! 		char	   *encoding = scan_option(&string, OT_NORMAL, NULL);
if (!encoding)
/* show encoding */
--- 378,384 ----
/* \encoding -- set/show client side encoding */
else if (strcmp(cmd, "encoding") == 0)
{
! 		char	   *encoding = scan_option(&string, OT_NORMAL, NULL, false);

if (!encoding)
/* show encoding */
***************
*** 406,412 ****
/* \f -- change field separator */
else if (strcmp(cmd, "f") == 0)
{
! char *fname = scan_option(&string, OT_NORMAL, NULL);

success = do_pset("fieldsep", fname, &pset.popt, quiet);
free(fname);
--- 406,412 ----
/* \f -- change field separator */
else if (strcmp(cmd, "f") == 0)
{
! 		char	   *fname = scan_option(&string, OT_NORMAL, NULL, false);

success = do_pset("fieldsep", fname, &pset.popt, quiet);
free(fname);
***************
*** 415,421 ****
/* \g means send query */
else if (strcmp(cmd, "g") == 0)
{
! char *fname = scan_option(&string, OT_FILEPIPE, NULL);

if (!fname)
pset.gfname = NULL;
--- 415,421 ----
/* \g means send query */
else if (strcmp(cmd, "g") == 0)
{
! 		char	   *fname = scan_option(&string, OT_FILEPIPE, NULL, false);

if (!fname)
pset.gfname = NULL;
***************
*** 447,453 ****
/* \i is include file */
else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0)
{
! char *fname = scan_option(&string, OT_NORMAL, NULL);

if (!fname)
{
--- 447,453 ----
/* \i is include file */
else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0)
{
! 		char	   *fname = scan_option(&string, OT_NORMAL, NULL, true);

if (!fname)
{
***************
*** 475,482 ****
char *opt1,
*opt2;

! opt1 = scan_option(&string, OT_NORMAL, NULL);
! opt2 = scan_option(&string, OT_NORMAL, NULL);

if (strcmp(cmd + 3, "export") == 0)
{
--- 475,482 ----
char	   *opt1,
*opt2;

! opt1 = scan_option(&string, OT_NORMAL, NULL, true);
! opt2 = scan_option(&string, OT_NORMAL, NULL, true);

if (strcmp(cmd + 3, "export") == 0)
{
***************
*** 525,531 ****
/* \o -- set query output */
else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0)
{
! char *fname = scan_option(&string, OT_FILEPIPE, NULL);

success = setQFout(fname);
free(fname);
--- 525,531 ----
/* \o -- set query output */
else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0)
{
! 		char	   *fname = scan_option(&string, OT_FILEPIPE, NULL, true);

success = setQFout(fname);
free(fname);
***************
*** 544,551 ****
/* \pset -- set printing parameters */
else if (strcmp(cmd, "pset") == 0)
{
! char *opt0 = scan_option(&string, OT_NORMAL, NULL);
! char *opt1 = scan_option(&string, OT_NORMAL, NULL);

if (!opt0)
{
--- 544,551 ----
/* \pset -- set printing parameters */
else if (strcmp(cmd, "pset") == 0)
{
! 		char	   *opt0 = scan_option(&string, OT_NORMAL, NULL, false);
! 		char	   *opt1 = scan_option(&string, OT_NORMAL, NULL, false);

if (!opt0)
{
***************
*** 574,580 ****
/* \s save history in a file or show it on the screen */
else if (strcmp(cmd, "s") == 0)
{
! char *fname = scan_option(&string, OT_NORMAL, NULL);

success = saveHistory(fname ? fname : "/dev/tty");

--- 574,580 ----
/* \s save history in a file or show it on the screen */
else if (strcmp(cmd, "s") == 0)
{
! 		char	   *fname = scan_option(&string, OT_NORMAL, NULL, true);

success = saveHistory(fname ? fname : "/dev/tty");

***************
*** 586,592 ****
/* \set -- generalized set variable/option command */
else if (strcmp(cmd, "set") == 0)
{
! char *opt0 = scan_option(&string, OT_NORMAL, NULL);

if (!opt0)
{
--- 586,592 ----
/* \set -- generalized set variable/option command */
else if (strcmp(cmd, "set") == 0)
{
! 		char	   *opt0 = scan_option(&string, OT_NORMAL, NULL, false);

if (!opt0)
{
***************
*** 611,621 ****
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)
--- 611,621 ----
char	   *newval = NULL;
char	   *opt;

! opt = scan_option(&string, OT_NORMAL, NULL, false);
newval = xstrdup(opt ? opt : "");
free(opt);

! while ((opt = scan_option(&string, OT_NORMAL, NULL, false)))
{
newval = realloc(newval, strlen(newval) + strlen(opt) + 1);
if (!newval)
***************
*** 645,651 ****
/* \T -- define html <table ...> attributes */
else if (strcmp(cmd, "T") == 0)
{
! char *value = scan_option(&string, OT_NORMAL, NULL);

success = do_pset("tableattr", value, &pset.popt, quiet);
free(value);
--- 645,651 ----
/* \T -- define html <table ...> attributes */
else if (strcmp(cmd, "T") == 0)
{
! 		char	   *value = scan_option(&string, OT_NORMAL, NULL, false);

success = do_pset("tableattr", value, &pset.popt, quiet);
free(value);
***************
*** 654,660 ****
/* \unset */
else if (strcmp(cmd, "unset") == 0)
{
! char *opt = scan_option(&string, OT_NORMAL, NULL);

if (!opt)
{
--- 654,660 ----
/* \unset */
else if (strcmp(cmd, "unset") == 0)
{
! 		char	   *opt = scan_option(&string, OT_NORMAL, NULL, false);

if (!opt)
{
***************
*** 683,689 ****
}
else
{
! fname = scan_option(&string, OT_FILEPIPE, NULL);

if (!fname)
{
--- 683,689 ----
}
else
{
! 			fname = scan_option(&string, OT_FILEPIPE, NULL, true);

if (!fname)
{
***************
*** 738,744 ****
/* \z -- list table rights (grant/revoke) */
else if (strcmp(cmd, "z") == 0)
{
! char *opt = scan_option(&string, OT_SQLID, NULL);

success = permissionsList(opt);
free(opt);
--- 738,744 ----
/* \z -- list table rights (grant/revoke) */
else if (strcmp(cmd, "z") == 0)
{
! 		char	   *opt = scan_option(&string, OT_SQLID, NULL, true);

success = permissionsList(opt);
free(opt);
***************
*** 769,775 ****
char *value;

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);
--- 769,775 ----
char	   *value;

fprintf(stderr, "+ optstr = |%s|\n", options_string);
! while ((value = scan_option(&string, OT_NORMAL, NULL, true)))
{
fprintf(stderr, "+ opt(%d) = |%s|\n", i++, value);
free(value);
***************
*** 784,790 ****
status = CMD_ERROR;

/* eat the rest of the options string */
! 	while ((val = scan_option(&string, OT_NORMAL, NULL)))
{
if (status != CMD_UNKNOWN)
psql_error("\\%s: extra argument '%s' ignored\n", cmd, val);
--- 784,790 ----
status = CMD_ERROR;
/* eat the rest of the options string */
! 	while ((val = scan_option(&string, OT_NORMAL, NULL, false)))
{
if (status != CMD_UNKNOWN)
psql_error("\\%s: extra argument '%s' ignored\n", cmd, val);
***************
*** 803,809 ****
* scan_option()
*/
static char *
! scan_option(char **string, enum option_type type, char *quote)
{
unsigned int pos = 0;
char	   *options_string;
--- 803,809 ----
* scan_option()
*/
static char *
! scan_option(char **string, enum option_type type, char *quote, bool semicolon)
{
unsigned int pos = 0;
char	   *options_string;
***************
*** 860,865 ****
--- 860,866 ----
* If this is expected to be an SQL identifier like option
* then we strip out the double quotes
*/
+ 
if (type == OT_SQLID)
{
unsigned int k,
***************
*** 891,897 ****
*string = options_string + jj + 1;
if (quote)
*quote = '"';
! 
return return_val;
}
--- 892,898 ----
*string = options_string + jj + 1;
if (quote)
*quote = '"';
! 			
return return_val;
}
***************
*** 1071,1076 ****
--- 1072,1084 ----
}
strncpy(return_val, &options_string[pos], token_end);
return_val[token_end] = 0;
+ 
+ 				/* Strip any trailing semi-colons for some types */
+ 				if (semicolon) {
+ 					int i;
+ 					for (i = strlen(return_val)-1; i && return_val[i]==';'; i--);
+ 					if (i<strlen(return_val)-1) return_val[i+1]='\0';
+ 				}

if (type == OT_SQLID)
for (cp = return_val; *cp; cp += PQmblen(cp, pset.encoding))

---------------------------(end of broadcast)---------------------------
TIP 1: subscribe and unsubscribe commands go to majordomo@postgresql.org

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026
#10Bruce Momjian
bruce@momjian.us
In reply to: Greg Sabino Mullane (#8)
Re: Trailing semicolons in psql patch

Patch applied. Thanks.

Also, seems there is now a \cd command. I assume the param for that
would be 'true'.

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Attached is the updated version of the patch, which matches
on words as opposed to lines, which means that all of the
following work in psql:

\d foo \d bar
\d foo; \d bar
\d foo \d bar;;
\d foo; <space>

This one also uses "true and false" and strips semicolons
for the following backslash commands: \C \c \d \e \i \o \s \z

Thanks to Bruce Momjian, Peter Eisentraut, and Tom Lane for
the suggestions.

Greg Sabino Mullane
greg@turnstep.com
PGP Key: 0x14964AC8 200110040743

-----BEGIN PGP SIGNATURE-----
Comment: http://www.turnstep.com/pgp.html

iQA+AwUBO7xNp7ybkGcUlkrIEQKzwgCXcaQGh16cXELnjKDs7lpg7BP7twCg1jHW
lDE1BVxPTrLXQZeQRn+ieu4=
=PK53
-----END PGP SIGNATURE-----

*** ./src/bin/psql/command.c.orig	Thu Oct  4 07:26:30 2001
--- ./src/bin/psql/command.c	Thu Oct  4 07:30:37 2001
***************
*** 55,61 ****
{
OT_NORMAL, OT_SQLID, OT_FILEPIPE
};
! 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);
--- 55,61 ----
{
OT_NORMAL, OT_SQLID, OT_FILEPIPE
};
! static char *scan_option(char **string, enum option_type type, char *quote, bool semicolon);
static char *unescape(const unsigned char *source, size_t len);

static bool do_edit(const char *filename_arg, PQExpBuffer query_buf);
***************
*** 216,222 ****
/* \C -- override table title (formerly change HTML caption) */
else if (strcmp(cmd, "C") == 0)
{
! char *opt = scan_option(&string, OT_NORMAL, NULL);

success = do_pset("title", opt, &pset.popt, quiet);
free(opt);
--- 216,222 ----
/* \C -- override table title (formerly change HTML caption) */
else if (strcmp(cmd, "C") == 0)
{
! 		char	   *opt = scan_option(&string, OT_NORMAL, NULL, true);

success = do_pset("title", opt, &pset.popt, quiet);
free(opt);
***************
*** 238,245 ****
char opt1q,
opt2q;

! opt1 = scan_option(&string, OT_NORMAL, &opt1q);
! opt2 = scan_option(&string, OT_NORMAL, &opt2q);

if (opt2)
/* gave username */
--- 238,245 ----
char		opt1q,
opt2q;

! opt1 = scan_option(&string, OT_NORMAL, &opt1q, true);
! opt2 = scan_option(&string, OT_NORMAL, &opt2q, true);

if (opt2)
/* gave username */
***************
*** 273,280 ****
{
char *name;
bool show_verbose;

- name = scan_option(&string, OT_SQLID, NULL);
show_verbose = strchr(cmd, '+') ? true : false;

switch (cmd[1])
--- 273,280 ----
{
char	   *name;
bool		show_verbose;
+ 		name = scan_option(&string, OT_SQLID, NULL, true);

show_verbose = strchr(cmd, '+') ? true : false;

switch (cmd[1])
***************
*** 337,343 ****
}
else
{
! 			fname = scan_option(&string, OT_NORMAL, NULL);
status = do_edit(fname, query_buf) ? CMD_NEWEDIT : CMD_ERROR;
free(fname);
}
--- 337,343 ----
}
else
{
! 			fname = scan_option(&string, OT_NORMAL, NULL, true);
status = do_edit(fname, query_buf) ? CMD_NEWEDIT : CMD_ERROR;
free(fname);
}
***************
*** 356,363 ****
fout = pset.queryFout;
else
fout = stdout;
! 
! 		while ((value = scan_option(&string, OT_NORMAL, &quoted)))
{
if (!quoted && strcmp(value, "-n") == 0)
no_newline = true;
--- 356,363 ----
fout = pset.queryFout;
else
fout = stdout;
! 	
! 		while ((value = scan_option(&string, OT_NORMAL, &quoted, false)))
{
if (!quoted && strcmp(value, "-n") == 0)
no_newline = true;
***************
*** 378,384 ****
/* \encoding -- set/show client side encoding */
else if (strcmp(cmd, "encoding") == 0)
{
! 		char	   *encoding = scan_option(&string, OT_NORMAL, NULL);
if (!encoding)
/* show encoding */
--- 378,384 ----
/* \encoding -- set/show client side encoding */
else if (strcmp(cmd, "encoding") == 0)
{
! 		char	   *encoding = scan_option(&string, OT_NORMAL, NULL, false);

if (!encoding)
/* show encoding */
***************
*** 406,412 ****
/* \f -- change field separator */
else if (strcmp(cmd, "f") == 0)
{
! char *fname = scan_option(&string, OT_NORMAL, NULL);

success = do_pset("fieldsep", fname, &pset.popt, quiet);
free(fname);
--- 406,412 ----
/* \f -- change field separator */
else if (strcmp(cmd, "f") == 0)
{
! 		char	   *fname = scan_option(&string, OT_NORMAL, NULL, false);

success = do_pset("fieldsep", fname, &pset.popt, quiet);
free(fname);
***************
*** 415,421 ****
/* \g means send query */
else if (strcmp(cmd, "g") == 0)
{
! char *fname = scan_option(&string, OT_FILEPIPE, NULL);

if (!fname)
pset.gfname = NULL;
--- 415,421 ----
/* \g means send query */
else if (strcmp(cmd, "g") == 0)
{
! 		char	   *fname = scan_option(&string, OT_FILEPIPE, NULL, false);

if (!fname)
pset.gfname = NULL;
***************
*** 447,453 ****
/* \i is include file */
else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0)
{
! char *fname = scan_option(&string, OT_NORMAL, NULL);

if (!fname)
{
--- 447,453 ----
/* \i is include file */
else if (strcmp(cmd, "i") == 0 || strcmp(cmd, "include") == 0)
{
! 		char	   *fname = scan_option(&string, OT_NORMAL, NULL, true);

if (!fname)
{
***************
*** 475,482 ****
char *opt1,
*opt2;

! opt1 = scan_option(&string, OT_NORMAL, NULL);
! opt2 = scan_option(&string, OT_NORMAL, NULL);

if (strcmp(cmd + 3, "export") == 0)
{
--- 475,482 ----
char	   *opt1,
*opt2;

! opt1 = scan_option(&string, OT_NORMAL, NULL, true);
! opt2 = scan_option(&string, OT_NORMAL, NULL, true);

if (strcmp(cmd + 3, "export") == 0)
{
***************
*** 525,531 ****
/* \o -- set query output */
else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0)
{
! char *fname = scan_option(&string, OT_FILEPIPE, NULL);

success = setQFout(fname);
free(fname);
--- 525,531 ----
/* \o -- set query output */
else if (strcmp(cmd, "o") == 0 || strcmp(cmd, "out") == 0)
{
! 		char	   *fname = scan_option(&string, OT_FILEPIPE, NULL, true);

success = setQFout(fname);
free(fname);
***************
*** 544,551 ****
/* \pset -- set printing parameters */
else if (strcmp(cmd, "pset") == 0)
{
! char *opt0 = scan_option(&string, OT_NORMAL, NULL);
! char *opt1 = scan_option(&string, OT_NORMAL, NULL);

if (!opt0)
{
--- 544,551 ----
/* \pset -- set printing parameters */
else if (strcmp(cmd, "pset") == 0)
{
! 		char	   *opt0 = scan_option(&string, OT_NORMAL, NULL, false);
! 		char	   *opt1 = scan_option(&string, OT_NORMAL, NULL, false);

if (!opt0)
{
***************
*** 574,580 ****
/* \s save history in a file or show it on the screen */
else if (strcmp(cmd, "s") == 0)
{
! char *fname = scan_option(&string, OT_NORMAL, NULL);

success = saveHistory(fname ? fname : "/dev/tty");

--- 574,580 ----
/* \s save history in a file or show it on the screen */
else if (strcmp(cmd, "s") == 0)
{
! 		char	   *fname = scan_option(&string, OT_NORMAL, NULL, true);

success = saveHistory(fname ? fname : "/dev/tty");

***************
*** 586,592 ****
/* \set -- generalized set variable/option command */
else if (strcmp(cmd, "set") == 0)
{
! char *opt0 = scan_option(&string, OT_NORMAL, NULL);

if (!opt0)
{
--- 586,592 ----
/* \set -- generalized set variable/option command */
else if (strcmp(cmd, "set") == 0)
{
! 		char	   *opt0 = scan_option(&string, OT_NORMAL, NULL, false);

if (!opt0)
{
***************
*** 611,621 ****
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)
--- 611,621 ----
char	   *newval = NULL;
char	   *opt;

! opt = scan_option(&string, OT_NORMAL, NULL, false);
newval = xstrdup(opt ? opt : "");
free(opt);

! while ((opt = scan_option(&string, OT_NORMAL, NULL, false)))
{
newval = realloc(newval, strlen(newval) + strlen(opt) + 1);
if (!newval)
***************
*** 645,651 ****
/* \T -- define html <table ...> attributes */
else if (strcmp(cmd, "T") == 0)
{
! char *value = scan_option(&string, OT_NORMAL, NULL);

success = do_pset("tableattr", value, &pset.popt, quiet);
free(value);
--- 645,651 ----
/* \T -- define html <table ...> attributes */
else if (strcmp(cmd, "T") == 0)
{
! 		char	   *value = scan_option(&string, OT_NORMAL, NULL, false);

success = do_pset("tableattr", value, &pset.popt, quiet);
free(value);
***************
*** 654,660 ****
/* \unset */
else if (strcmp(cmd, "unset") == 0)
{
! char *opt = scan_option(&string, OT_NORMAL, NULL);

if (!opt)
{
--- 654,660 ----
/* \unset */
else if (strcmp(cmd, "unset") == 0)
{
! 		char	   *opt = scan_option(&string, OT_NORMAL, NULL, false);

if (!opt)
{
***************
*** 683,689 ****
}
else
{
! fname = scan_option(&string, OT_FILEPIPE, NULL);

if (!fname)
{
--- 683,689 ----
}
else
{
! 			fname = scan_option(&string, OT_FILEPIPE, NULL, true);

if (!fname)
{
***************
*** 738,744 ****
/* \z -- list table rights (grant/revoke) */
else if (strcmp(cmd, "z") == 0)
{
! char *opt = scan_option(&string, OT_SQLID, NULL);

success = permissionsList(opt);
free(opt);
--- 738,744 ----
/* \z -- list table rights (grant/revoke) */
else if (strcmp(cmd, "z") == 0)
{
! 		char	   *opt = scan_option(&string, OT_SQLID, NULL, true);

success = permissionsList(opt);
free(opt);
***************
*** 769,775 ****
char *value;

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);
--- 769,775 ----
char	   *value;

fprintf(stderr, "+ optstr = |%s|\n", options_string);
! while ((value = scan_option(&string, OT_NORMAL, NULL, true)))
{
fprintf(stderr, "+ opt(%d) = |%s|\n", i++, value);
free(value);
***************
*** 784,790 ****
status = CMD_ERROR;

/* eat the rest of the options string */
! 	while ((val = scan_option(&string, OT_NORMAL, NULL)))
{
if (status != CMD_UNKNOWN)
psql_error("\\%s: extra argument '%s' ignored\n", cmd, val);
--- 784,790 ----
status = CMD_ERROR;
/* eat the rest of the options string */
! 	while ((val = scan_option(&string, OT_NORMAL, NULL, false)))
{
if (status != CMD_UNKNOWN)
psql_error("\\%s: extra argument '%s' ignored\n", cmd, val);
***************
*** 803,809 ****
* scan_option()
*/
static char *
! scan_option(char **string, enum option_type type, char *quote)
{
unsigned int pos = 0;
char	   *options_string;
--- 803,809 ----
* scan_option()
*/
static char *
! scan_option(char **string, enum option_type type, char *quote, bool semicolon)
{
unsigned int pos = 0;
char	   *options_string;
***************
*** 860,865 ****
--- 860,866 ----
* If this is expected to be an SQL identifier like option
* then we strip out the double quotes
*/
+ 
if (type == OT_SQLID)
{
unsigned int k,
***************
*** 891,897 ****
*string = options_string + jj + 1;
if (quote)
*quote = '"';
! 
return return_val;
}
--- 892,898 ----
*string = options_string + jj + 1;
if (quote)
*quote = '"';
! 			
return return_val;
}
***************
*** 1071,1076 ****
--- 1072,1084 ----
}
strncpy(return_val, &options_string[pos], token_end);
return_val[token_end] = 0;
+ 
+ 				/* Strip any trailing semi-colons for some types */
+ 				if (semicolon) {
+ 					int i;
+ 					for (i = strlen(return_val)-1; i && return_val[i]==';'; i--);
+ 					if (i<strlen(return_val)-1) return_val[i+1]='\0';
+ 				}

if (type == OT_SQLID)
for (cp = return_val; *cp; cp += PQmblen(cp, pset.encoding))

---------------------------(end of broadcast)---------------------------
TIP 1: subscribe and unsubscribe commands go to majordomo@postgresql.org

-- 
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 853-3000
  +  If your life is a hard drive,     |  830 Blythe Avenue
  +  Christ can be your backup.        |  Drexel Hill, Pennsylvania 19026