Hi

2015-11-25 17:13 GMT+01:00 Catalin Iacob <iacobcata...@gmail.com>:

> On Wed, Nov 18, 2015 at 5:49 PM, Catalin Iacob <iacobcata...@gmail.com>
> wrote:
> > On Tue, Nov 17, 2015 at 10:13 PM, Tom Lane <t...@sss.pgh.pa.us> wrote:
> >> 1. -c no longer implies --no-psqlrc.  That's a backwards
> incompatibility,
> >> but very easy to explain and very easy to work around.
> >>
> >> 2. You can have multiple -c and/or -f.  Each -c is processed in
> >> the traditional way, ie, either it's a single backslash command
> >> or it's sent in a single PQexec.  That doesn't seem to me to have
> >> much impact on the behavior of adjacent -c or -f.
> >>
> >> 3. If you combine -1 with -c and/or -f, you get one BEGIN inserted
> >> at the beginning and one COMMIT at the end.  Nothing else changes.
>
> > I'll try to write the documentation patch for these semantics sometime
> > next week.
>
> Attached is my attempt at a documentation patch, feedback welcome. I'm
> assuming Pavel will pick up the implementation, if not I could also
> try it.
>

I am sorry for delay - the end of year :(

Attached patch per Tom Lane proposal.

* multiple -c -f options are supported, the order of options is respected
* the statements for one -c options are executed in transactions
* Iacob's doc patch merged

Regards

Pavel
diff --git a/doc/src/sgml/ref/psql-ref.sgml b/doc/src/sgml/ref/psql-ref.sgml
new file mode 100644
index 5899bb4..2928c92
*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
*************** PostgreSQL documentation
*** 38,46 ****
       <productname>PostgreSQL</productname>. It enables you to type in
       queries interactively, issue them to
       <productname>PostgreSQL</productname>, and see the query results.
!      Alternatively, input can be from a file. In addition, it provides a
!      number of meta-commands and various shell-like features to
!      facilitate writing scripts and automating a wide variety of tasks.
      </para>
   </refsect1>
  
--- 38,47 ----
       <productname>PostgreSQL</productname>. It enables you to type in
       queries interactively, issue them to
       <productname>PostgreSQL</productname>, and see the query results.
!      Alternatively, input can be from a file or from command line
!      arguments. In addition, it provides a number of meta-commands and various
!      shell-like features to facilitate writing scripts and automating a wide
!      variety of tasks.
      </para>
   </refsect1>
  
*************** PostgreSQL documentation
*** 89,126 ****
        <term><option>--command=<replaceable class="parameter">command</replaceable></></term>
        <listitem>
        <para>
!       Specifies that <application>psql</application> is to execute one
!       command string, <replaceable class="parameter">command</replaceable>,
!       and then exit. This is useful in shell scripts. Start-up files
!       (<filename>psqlrc</filename> and <filename>~/.psqlrc</filename>) are
!       ignored with this option.
        </para>
        <para>
!       <replaceable class="parameter">command</replaceable> must be either
!       a command string that is completely parsable by the server (i.e.,
!       it contains no <application>psql</application>-specific features),
!       or a single backslash command. Thus you cannot mix
!       <acronym>SQL</acronym> and <application>psql</application>
!       meta-commands with this option. To achieve that, you could
!       pipe the string into <application>psql</application>, for example:
!       <literal>echo '\x \\ SELECT * FROM foo;' | psql</literal>.
        (<literal>\\</> is the separator meta-command.)
        </para>
        <para>
!        If the command string contains multiple SQL commands, they are
!        processed in a single transaction, unless there are explicit
!        <command>BEGIN</>/<command>COMMIT</> commands included in the
!        string to divide it into multiple transactions.  This is
!        different from the behavior when the same string is fed to
!        <application>psql</application>'s standard input.  Also, only
!        the result of the last SQL command is returned.
        </para>
        <para>
!        Because of these legacy behaviors, putting more than one command in
!        the <option>-c</option> string often has unexpected results.  It's
!        better to feed multiple commands to <application>psql</application>'s
!        standard input, either using <application>echo</application> as
!        illustrated above, or via a shell here-document, for example:
  <programlisting>
  psql &lt;&lt;EOF
  \x
--- 90,134 ----
        <term><option>--command=<replaceable class="parameter">command</replaceable></></term>
        <listitem>
        <para>
!       Specifies that <application>psql</application> is to execute the given
!       command string, <replaceable class="parameter">command</replaceable>.
!       This option can be repeated and combined in any order with
!       the <option>-f</option> option.
        </para>
        <para>
!       <replaceable class="parameter">command</replaceable> must be either a
!       command string that is completely parsable by the server (i.e., it
!       contains no <application>psql</application>-specific features), or a
!       single backslash command. Thus you cannot mix
!       <acronym>SQL</acronym> and <application>psql</application> meta-commands
!       with this option. To achieve that, you could use
!       repeated <option>-c</option> options or pipe the string
!       into <application>psql</application>, for example:
!       <literal>psql -c '\x' -c 'SELECT * FROM foo;'</literal> or
!       <literal>echo '\x \\ SELECT * FROM foo;' | psql</literal>
        (<literal>\\</> is the separator meta-command.)
        </para>
        <para>
!        Each command string passed to <option>-c</option> is sent to the server
!        as a single query. Because of this, the server executes it as a single
!        transaction, even if a command string contains
!        multiple <acronym>SQL</acronym> commands, unless there are
!        explicit <command>BEGIN</>/<command>COMMIT</> commands included in the
!        string to divide it into multiple transactions.  Also, the server only
!        returns the result of the last <acronym>SQL</acronym> command to the
!        client.  This is different from the behavior when the same string with
!        multiple <acronym>SQL</acronym> commands is fed
!        to <application>psql</application>'s standard input because
!        then <application>psql</application> sends each <acronym>SQL</acronym>
!        command separately.
        </para>
        <para>
!        Because of the execution as a single query, putting more than one
!        command in the <option>-c</option> string often has unexpected results.
!        It's better to use repeated <option>-c</option> commands or feed
!        multiple commands to <application>psql</application>'s standard input,
!        either using <application>echo</application> as illustrated above, or
!        via a shell here-document, for example:
  <programlisting>
  psql &lt;&lt;EOF
  \x
*************** EOF
*** 183,193 ****
        <term><option>--file=<replaceable class="parameter">filename</replaceable></></term>
        <listitem>
        <para>
!       Use the file <replaceable class="parameter">filename</replaceable>
!       as the source of commands instead of reading commands interactively.
!       After the file is processed, <application>psql</application>
!       terminates. This is in many ways equivalent to the meta-command
!       <command>\i</command>.
        </para>
  
        <para>
--- 191,203 ----
        <term><option>--file=<replaceable class="parameter">filename</replaceable></></term>
        <listitem>
        <para>
!       Use the file <replaceable class="parameter">filename</replaceable> as
!       the source of commands instead of reading commands interactively.  This
!       option can be repeated and combined in any order with
!       the <option>-c</option> option.  After the commands in
!       every <option>-c</option> command string and <option>-f</option> file
!       are processed, <application>psql</application> terminates.  This option
!       is in many ways equivalent to the meta-command <command>\i</command>.
        </para>
  
        <para>
*************** EOF
*** 539,558 ****
        <term><option>--single-transaction</option></term>
        <listitem>
         <para>
!         When <application>psql</application> executes a script, adding
!         this option wraps <command>BEGIN</>/<command>COMMIT</> around the
!         script to execute it as a single transaction.  This ensures that
!         either all the commands complete successfully, or no changes are
!         applied.
         </para>
  
         <para>
!         If the script itself uses <command>BEGIN</>, <command>COMMIT</>,
          or <command>ROLLBACK</>, this option will not have the desired
!         effects.
!         Also, if the script contains any command that cannot be executed
!         inside a transaction block, specifying this option will cause that
!         command (and hence the whole transaction) to fail.
         </para>
        </listitem>
       </varlistentry>
--- 549,569 ----
        <term><option>--single-transaction</option></term>
        <listitem>
         <para>
!         When <application>psql</application> executes commands from a script
!         and/or a <option>-c</option> option, adding this option
!         wraps <command>BEGIN</>/<command>COMMIT</> around all of those
!         commands as a whole to execute them as a single transaction.  This
!         ensures that either all the commands complete successfully, or no
!         changes are applied.
         </para>
  
         <para>
!         If the commands themselves
!         contain <command>BEGIN</>, <command>COMMIT</>,
          or <command>ROLLBACK</>, this option will not have the desired
!         effects.  Also, if an individual command cannot be executed inside a
!         transaction block, specifying this option will cause the whole
!         transaction to fail.
         </para>
        </listitem>
       </varlistentry>
*************** PSQL_EDITOR_LINENUMBER_ARG='--line '
*** 3725,3731 ****
     <term><filename>psqlrc</filename> and <filename>~/.psqlrc</filename></term>
     <listitem>
      <para>
!      Unless it is passed an <option>-X</option> or <option>-c</option> option,
       <application>psql</application> attempts to read and execute commands
       from the system-wide startup file (<filename>psqlrc</filename>) and then
       the user's personal startup file (<filename>~/.psqlrc</filename>), after
--- 3736,3742 ----
     <term><filename>psqlrc</filename> and <filename>~/.psqlrc</filename></term>
     <listitem>
      <para>
!      Unless it is passed an <option>-X</option> option,
       <application>psql</application> attempts to read and execute commands
       from the system-wide startup file (<filename>psqlrc</filename>) and then
       the user's personal startup file (<filename>~/.psqlrc</filename>), after
*************** PSQL_EDITOR_LINENUMBER_ARG='--line '
*** 3819,3824 ****
--- 3830,3842 ----
        </para>
        </listitem>
  
+       <listitem>
+       <para>
+        Before <productname>PostgreSQL</productname> 9.6, <option>-c</option>
+        used to imply <option>-X</option> and therefore it wouldn't
+        read <filename>psqlrc</filename> files, that is no longer the case.
+       </para>
+       </listitem>
      </itemizedlist>
   </refsect1>
  
diff --git a/src/bin/psql/command.c b/src/bin/psql/command.c
new file mode 100644
index 72c00c1..5e3b4ed
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
*************** exec_command(const char *cmd,
*** 918,924 ****
  			include_relative = (strcmp(cmd, "ir") == 0
  								|| strcmp(cmd, "include_relative") == 0);
  			expand_tilde(&fname);
! 			success = (process_file(fname, false, include_relative) == EXIT_SUCCESS);
  			free(fname);
  		}
  	}
--- 918,924 ----
  			include_relative = (strcmp(cmd, "ir") == 0
  								|| strcmp(cmd, "include_relative") == 0);
  			expand_tilde(&fname);
! 			success = (process_file(fname, include_relative) == EXIT_SUCCESS);
  			free(fname);
  		}
  	}
*************** do_edit(const char *filename_arg, PQExpB
*** 2287,2299 ****
   * the file from where the currently processed file (if any) is located.
   */
  int
! process_file(char *filename, bool single_txn, bool use_relative_path)
  {
  	FILE	   *fd;
  	int			result;
  	char	   *oldfilename;
  	char		relpath[MAXPGPATH];
- 	PGresult   *res;
  
  	if (!filename)
  	{
--- 2287,2298 ----
   * the file from where the currently processed file (if any) is located.
   */
  int
! process_file(char *filename, bool use_relative_path)
  {
  	FILE	   *fd;
  	int			result;
  	char	   *oldfilename;
  	char		relpath[MAXPGPATH];
  
  	if (!filename)
  	{
*************** process_file(char *filename, bool single
*** 2338,2374 ****
  	oldfilename = pset.inputfile;
  	pset.inputfile = filename;
  
- 	if (single_txn)
- 	{
- 		if ((res = PSQLexec("BEGIN")) == NULL)
- 		{
- 			if (pset.on_error_stop)
- 			{
- 				result = EXIT_USER;
- 				goto error;
- 			}
- 		}
- 		else
- 			PQclear(res);
- 	}
- 
  	result = MainLoop(fd);
  
- 	if (single_txn)
- 	{
- 		if ((res = PSQLexec("COMMIT")) == NULL)
- 		{
- 			if (pset.on_error_stop)
- 			{
- 				result = EXIT_USER;
- 				goto error;
- 			}
- 		}
- 		else
- 			PQclear(res);
- 	}
- 
- error:
  	if (fd != stdin)
  		fclose(fd);
  
--- 2337,2344 ----
diff --git a/src/bin/psql/command.h b/src/bin/psql/command.h
new file mode 100644
index 54385e8..c817600
*** a/src/bin/psql/command.h
--- b/src/bin/psql/command.h
*************** typedef enum _backslashResult
*** 27,33 ****
  extern backslashResult HandleSlashCmds(PsqlScanState scan_state,
  				PQExpBuffer query_buf);
  
! extern int	process_file(char *filename, bool single_txn, bool use_relative_path);
  
  extern bool do_pset(const char *param,
  		const char *value,
--- 27,33 ----
  extern backslashResult HandleSlashCmds(PsqlScanState scan_state,
  				PQExpBuffer query_buf);
  
! extern int	process_file(char *filename, bool use_relative_path);
  
  extern bool do_pset(const char *param,
  		const char *value,
diff --git a/src/bin/psql/common.c b/src/bin/psql/common.c
new file mode 100644
index 0e266a3..fff16c0
*** a/src/bin/psql/common.c
--- b/src/bin/psql/common.c
*************** recognized_connection_string(const char
*** 1886,1888 ****
--- 1886,1911 ----
  {
  	return uri_prefix_length(connstr) != 0 || strchr(connstr, '=') != NULL;
  }
+ 
+ /*
+  * Support for list of actions. The SimpleStringList cannot be used due possible
+  * combination different actions with the requirement to save the order.
+  */
+ void
+ simple_action_list_append(SimpleActionList *list, int atyp, const char *val)
+ {
+ 	SimpleActionListCell  *cell;
+ 
+ 	cell = (SimpleActionListCell *)
+ 		pg_malloc(offsetof(SimpleActionListCell, val) + strlen(val) + 1);
+ 
+ 	cell->next = NULL;
+ 	cell->atyp = atyp;
+ 	strcpy(cell->val, val);
+ 
+ 	if (list->tail)
+ 		list->tail->next = cell;
+ 	else
+ 		list->head = cell;
+ 	list->tail = cell;
+ }
diff --git a/src/bin/psql/common.h b/src/bin/psql/common.h
new file mode 100644
index caf31d1..0965c35
*** a/src/bin/psql/common.h
--- b/src/bin/psql/common.h
***************
*** 14,21 ****
--- 14,43 ----
  
  #include "print.h"
  
+ enum _atypes
+ {
+ 	ACT_SINGLE_QUERY,
+ 	ACT_SINGLE_SLASH,
+ 	ACT_FILE
+ };
+ 
+ typedef struct SimpleActionListCell
+ {
+ 	struct SimpleActionListCell *next;
+ 	int 		atyp;
+ 	char		val[FLEXIBLE_ARRAY_MEMBER];
+ } SimpleActionListCell;
+ 
+ typedef struct SimpleActionList
+ {
+ 	SimpleActionListCell *head;
+ 	SimpleActionListCell *tail;
+ } SimpleActionList;
+ 
  #define atooid(x)  ((Oid) strtoul((x), NULL, 10))
  
+ extern void simple_action_list_append(SimpleActionList *list, int atyp, const char *val);
+ 
  extern bool setQFout(const char *fname);
  
  extern void psql_error(const char *fmt,...) pg_attribute_printf(1, 2);
diff --git a/src/bin/psql/startup.c b/src/bin/psql/startup.c
new file mode 100644
index 7aa997d..eda779b
*** a/src/bin/psql/startup.c
--- b/src/bin/psql/startup.c
*************** PsqlSettings pset;
*** 51,60 ****
  enum _actions
  {
  	ACT_NOTHING = 0,
- 	ACT_SINGLE_SLASH,
  	ACT_LIST_DB,
! 	ACT_SINGLE_QUERY,
! 	ACT_FILE
  };
  
  struct adhoc_opts
--- 51,58 ----
  enum _actions
  {
  	ACT_NOTHING = 0,
  	ACT_LIST_DB,
! 	ACT_FILE_STDIN
  };
  
  struct adhoc_opts
*************** struct adhoc_opts
*** 69,74 ****
--- 67,73 ----
  	bool		no_readline;
  	bool		no_psqlrc;
  	bool		single_txn;
+ 	SimpleActionList	actions;
  };
  
  static void parse_psql_options(int argc, char *argv[],
*************** main(int argc, char *argv[])
*** 159,164 ****
--- 158,166 ----
  	SetVariable(pset.vars, "PROMPT2", DEFAULT_PROMPT2);
  	SetVariable(pset.vars, "PROMPT3", DEFAULT_PROMPT3);
  
+ 	options.actions.head = NULL;
+ 	options.actions.tail = NULL;
+ 
  	parse_psql_options(argc, argv, &options);
  
  	/*
*************** main(int argc, char *argv[])
*** 166,179 ****
  	 * as if the user had specified "-f -".  This lets single-transaction mode
  	 * work in this case.
  	 */
! 	if (options.action == ACT_NOTHING && pset.notty)
  	{
! 		options.action = ACT_FILE;
  		options.action_string = NULL;
  	}
  
  	/* Bail out if -1 was specified but will be ignored. */
! 	if (options.single_txn && options.action != ACT_FILE && options.action == ACT_NOTHING)
  	{
  		fprintf(stderr, _("%s: -1 can only be used in non-interactive mode\n"), pset.progname);
  		exit(EXIT_FAILURE);
--- 168,182 ----
  	 * as if the user had specified "-f -".  This lets single-transaction mode
  	 * work in this case.
  	 */
! 	if (options.action == ACT_NOTHING && options.actions.head == NULL && pset.notty)
  	{
! 		options.action = ACT_FILE_STDIN;
  		options.action_string = NULL;
  	}
  
  	/* Bail out if -1 was specified but will be ignored. */
! 	if (options.single_txn && options.action != ACT_FILE_STDIN && options.action == ACT_NOTHING
! 			&& options.actions.head == NULL)
  	{
  		fprintf(stderr, _("%s: -1 can only be used in non-interactive mode\n"), pset.progname);
  		exit(EXIT_FAILURE);
*************** main(int argc, char *argv[])
*** 282,332 ****
  	/*
  	 * Now find something to do
  	 */
! 
! 	/*
! 	 * process file given by -f
! 	 */
! 	if (options.action == ACT_FILE)
  	{
  		if (!options.no_psqlrc)
  			process_psqlrc(argv[0]);
  
! 		successResult = process_file(options.action_string, options.single_txn, false);
! 	}
  
! 	/*
! 	 * process slash command if one was given to -c
! 	 */
! 	else if (options.action == ACT_SINGLE_SLASH)
! 	{
! 		PsqlScanState scan_state;
  
! 		if (pset.echo == PSQL_ECHO_ALL)
! 			puts(options.action_string);
  
! 		scan_state = psql_scan_create();
! 		psql_scan_setup(scan_state,
! 						options.action_string,
! 						strlen(options.action_string));
  
! 		successResult = HandleSlashCmds(scan_state, NULL) != PSQL_CMD_ERROR
! 			? EXIT_SUCCESS : EXIT_FAILURE;
  
! 		psql_scan_destroy(scan_state);
! 	}
  
! 	/*
! 	 * If the query given to -c was a normal one, send it
! 	 */
! 	else if (options.action == ACT_SINGLE_QUERY)
! 	{
! 		if (pset.echo == PSQL_ECHO_ALL)
! 			puts(options.action_string);
  
! 		successResult = SendQuery(options.action_string)
! 			? EXIT_SUCCESS : EXIT_FAILURE;
! 	}
  
  	/*
  	 * or otherwise enter interactive main loop
  	 */
--- 285,368 ----
  	/*
  	 * Now find something to do
  	 */
! 	if (options.actions.head)
  	{
+ 		PGresult		*res;
+ 		SimpleActionListCell	*cell;
+ 
  		if (!options.no_psqlrc)
  			process_psqlrc(argv[0]);
  
! 		successResult = EXIT_SUCCESS;		/* be compiler quiete */
  
! 		if (options.single_txn)
! 		{
! 			if ((res = PSQLexec("BEGIN")) == NULL)
! 			{
! 				if (pset.on_error_stop)
! 				{
! 					successResult = EXIT_USER;
! 					goto error;
! 				}
! 			}
! 			else
! 				PQclear(res);
! 		}
  
! 		for (cell = options.actions.head; cell; cell = cell->next)
! 		{
! 			if (cell->atyp == ACT_SINGLE_QUERY)
! 			{
! 				if (pset.echo == PSQL_ECHO_ALL)
! 					puts(cell->val);
  
! 				successResult = SendQuery(cell->val)
! 					? EXIT_SUCCESS : EXIT_FAILURE;
! 			}
! 			else if (cell->atyp == ACT_SINGLE_SLASH)
! 			{
! 				PsqlScanState scan_state;
  
! 				if (pset.echo == PSQL_ECHO_ALL)
! 					puts(cell->val);
  
! 				scan_state = psql_scan_create();
! 				psql_scan_setup(scan_state,
! 							cell->val,
! 							strlen(cell->val));
  
! 				successResult = HandleSlashCmds(scan_state, NULL) != PSQL_CMD_ERROR
! 					? EXIT_SUCCESS : EXIT_FAILURE;
  
! 				psql_scan_destroy(scan_state);
! 			}
! 			else
! 			{
! 				/* ACT_FILE */
! 				successResult = process_file(cell->val, false);
! 			}
! 
! 			if (successResult != EXIT_SUCCESS && pset.on_error_stop)
! 				break;
! 		}
! 
! 		if (options.single_txn)
! 		{
! 			if ((res = PSQLexec("COMMIT")) == NULL)
! 			{
! 				if (pset.on_error_stop)
! 				{
! 					successResult = EXIT_USER;
! 					goto error;
! 				}
! 			}
! 			else
! 				PQclear(res);
! 		}
  
+ error:
+ 		;
+ 	}
  	/*
  	 * or otherwise enter interactive main loop
  	 */
*************** parse_psql_options(int argc, char *argv[
*** 419,432 ****
  				SetVariable(pset.vars, "ECHO", "errors");
  				break;
  			case 'c':
- 				options->action_string = pg_strdup(optarg);
  				if (optarg[0] == '\\')
! 				{
! 					options->action = ACT_SINGLE_SLASH;
! 					options->action_string++;
! 				}
  				else
! 					options->action = ACT_SINGLE_QUERY;
  				break;
  			case 'd':
  				options->dbname = pg_strdup(optarg);
--- 455,468 ----
  				SetVariable(pset.vars, "ECHO", "errors");
  				break;
  			case 'c':
  				if (optarg[0] == '\\')
! 					simple_action_list_append(&options->actions,
! 								    ACT_SINGLE_SLASH,
! 								    pstrdup(optarg + 1));
  				else
! 					simple_action_list_append(&options->actions,
! 								    ACT_SINGLE_QUERY,
! 								    pstrdup(optarg));
  				break;
  			case 'd':
  				options->dbname = pg_strdup(optarg);
*************** parse_psql_options(int argc, char *argv[
*** 438,445 ****
  				SetVariableBool(pset.vars, "ECHO_HIDDEN");
  				break;
  			case 'f':
! 				options->action = ACT_FILE;
! 				options->action_string = pg_strdup(optarg);
  				break;
  			case 'F':
  				pset.popt.topt.fieldSep.separator = pg_strdup(optarg);
--- 474,482 ----
  				SetVariableBool(pset.vars, "ECHO_HIDDEN");
  				break;
  			case 'f':
! 				simple_action_list_append(&options->actions,
! 								ACT_FILE,
! 									    pg_strdup(optarg));
  				break;
  			case 'F':
  				pset.popt.topt.fieldSep.separator = pg_strdup(optarg);
*************** process_psqlrc_file(char *filename)
*** 674,684 ****
  
  	/* check for minor version first, then major, then no version */
  	if (access(psqlrc_minor, R_OK) == 0)
! 		(void) process_file(psqlrc_minor, false, false);
  	else if (access(psqlrc_major, R_OK) == 0)
! 		(void) process_file(psqlrc_major, false, false);
  	else if (access(filename, R_OK) == 0)
! 		(void) process_file(filename, false, false);
  
  	free(psqlrc_minor);
  	free(psqlrc_major);
--- 711,721 ----
  
  	/* check for minor version first, then major, then no version */
  	if (access(psqlrc_minor, R_OK) == 0)
! 		(void) process_file(psqlrc_minor, false);
  	else if (access(psqlrc_major, R_OK) == 0)
! 		(void) process_file(psqlrc_major, false);
  	else if (access(filename, R_OK) == 0)
! 		(void) process_file(filename, false);
  
  	free(psqlrc_minor);
  	free(psqlrc_major);
-- 
Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org)
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers

Reply via email to