Re: Continue transactions after errors in psql - Mailing list pgsql-patches

From Bruce Momjian
Subject Re: Continue transactions after errors in psql
Date
Msg-id 200504251634.j3PGY0Z11527@candle.pha.pa.us
Whole thread Raw
In response to Re: Continue transactions after errors in psql  ("Greg Sabino Mullane" <greg@turnstep.com>)
Responses Re: Continue transactions after errors in psql
List pgsql-patches
Greg Sabino Mullane wrote:
[ There is text before PGP section. ]
>
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
>
> Finally had a chance to sit down at look at this afresh, and I'm
> pretty sure I've got all the kinks worked out this time. Apologies
> for not attaching, but my mail system is not working well enough
> at the moment. So, please try to break this patch:
>
> http://www.gtsm.com/pg/psql_error_recovery.diff

I have modified Greg's patch to fit in better with the existing psql
code.  I changed the command to \set ON_ERROR_ROLLBACK, which seems to
fit better.  Here is an illustration of its use (patch attached):

    test=> BEGIN;
    BEGIN
    test=> lkjasdf;
    ERROR:  syntax error at or near "lkjasdf" at character 1
    LINE 1: lkjasdf;
            ^
    test=> SELECT 1;
    ERROR:  current transaction is aborted, commands ignored until end of
    transaction block
    test=> COMMIT;
    ROLLBACK
    test=> \set ON_ERROR_ROLLBACK on
    test=> BEGIN;
    BEGIN
    test=> lkjasdf;
    ERROR:  syntax error at or near "lkjasdf" at character 1
    LINE 1: lkjasdf;
            ^
    test=> SELECT 1;
     ?column?
    ----------
            1
    (1 row)

    test=> COMMIT;
    COMMIT

--
  Bruce Momjian                        |  http://candle.pha.pa.us
  pgman@candle.pha.pa.us               |  (610) 359-1001
  +  If your life is a hard drive,     |  13 Roberts Road
  +  Christ can be your backup.        |  Newtown Square, Pennsylvania 19073
Index: doc/src/sgml/ref/psql-ref.sgml
===================================================================
RCS file: /cvsroot/pgsql/doc/src/sgml/ref/psql-ref.sgml,v
retrieving revision 1.134
diff -c -c -r1.134 psql-ref.sgml
*** doc/src/sgml/ref/psql-ref.sgml    14 Mar 2005 06:19:01 -0000    1.134
--- doc/src/sgml/ref/psql-ref.sgml    25 Apr 2005 16:26:03 -0000
***************
*** 2050,2055 ****
--- 2050,2070 ----
        </varlistentry>

        <varlistentry>
+       <indexterm>
+        <primary>rollback</primary>
+        <secondary>psql</secondary>
+       </indexterm>
+         <term><varname>ON_ERROR_ROLLBACK</varname></term>
+         <listitem>
+         <para>
+         When in a transaction, wrap every command in a savepoint that is
+         rolled back on error. Thus, errors will not abort an open
+         transaction.
+         </para>
+         </listitem>
+       </varlistentry>
+
+       <varlistentry>
          <term><varname>ON_ERROR_STOP</varname></term>
          <listitem>
          <para>
Index: src/bin/psql/common.c
===================================================================
RCS file: /cvsroot/pgsql/src/bin/psql/common.c,v
retrieving revision 1.96
diff -c -c -r1.96 common.c
*** src/bin/psql/common.c    22 Feb 2005 04:40:52 -0000    1.96
--- src/bin/psql/common.c    25 Apr 2005 16:26:05 -0000
***************
*** 941,950 ****
  bool
  SendQuery(const char *query)
  {
!     PGresult   *results;
!     TimevalStruct before,
!                 after;
!     bool        OK;

      if (!pset.db)
      {
--- 941,950 ----
  bool
  SendQuery(const char *query)
  {
!     PGresult     *results;
!     TimevalStruct before, after;
!     bool OK, on_error_rollback_savepoint = false;
!     PGTransactionStatusType transaction_status;

      if (!pset.db)
      {
***************
*** 973,979 ****

      SetCancelConn();

!     if (PQtransactionStatus(pset.db) == PQTRANS_IDLE &&
          !GetVariableBool(pset.vars, "AUTOCOMMIT") &&
          !command_no_begin(query))
      {
--- 973,981 ----

      SetCancelConn();

!     transaction_status = PQtransactionStatus(pset.db);
!
!     if (transaction_status == PQTRANS_IDLE &&
          !GetVariableBool(pset.vars, "AUTOCOMMIT") &&
          !command_no_begin(query))
      {
***************
*** 987,992 ****
--- 989,1016 ----
          }
          PQclear(results);
      }
+     else if (transaction_status == PQTRANS_INTRANS &&
+              GetVariableBool(pset.vars, "ON_ERROR_ROLLBACK"))
+     {
+         if (pset.sversion < 80000)
+         {
+             fprintf(stderr, _("The server version (%d) does not support savepoints for ON_ERROR_ROLLBACK.\n"),
+                 pset.sversion);
+         }
+         else
+         {
+             results = PQexec(pset.db, "SAVEPOINT pg_psql_temporary_savepoint");
+             if (PQresultStatus(results) != PGRES_COMMAND_OK)
+             {
+                 psql_error("%s", PQerrorMessage(pset.db));
+                 PQclear(results);
+                 ResetCancelConn();
+                 return false;
+             }
+             PQclear(results);
+             on_error_rollback_savepoint = true;
+         }
+     }

      if (pset.timing)
          GETTIMEOFDAY(&before);
***************
*** 1005,1010 ****
--- 1029,1069 ----

      PQclear(results);

+     /* If we made a temporary savepoint, possibly release/rollback */
+     if (on_error_rollback_savepoint)
+     {
+         transaction_status = PQtransactionStatus(pset.db);
+
+         /* We always rollback on an error */
+         if (transaction_status == PQTRANS_INERROR)
+             results = PQexec(pset.db, "ROLLBACK TO pg_psql_temporary_savepoint");
+         /* If they are no longer in a transaction, then do nothing */
+         else if (transaction_status != PQTRANS_INTRANS)
+             results = NULL;
+         else
+         {
+             /*
+              *     Do nothing if they are messing with savepoints themselves:
+              *     doing otherwise would cause us to remove their savepoint,
+              *     or have us rollback our savepoint they have just removed
+              */
+             if (strcmp(PQcmdStatus(results), "SAVEPOINT") == 0 ||
+                 strcmp(PQcmdStatus(results), "RELEASE") == 0 ||
+                 strcmp(PQcmdStatus(results), "ROLLBACK") ==0)
+                 results = NULL;
+             else
+                 results = PQexec(pset.db, "RELEASE pg_psql_temporary_savepoint");
+         }
+         if (PQresultStatus(results) != PGRES_COMMAND_OK)
+         {
+             psql_error("%s", PQerrorMessage(pset.db));
+             PQclear(results);
+             ResetCancelConn();
+             return false;
+         }
+         PQclear(results);
+     }
+
      /* Possible microtiming output */
      if (OK && pset.timing)
          printf(_("Time: %.3f ms\n"), DIFF_MSEC(&after, &before));

pgsql-patches by date:

Previous
From: Bruce Momjian
Date:
Subject: Re: Cleaning up unreferenced table files
Next
From: Alvaro Herrera
Date:
Subject: Re: Continue transactions after errors in psql