On Wed, Mar 26, 2014 at 5:53 PM, Heikki Linnakangas
<hlinnakan...@vmware.com> wrote:
> * I'm not too fond of the RollbackError name. It sounds like "an error while
> rolling back". I googled around and found out that DataDirect's proprietary
> driver has the same option, and they call it TransactionErrorBehavior. See
> http://blogs.datadirect.com/2013/07/solution-unexpected-postgres-current-transaction-aborted-error.html.
>
> * Instead of using 0-2 as the values, let's give them descriptive names.
> Something like "none", "RollbackTransaction", "RollbackStatement".
> (actually, we'll probably want to also allow the integers, to keep the
> connection string short, as there is a size limit on that)
OK, I have been working more on that, giving the attached patch. The
parameter name is changed to TransactionErrorBehavior, able to use the
values "Statement", "Nop", "Transaction" and "Default". "Default"
corresponds to the default behavior, that is used to set the rollback
behavior depending on the Postgres version driver is connected with.
As of now, TransactionErrorBehavior does not accept integer values as
it makes the patch a bit more simple, this could be added with some
atoi calls in dlg_specific.c.

I have updated dlg_wingui.c as well. Patch has always its set of docs
and regression tests.

Regards,
-- 
Michael
diff --git a/connection.c b/connection.c
index 8601cb7..8361691 100644
--- a/connection.c
+++ b/connection.c
@@ -288,7 +288,7 @@ CC_conninfo_init(ConnInfo *conninfo, UInt4 option)
        conninfo->bytea_as_longvarbinary = -1;
        conninfo->use_server_side_prepare = -1;
        conninfo->lower_case_identifier = -1;
-       conninfo->rollback_on_error = -1;
+       conninfo->transaction_error_value = TRANSACTION_ERROR_DEFAULT;
        conninfo->force_abbrev_connstr = -1;
        conninfo->bde_environment = -1;
        conninfo->fake_mss = -1;
@@ -321,6 +321,7 @@ CC_copy_conninfo(ConnInfo *ci, const ConnInfo *sci)
        CORR_STRCPY(username);
        NAME_TO_NAME(ci->password, sci->password);
        CORR_STRCPY(protocol);
+       CORR_STRCPY(transaction_error);
        CORR_STRCPY(port);
        CORR_STRCPY(sslmode);
        CORR_STRCPY(onlyread);
@@ -341,7 +342,7 @@ CC_copy_conninfo(ConnInfo *ci, const ConnInfo *sci)
        CORR_VALCPY(bytea_as_longvarbinary);
        CORR_VALCPY(use_server_side_prepare);
        CORR_VALCPY(lower_case_identifier);
-       CORR_VALCPY(rollback_on_error);
+       CORR_VALCPY(transaction_error_value);
        CORR_VALCPY(force_abbrev_connstr);
        CORR_VALCPY(bde_environment);
        CORR_VALCPY(fake_mss);
@@ -2370,7 +2371,7 @@ int       CC_get_max_idlen(ConnectionClass *self)
        {
                QResultClass    *res;
 
-               res = CC_send_query(self, "show max_identifier_length", NULL, 
ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL);
+               res = CC_send_query(self, "show max_identifier_length", NULL, 
TRANSACTION_ERROR | IGNORE_ABORT_ON_CONN, NULL);
                if (QR_command_maybe_successful(res))
                        len = self->max_identifier_length = atoi(res->command);
                QR_Destructor(res);
@@ -2539,7 +2540,7 @@ static void CC_clear_cursors(ConnectionClass *self, BOOL 
on_abort)
                                {
                                        snprintf(cmd, sizeof(cmd), "MOVE 0 in 
\"%s\"", QR_get_cursor(res));
                                        CONNLOCK_RELEASE(self);
-                                       wres = CC_send_query(self, cmd, NULL, 
ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL);
+                                       wres = CC_send_query(self, cmd, NULL, 
TRANSACTION_ERROR | IGNORE_ABORT_ON_CONN, NULL);
                                        QR_set_no_survival_check(res);
                                        if (QR_command_maybe_successful(wres))
                                                QR_set_permanent(res);
@@ -2733,7 +2734,7 @@ CC_send_query_append(ConnectionClass *self, const char 
*query, QueryInfo *qi, UD
        BOOL    ignore_abort_on_conn = ((flag & IGNORE_ABORT_ON_CONN) != 0),
                create_keyset = ((flag & CREATE_KEYSET) != 0),
                issue_begin = ((flag & GO_INTO_TRANSACTION) != 0 && 
!CC_is_in_trans(self)),
-               rollback_on_error, query_rollback, end_with_commit;
+               rollback_error_value, query_rollback, end_with_commit;
 
        const char      *wq;
        char            swallow, *ptr;
@@ -2844,13 +2845,13 @@ CC_send_query_append(ConnectionClass *self, const char 
*query, QueryInfo *qi, UD
                return res;
        }
 
-       rollback_on_error = (flag & ROLLBACK_ON_ERROR) != 0;
+       rollback_error_value = (flag & TRANSACTION_ERROR) != 0;
        end_with_commit = (flag & END_WITH_COMMIT) != 0;
 #define        return DONT_CALL_RETURN_FROM_HERE???
        consider_rollback = (issue_begin || (CC_is_in_trans(self) && 
!CC_is_in_error_trans(self)) || strnicmp(query, "begin", 5) == 0);
-       if (rollback_on_error)
-               rollback_on_error = consider_rollback;
-       query_rollback = (rollback_on_error && !end_with_commit && 
PG_VERSION_GE(self, 8.0));
+       if (rollback_error_value)
+               rollback_error_value = consider_rollback;
+       query_rollback = (rollback_error_value && !end_with_commit && 
PG_VERSION_GE(self, 8.0));
        if (!query_rollback && consider_rollback && !end_with_commit)
        {
                if (stmt)
@@ -3341,7 +3342,7 @@ cleanup:
                CC_on_abort(self, CONN_DEAD);
                retres = NULL;
        }
-       if (rollback_on_error && CC_is_in_trans(self) && 
!discard_next_savepoint)
+       if (rollback_error_value && CC_is_in_trans(self) && 
!discard_next_savepoint)
        {
                char    cmd[64];
 
@@ -3819,10 +3820,10 @@ CC_lookup_lo(ConnectionClass *self)
 
        if (PG_VERSION_GE(self, 7.4))
                res = CC_send_query(self, "select oid, typbasetype from pg_type 
where typname = '"  PG_TYPE_LO_NAME "'",
-                       NULL, IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL);
+                       NULL, IGNORE_ABORT_ON_CONN | TRANSACTION_ERROR, NULL);
        else
                res = CC_send_query(self, "select oid, 0 from pg_type where 
typname='" PG_TYPE_LO_NAME "'",
-                       NULL, IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL);
+                       NULL, IGNORE_ABORT_ON_CONN | TRANSACTION_ERROR, NULL);
        if (QR_command_maybe_successful(res) && QR_get_num_cached_tuples(res) > 
0)
        {
                OID     basetype;
@@ -4009,7 +4010,7 @@ CC_get_current_schema(ConnectionClass *conn)
        {
                QResultClass    *res;
 
-               if (res = CC_send_query(conn, "select current_schema()", NULL, 
IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL), 
QR_command_maybe_successful(res))
+               if (res = CC_send_query(conn, "select current_schema()", NULL, 
IGNORE_ABORT_ON_CONN | TRANSACTION_ERROR, NULL), 
QR_command_maybe_successful(res))
                {
                        if (QR_get_num_total_tuples(res) == 1)
                                conn->current_schema = 
strdup(QR_get_value_backend_text(res, 0, 0));
@@ -4124,7 +4125,7 @@ int       CC_discard_marked_objects(ConnectionClass *conn)
                        snprintf(cmd, sizeof(cmd), "DEALLOCATE \"%s\"", pname + 
1);
                else
                        snprintf(cmd, sizeof(cmd), "CLOSE \"%s\"", pname + 1);
-               res = CC_send_query(conn, cmd, NULL, ROLLBACK_ON_ERROR | 
IGNORE_ABORT_ON_CONN, NULL);
+               res = CC_send_query(conn, cmd, NULL, TRANSACTION_ERROR | 
IGNORE_ABORT_ON_CONN, NULL);
                QR_Destructor(res);
                free(conn->discardp[i]);
                conn->num_discardp--;
@@ -4133,6 +4134,47 @@ int      CC_discard_marked_objects(ConnectionClass *conn)
        return 1;
 }
 
+/*
+ * Get text value of TransactionErrorBehavior.
+ */
+const char *
+CC_get_transaction_error_txt(int value)
+{
+       switch (value)
+       {
+               case TRANSACTION_ERROR_NOP:
+                       return TRANSACTION_ERROR_TXT_NOP;
+               case TRANSACTION_ERROR_TRANSACTION:
+                       return TRANSACTION_ERROR_TXT_TRANSACTION;
+               case TRANSACTION_ERROR_STATEMENT:
+                       return TRANSACTION_ERROR_TXT_STATEMENT;
+               case TRANSACTION_ERROR_DEFAULT:
+               default:
+                       return TRANSACTION_ERROR_TXT_DEFAULT;
+       }
+}
+
+/*
+ * Get integer value of TransactionErrorBehavior.
+ */
+int
+CC_get_transaction_error_int(const char *value)
+{
+       /* Leave if there is nothing */
+       if (value == NULL || value[0] == '\0')
+               return TRANSACTION_ERROR_DEFAULT;
+
+       if (strcmp(TRANSACTION_ERROR_TXT_NOP, value) == 0)
+               return TRANSACTION_ERROR_NOP;
+       else if (strcmp(TRANSACTION_ERROR_TXT_TRANSACTION, value) == 0)
+               return TRANSACTION_ERROR_TRANSACTION;
+       else if (strcmp(TRANSACTION_ERROR_TXT_STATEMENT, value) == 0)
+               return TRANSACTION_ERROR_STATEMENT;
+
+       /* Default case */
+       return TRANSACTION_ERROR_DEFAULT;
+}
+
 #ifdef USE_LIBPQ
 static int
 LIBPQ_connect(ConnectionClass *self)
diff --git a/connection.h b/connection.h
index e92ff61..7e56b79 100644
--- a/connection.h
+++ b/connection.h
@@ -287,7 +287,16 @@ typedef struct
        char            database[MEDIUM_REGISTRY_LEN];
        char            username[MEDIUM_REGISTRY_LEN];
        pgNAME          password;
+
+       /*
+        * Protocol number, this can be used as well to define the behavior
+        * of rollback in case of error.
+        */
        char            protocol[SMALL_REGISTRY_LEN];
+
+       /* Value of TransactionErrorBehavior */
+       char            transaction_error[MEDIUM_REGISTRY_LEN];
+
        char            port[SMALL_REGISTRY_LEN];
        char            sslmode[16];
        char            onlyread[SMALL_REGISTRY_LEN];
@@ -308,7 +317,16 @@ typedef struct
        signed char     bytea_as_longvarbinary;
        signed char     use_server_side_prepare;
        signed char     lower_case_identifier;
-       signed char     rollback_on_error;
+
+       /*
+        * Control behavior of transaction rollback on error.
+        *
+        * transaction_error_value is used to store the value given by either
+        * Protocol (as an extension of the protocol number) or
+        * TransactionErrorBehavior as a connection parameter.
+        */
+       signed char     transaction_error_value;
+
        signed char     force_abbrev_connstr;
        signed char     bde_environment;
        signed char     fake_mss;
@@ -377,6 +395,25 @@ typedef struct
 #define PG_VERSION_LE(conn, ver) (! PG_VERSION_GT(conn, ver))
 #define PG_VERSION_LT(conn, ver) (! PG_VERSION_GE(conn, ver))
 
+/*
+ * Variables used to control the behavior of rollback on error
+ * Default defines a behavior that defers depending on the backend
+ * version. Those numerical values can be used as an extension of
+ * Protocol, and map text values below.
+ */
+#define TRANSACTION_ERROR_DEFAULT              -1
+#define TRANSACTION_ERROR_NOP                  0
+#define TRANSACTION_ERROR_TRANSACTION  1
+#define TRANSACTION_ERROR_STATEMENT            2
+/*
+ * Text versions of precedent parameters. They can be used by
+ * TransactionErrorBehavior.
+ */
+#define TRANSACTION_ERROR_TXT_DEFAULT          "Default"
+#define TRANSACTION_ERROR_TXT_NOP                      "Nop"
+#define TRANSACTION_ERROR_TXT_TRANSACTION      "Transaction"
+#define TRANSACTION_ERROR_TXT_STATEMENT                "Statement"
+
 /*     This is used to store cached table information in the connection */
 struct col_info
 {
@@ -585,6 +622,12 @@ void               ProcessRollback(ConnectionClass *conn, 
BOOL undo, BOOL partial);
 const char     *CC_get_current_schema(ConnectionClass *conn);
 int             CC_mark_a_object_to_discard(ConnectionClass *conn, int type, 
const char *plan);
 int             CC_discard_marked_objects(ConnectionClass *conn);
+/*
+ * Procedures to get txt and numerical values of transaction rollback
+ * behavior.
+ */
+const char     *CC_get_transaction_error_txt(int value);
+int                    CC_get_transaction_error_int(const char *value);
 
 int    handle_error_message(ConnectionClass *self, char *msgbuf, size_t buflen,
                 char *sqlstate, const char *comment, QResultClass *res);
@@ -606,7 +649,7 @@ enum {
        IGNORE_ABORT_ON_CONN    = 1L /* not set the error result even when  */
        ,CREATE_KEYSET          = (1L << 1) /* create keyset for updatable 
curosrs */
        ,GO_INTO_TRANSACTION    = (1L << 2) /* issue begin in advance */
-       ,ROLLBACK_ON_ERROR      = (1L << 3) /* rollback the query when an error 
occurs */
+       ,TRANSACTION_ERROR      = (1L << 3) /* rollback the query when an error 
occurs */
        ,END_WITH_COMMIT        = (1L << 4) /* the query ends with COMMMIT 
command */
        ,IGNORE_ROUND_TRIP      = (1L << 5) /* the commincation round trip time 
is considered ignorable */
 };
diff --git a/dlg_specific.c b/dlg_specific.c
index 8e051af..06be993 100644
--- a/dlg_specific.c
+++ b/dlg_specific.c
@@ -229,16 +229,11 @@ inolog("force_abbrev=%d abbrev=%d\n", 
ci->force_abbrev_connstr, abbrev);
 inolog("hlen=%d", hlen);
        if (!abbrev)
        {
-               char    protocol_and[16];
-
-               if (ci->rollback_on_error >= 0)
-                       snprintf(protocol_and, sizeof(protocol_and), "%s-%d", 
ci->protocol, ci->rollback_on_error);
-               else
-                       strncpy_null(protocol_and, ci->protocol, 
sizeof(protocol_and));
                olen = snprintf(&connect_string[hlen], nlen, ";"
                        INI_SSLMODE "=%s;"
                        INI_READONLY "=%s;"
                        INI_PROTOCOL "=%s;"
+                       INI_TRANSACTION_ERROR "=%s;"
                        INI_FAKEOIDINDEX "=%s;"
                        INI_SHOWOIDCOLUMN "=%s;"
                        INI_ROWVERSIONING "=%s;"
@@ -276,7 +271,8 @@ inolog("hlen=%d", hlen);
 #endif /* _HANDLE_ENLIST_IN_DTC_ */
                        ,ci->sslmode
                        ,ci->onlyread
-                       ,protocol_and
+                       ,ci->protocol
+                       
,CC_get_transaction_error_txt(ci->transaction_error_value)
                        ,ci->fake_oid_index
                        ,ci->show_oid_column
                        ,ci->row_versioning
@@ -408,22 +404,25 @@ inolog("hlen=%d", hlen);
                                ci->int8_as,
                                ci->drivers.extra_systable_prefixes,
                                EFFECTIVE_BIT_COUNT, flag);
-               if (olen < nlen && (PROTOCOL_74(ci) || ci->rollback_on_error >= 
0))
+               if (olen < nlen &&
+                       (PROTOCOL_74(ci) ||
+                        ci->transaction_error_value != 
TRANSACTION_ERROR_DEFAULT))
                {
                        hlen = strlen(connect_string);
                        nlen = MAX_CONNECT_STRING - hlen;
                        /*
                         * The PROTOCOL setting must be placed after CX flag
                         * so that this option can override the CX setting.
+                        * Complete it with TransactionErrorBehavior.
                         */
-                       if (ci->rollback_on_error >= 0)
+                       if (ci->transaction_error_value != 
TRANSACTION_ERROR_DEFAULT)
                                olen = snprintf(&connect_string[hlen], nlen, ";"
-                               ABBR_PROTOCOL "=%s-%d",
-                               ci->protocol, ci->rollback_on_error);
+                                       ABBR_PROTOCOL "=%s;" 
ABBR_TRANSACTION_ERROR "=%s",
+                                       ci->protocol,
+                                       
CC_get_transaction_error_txt(ci->transaction_error_value));
                        else
                                olen = snprintf(&connect_string[hlen], nlen, ";"
-                               ABBR_PROTOCOL "=%s",
-                               ci->protocol);
+                                       ABBR_PROTOCOL "=%s", ci->protocol);
                }
        }
        if (olen < nlen)
@@ -541,22 +540,33 @@ copyAttributes(ConnInfo *ci, const char *attribute, const 
char *value)
        else if (stricmp(attribute, INI_PROTOCOL) == 0 || stricmp(attribute, 
ABBR_PROTOCOL) == 0)
        {
                char    *ptr;
-
                ptr = strchr(value, '-');
-               if (ptr)
+
+               /*
+                * Copy value controlling rollback on error if an extension of
+                * Protocol is found and if it is not set already by 
TransactionErrorBehavior.
+                */
+               if (ptr && ci->transaction_error[0] == '\0')
                {
                        if ('-' != *value)
                        {
                                *ptr = '\0';
                                strcpy(ci->protocol, value);
                        }
-                       ci->rollback_on_error = atoi(ptr + 1);
-                       mylog("rollback_on_error=%d\n", ci->rollback_on_error);
+                       ci->transaction_error_value = atoi(ptr + 1);
+                       mylog("transaction_error_value=%d\n", 
ci->transaction_error_value);
                }
                else
                        strcpy(ci->protocol, value);
        }
 
+       else if (stricmp(attribute, INI_TRANSACTION_ERROR) == 0 ||
+                        stricmp(attribute, ABBR_TRANSACTION_ERROR) == 0)
+       {
+               strcpy(ci->transaction_error, value);
+               ci->transaction_error_value = 
CC_get_transaction_error_int(value);
+       }
+
        else if (stricmp(attribute, INI_SHOWOIDCOLUMN) == 0 || 
stricmp(attribute, ABBR_SHOWOIDCOLUMN) == 0)
                strcpy(ci->show_oid_column, value);
 
@@ -663,7 +673,7 @@ copyAttributes(ConnInfo *ci, const char *attribute, const 
char *value)
        else
                found = FALSE;
 
-       mylog("%s: 
DSN='%s',server='%s',dbase='%s',user='%s',passwd='%s',port='%s',onlyread='%s',protocol='%s',conn_settings='%s',disallow_premature=%d)\n",
 func, ci->dsn, ci->server, ci->database, ci->username, 
NAME_IS_VALID(ci->password) ? "xxxxx" : "", ci->port, ci->onlyread, 
ci->protocol, ci->conn_settings, ci->disallow_premature);
+       mylog("%s: 
DSN='%s',server='%s',dbase='%s',user='%s',passwd='%s',port='%s',onlyread='%s',protocol='%s',rollback_error='%s',conn_settings='%s',disallow_premature=%d)\n",
 func, ci->dsn, ci->server, ci->database, ci->username, 
NAME_IS_VALID(ci->password) ? "xxxxx" : "", ci->port, ci->onlyread, 
ci->protocol, CC_get_transaction_error_txt(ci->transaction_error_value), 
ci->conn_settings, ci->disallow_premature);
 
        return found;
 }
@@ -893,21 +903,45 @@ getDSNinfo(ConnInfo *ci, char overwrite)
        if (ci->show_system_tables[0] == '\0' || overwrite)
                SQLGetPrivateProfileString(DSN, INI_SHOWSYSTEMTABLES, "", 
ci->show_system_tables, sizeof(ci->show_system_tables), ODBC_INI);
 
+       /*
+        * Set up value for Protocol. Error on rollback
+        */
        if (ci->protocol[0] == '\0' || overwrite)
        {
                char    *ptr;
                SQLGetPrivateProfileString(DSN, INI_PROTOCOL, "", ci->protocol, 
sizeof(ci->protocol), ODBC_INI);
-               if (ptr = strchr(ci->protocol, '-'), NULL != ptr)
+
+               /*
+                * Check if behavior of rollback on error is set as an extension
+                * of Protocol. Note that if this parameter has already been set
+                * by TransactionErrorBehavior, we simply ignore the value set 
here.
+                */
+               if (ptr = strchr(ci->protocol, '-'), NULL != ptr &&
+                       ci->transaction_error[0] == '\0')
                {
                        *ptr = '\0';
-                       if (overwrite || ci->rollback_on_error < 0)
+                       if (overwrite ||
+                               ci->transaction_error_value == 
TRANSACTION_ERROR_DEFAULT)
                        {
-                               ci->rollback_on_error = atoi(ptr + 1);
-                               mylog("rollback_on_error=%d\n", 
ci->rollback_on_error);
+                               ci->transaction_error_value = atoi(ptr + 1);
+                               mylog("transaction_error_value=%d\n", 
ci->transaction_error_value);
                        }
                }
        }
 
+       /*
+        * Set up value for TransactionErrorBehavior. This gets priority over 
Protocol.
+        */
+       if (ci->transaction_error[0] == '\0' || overwrite)
+       {
+               if (ci->transaction_error_value == TRANSACTION_ERROR_DEFAULT
+                       || overwrite)
+               {
+                       ci->transaction_error_value = 
CC_get_transaction_error_int(ci->transaction_error);
+                       mylog("transaction_error_value=%d\n", 
ci->transaction_error_value);
+               }
+       }
+
        if (NAME_IS_NULL(ci->conn_settings) || overwrite)
        {
                SQLGetPrivateProfileString(DSN, INI_CONNSETTINGS, "", 
encoded_item, sizeof(encoded_item), ODBC_INI);
@@ -1205,12 +1239,14 @@ writeDSNinfo(const ConnInfo *ci)
                                                                 
ci->show_system_tables,
                                                                 ODBC_INI);
 
-       if (ci->rollback_on_error >= 0)
-               sprintf(temp, "%s-%d", ci->protocol, ci->rollback_on_error);
-       else
-               strncpy_null(temp, ci->protocol, sizeof(temp));
        SQLWritePrivateProfileString(DSN,
                                                                 INI_PROTOCOL,
+                                                                ci->protocol,
+                                                                ODBC_INI);
+
+       sprintf(temp, "%s", 
CC_get_transaction_error_txt(ci->transaction_error_value));
+       SQLWritePrivateProfileString(DSN,
+                                                                
INI_TRANSACTION_ERROR,
                                                                 temp,
                                                                 ODBC_INI);
 
diff --git a/dlg_specific.h b/dlg_specific.h
index c73bb64..d994423 100644
--- a/dlg_specific.h
+++ b/dlg_specific.h
@@ -75,6 +75,8 @@ extern "C" {
 #define ABBR_COMMLOG                   "B3"
 #define INI_PROTOCOL                   "Protocol"      /* What protocol (6.2) 
*/
 #define ABBR_PROTOCOL                  "A1"
+#define INI_TRANSACTION_ERROR  "TransactionErrorBehavior" /* Rollback on error 
*/
+#define ABBR_TRANSACTION_ERROR "AA"
 #define INI_OPTIMIZER                  "Optimizer"     /* Use backend genetic
                                                         * optimizer */
 #define ABBR_OPTIMIZER                 "B4"
diff --git a/dlg_wingui.c b/dlg_wingui.c
index 072f542..394f7df 100644
--- a/dlg_wingui.c
+++ b/dlg_wingui.c
@@ -550,7 +550,7 @@ ds_options2Proc(HWND hdlg,
                                CheckDlgButton(hdlg, DS_PG74, 1);
 
                        /* How to issue Rollback */
-                       switch (ci->rollback_on_error)
+                       switch (ci->transaction_error_value)
                        {
                                case 0:
                                        CheckDlgButton(hdlg, DS_NO_ROLLBACK, 1);
@@ -639,14 +639,14 @@ ds_options2Proc(HWND hdlg,
 
                                        /* Issue rollback command on error */
                                        if (IsDlgButtonChecked(hdlg, 
DS_NO_ROLLBACK))
-                                               ci->rollback_on_error = 0;
+                                               ci->transaction_error_value = 
TRANSACTION_ERROR_NONE;
                                        else if (IsDlgButtonChecked(hdlg, 
DS_TRANSACTION_ROLLBACK))
-                                               ci->rollback_on_error = 1;
+                                               ci->transaction_error_value = 
TRANSACTION_ERROR_TRANSACTION;
                                        else if (IsDlgButtonChecked(hdlg, 
DS_STATEMENT_ROLLBACK))
-                                               ci->rollback_on_error = 2;
+                                               ci->transaction_error_value = 
TRANSACTION_ERROR_STATEMENT;
                                        else
                                                /* legacy */
-                                               ci->rollback_on_error = 1;
+                                               ci->transaction_error_value = 
TRANSACTION_ERROR_DEFAULT;
 
                                        /* Int8 As */
                                        if (IsDlgButtonChecked(hdlg, 
DS_INT8_AS_DEFAULT))
diff --git a/docs/config-opt.html b/docs/config-opt.html
index 6673bc2..8e18086 100644
--- a/docs/config-opt.html
+++ b/docs/config-opt.html
@@ -146,6 +146,17 @@
        </TR>
        <TR>
                <TD WIDTH=38%>
+                       Rollback on error
+               </TD>
+               <TD WIDTH=31%>
+                       TransactionErrorBehavior
+               </TD>
+               <TD WIDTH=31%>
+                       AA
+               </TD>
+       </TR>
+       <TR>
+               <TD WIDTH=38%>
                        Backend enetic optimizer
                </TD>
                <TD WIDTH=31%>
diff --git a/docs/config.html b/docs/config.html
index cb1c326..8b29ba6 100644
--- a/docs/config.html
+++ b/docs/config.html
@@ -238,23 +238,35 @@ with 6.4 and higher backends.<br />&nbsp;</li>
 with 7.4 and higher backends.<br />&nbsp;</li>
 </ul></li>
 
-<li><b>Level of rollback on errors:</b> Specifies what to rollback should an
-error occur.<br />&nbsp;
+<li><b>TransactionErrorBehavior</b>: Level of rollback on errors, specifies 
what to
+rollback should an error occur.<br />&nbsp;
 
 <ul>
-<li><i>Nop(0):</i> Don't rollback anything and let the application handle the
-error.<br />&nbsp;</li>
-
-<li><i>Transaction(1):</i> Rollback the entire transaction.<br />&nbsp;</li>
-
-<li><i>Statement(2):</i> Rollback the statement.<br />&nbsp;</li>
-<br>
-<b>Notes in a setup: This specification is set up with a PROTOCOL option 
parameter.</b><br><br>
-PROTOCOL=[6.2|6.3|6.4|7.4][-(0|1|2)]<br>
-default value is a sentence unit (it is a transaction unit before 8.0).<br>
-<br>
-
-</ul></li>
+<li><i>Default:</i> Default value. Transaction rollback behavior is chosen
+depending on the database backend version: 'Transaction' for Postgres servers
+older than 8.0 and 'Statement' for servers newer than 8.1.<br /></li>
+
+<li><i>Nop:</i> Don't rollback anything and let the application handle the
+error.<br /></li>
+
+<li><i>Transaction:</i> Rollback the entire transaction.<br /></li>
+
+<li><i>Statement:</i> Rollback the statement.<br /></li>
+<b>Notes:</b>
+  <ul>
+    <li>
+      This parameter can be set as an extension of PROTOCOL option
+      parameter.<br />
+      PROTOCOL=[6.2|6.3|6.4|7.4][-(0|1|2)]<br />
+      Default value is a sentence unit (it is a transaction unit
+      before 8.0).<br />
+    </li>
+    <li>
+      This parameter can be set with TRANSACTIONROLLBACKBEHAVIOR as well.
+      This takes precedence on PROTOCOL.<bt />
+    </li>
+  </ul>
+</ul>.<br />&nbsp;</li>
 
 
 <li><b>OID Options:</b><br />&nbsp;
diff --git a/execute.c b/execute.c
index 1cc4f04..a732eb9 100644
--- a/execute.c
+++ b/execute.c
@@ -651,28 +651,31 @@ inolog("%s:%p->internal=%d\n", func, stmt, 
stmt->internal);
        conn = SC_get_conn(stmt);
        if (conn)
                ci = &conn->connInfo;
-       ret = 0;
-       if (!ci || ci->rollback_on_error < 0) /* default */
+       ret = TRANSACTION_ERROR_NOP;
+       if (!ci || ci->transaction_error_value == TRANSACTION_ERROR_DEFAULT)
        {
                if (conn && PG_VERSION_GE(conn, 8.0))
-                       ret = 2; /* statement rollback */
+                       ret = TRANSACTION_ERROR_STATEMENT;
                else
-                       ret = 1; /* transaction rollback */
+                       ret = TRANSACTION_ERROR_TRANSACTION;
        }
        else
        {
-               ret = ci->rollback_on_error;
-               if (2 == ret && PG_VERSION_LT(conn, 8.0))
-                       ret = 1;
+               ret = ci->transaction_error_value;
+               if (ret == TRANSACTION_ERROR_STATEMENT &&
+                       PG_VERSION_LT(conn, 8.0))
+                       ret = TRANSACTION_ERROR_TRANSACTION;
        }
        switch (ret)
        {
-               case 1:
+               case TRANSACTION_ERROR_TRANSACTION:
                        SC_start_tc_stmt(stmt);
                        break;
-               case 2:
+               case TRANSACTION_ERROR_STATEMENT:
                        SC_start_rb_stmt(stmt);
                        break;
+               default:
+                       break;
        }
        return  ret;
 }
diff --git a/info.c b/info.c
index 81bfe6d..a6c7274 100644
--- a/info.c
+++ b/info.c
@@ -4012,7 +4012,7 @@ getClientColumnName(ConnectionClass *conn, UInt4 relid, 
char *serverColumnName,
        BOOL            continueExec = TRUE,
                                bError = FALSE;
        QResultClass *res = NULL;
-       UWORD   flag = IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR;
+       UWORD   flag = IGNORE_ABORT_ON_CONN | TRANSACTION_ERROR;
 
        *nameAlloced = FALSE;
        if (!conn->original_client_encoding || !isMultibyte(serverColumnName))
diff --git a/multibyte.c b/multibyte.c
index 2f7b594..512b9cc 100644
--- a/multibyte.c
+++ b/multibyte.c
@@ -444,7 +444,7 @@ CC_lookup_cs_new(ConnectionClass *self)
        char            *encstr = NULL;
        QResultClass    *res;
 
-       res = CC_send_query(self, "select pg_client_encoding()", NULL, 
IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL);
+       res = CC_send_query(self, "select pg_client_encoding()", NULL, 
IGNORE_ABORT_ON_CONN | TRANSACTION_ERROR, NULL);
        if (QR_command_maybe_successful(res))
        {
                const char *enc = QR_get_value_backend_text(res, 0, 0);
@@ -606,7 +606,7 @@ CC_lookup_characterset(ConnectionClass *self)
                        BOOL            cmd_success;
 
                        sprintf(query, "set client_encoding to '%s'", wenc);
-                       res = CC_send_query(self, query, NULL, 
IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL);
+                       res = CC_send_query(self, query, NULL, 
IGNORE_ABORT_ON_CONN | TRANSACTION_ERROR, NULL);
                        cmd_success = QR_command_maybe_successful(res);
                        QR_Destructor(res);
                        CC_set_errornumber(self, errnum);
diff --git a/parse.c b/parse.c
index 5aa7723..a45ddf4 100644
--- a/parse.c
+++ b/parse.c
@@ -382,7 +382,7 @@ static BOOL CheckHasOids(StatementClass * stmt)
        snprintf(query, sizeof(query),
                         "select relhasoids, c.oid from pg_class c, 
pg_namespace n where relname = '%s' and nspname = '%s' and c.relnamespace = 
n.oid",
                         SAFE_NAME(ti->table_name), SAFE_NAME(ti->schema_name));
-       res = CC_send_query(conn, query, NULL, ROLLBACK_ON_ERROR | 
IGNORE_ABORT_ON_CONN, NULL);
+       res = CC_send_query(conn, query, NULL, TRANSACTION_ERROR | 
IGNORE_ABORT_ON_CONN, NULL);
        if (QR_command_maybe_successful(res))
        {
                stmt->num_key_fields = PG_NUM_NORMAL_KEYS;
@@ -410,7 +410,7 @@ static BOOL CheckHasOids(StatementClass * stmt)
                if (!hasoids)
                {
                        sprintf(query, "select a.attname, a.atttypid from 
pg_index i, pg_attribute a where indrelid=%u and indnatts=1 and indisunique and 
indexprs is null and indpred is null and i.indrelid = a.attrelid and 
a.attnum=i.indkey[0] and attnotnull and atttypid in (%d, %d)", ti->table_oid, 
PG_TYPE_INT4, PG_TYPE_OID);
-                       res = CC_send_query(conn, query, NULL, 
ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL);
+                       res = CC_send_query(conn, query, NULL, 
TRANSACTION_ERROR | IGNORE_ABORT_ON_CONN, NULL);
                        if (QR_command_maybe_successful(res) && 
QR_get_num_total_tuples(res) > 0)
                        {
                                foundKey = TRUE;
@@ -744,7 +744,7 @@ COL_INFO **coli)
                                                 "select nspname from 
pg_namespace n, pg_class c"
                                                 " where c.relnamespace=n.oid 
and c.oid='\"%s\"'::regclass",
                                                 SAFE_NAME(table_name));
-                               res = CC_send_query(conn, token, NULL, 
ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN, NULL);
+                               res = CC_send_query(conn, token, NULL, 
TRANSACTION_ERROR | IGNORE_ABORT_ON_CONN, NULL);
                                if (QR_command_maybe_successful(res))
                                {
                                        if (QR_get_num_total_tuples(res) == 1)
diff --git a/qresult.c b/qresult.c
index 361be56..11558bb 100644
--- a/qresult.c
+++ b/qresult.c
@@ -487,7 +487,7 @@ QR_free_memory(QResultClass *self)
                                char            cmd[64];
 
                                snprintf(cmd, sizeof(cmd), "DEALLOCATE \"%s\"", 
plannm);
-                               res = CC_send_query(conn, cmd, NULL, 
IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL);
+                               res = CC_send_query(conn, cmd, NULL, 
IGNORE_ABORT_ON_CONN | TRANSACTION_ERROR, NULL);
                                QR_Destructor(res);
                        }
                }
@@ -710,7 +710,7 @@ QR_close(QResultClass *self)
                        char            buf[64];
 
                        if (QR_needs_survival_check(self))
-                               flag = ROLLBACK_ON_ERROR | IGNORE_ABORT_ON_CONN;
+                               flag = TRANSACTION_ERROR | IGNORE_ABORT_ON_CONN;
 
                        snprintf(buf, sizeof(buf), "close \"%s\"", 
QR_get_cursor(self));
                        /* End the transaction if there are no cursors left on 
this conn */
@@ -719,7 +719,7 @@ QR_close(QResultClass *self)
                            CC_cursor_count(conn) <= 1)
                        {
                                mylog("QResult: END transaction on conn=%p\n", 
conn);
-                               if ((ROLLBACK_ON_ERROR & flag) == 0)
+                               if ((TRANSACTION_ERROR & flag) == 0)
                                {
                                        strlcat(buf, ";commit", sizeof(buf));
                                        flag |= END_WITH_COMMIT;
diff --git a/statement.c b/statement.c
index 19510bf..86f65ac 100644
--- a/statement.c
+++ b/statement.c
@@ -698,7 +698,7 @@ SC_set_prepared(StatementClass *stmt, int prepared)
                                        char dealloc_stmt[128];
 
                                        sprintf(dealloc_stmt, "DEALLOCATE 
\"%s\"", stmt->plan_name);
-                                       res = CC_send_query(conn, dealloc_stmt, 
NULL, IGNORE_ABORT_ON_CONN | ROLLBACK_ON_ERROR, NULL);
+                                       res = CC_send_query(conn, dealloc_stmt, 
NULL, IGNORE_ABORT_ON_CONN | TRANSACTION_ERROR, NULL);
                                        QR_Destructor(res);
                                }
                        }
diff --git a/test/expected/error-rollback.out b/test/expected/error-rollback.out
index 436a328..51276c8 100644
--- a/test/expected/error-rollback.out
+++ b/test/expected/error-rollback.out
@@ -1,5 +1,5 @@
 \! ./src/error-rollback-test
-Test for rollback protocol 0
+Test for Protocol=7.4-0
 connected
 Executing query that will succeed
 Executing query that will fail
@@ -10,7 +10,7 @@ Executing query that will succeed
 Result set:
 1
 disconnecting
-Test for rollback protocol 1
+Test for Protocol=7.4-1
 connected
 Executing query that will succeed
 Executing query that will fail
@@ -20,7 +20,71 @@ Executing query that will succeed
 Result set:
 1
 disconnecting
-Test for rollback protocol 2
+Test for Protocol=7.4-2
+connected
+Executing query that will succeed
+Executing query that will fail
+Failed to execute statement
+22P02=ERROR: invalid input syntax for integer: "foo"
+Executing query that will succeed
+Result set:
+1
+1
+disconnecting
+Test for TransactionErrorBehavior=Nop
+connected
+Executing query that will succeed
+Executing query that will fail
+Failed to execute statement
+22P02=ERROR: invalid input syntax for integer: "foo"
+Rolling back with SQLEndTran
+Executing query that will succeed
+Result set:
+1
+disconnecting
+Test for TransactionErrorBehavior=Transaction
+connected
+Executing query that will succeed
+Executing query that will fail
+Failed to execute statement
+22P02=ERROR: invalid input syntax for integer: "foo"
+Executing query that will succeed
+Result set:
+1
+disconnecting
+Test for TransactionErrorBehavior=Statement
+connected
+Executing query that will succeed
+Executing query that will fail
+Failed to execute statement
+22P02=ERROR: invalid input syntax for integer: "foo"
+Executing query that will succeed
+Result set:
+1
+1
+disconnecting
+Test for TransactionErrorBehavior=Nop;Protocol=7.4-2
+connected
+Executing query that will succeed
+Executing query that will fail
+Failed to execute statement
+22P02=ERROR: invalid input syntax for integer: "foo"
+Rolling back with SQLEndTran
+Executing query that will succeed
+Result set:
+1
+disconnecting
+Test for TransactionErrorBehavior=Transaction;Protocol=7.4-0
+connected
+Executing query that will succeed
+Executing query that will fail
+Failed to execute statement
+22P02=ERROR: invalid input syntax for integer: "foo"
+Executing query that will succeed
+Result set:
+1
+disconnecting
+Test for TransactionErrorBehavior=Statement;Protocol=7.4-1
 connected
 Executing query that will succeed
 Executing query that will fail
diff --git a/test/src/error-rollback-test.c b/test/src/error-rollback-test.c
index 01c0f0c..b21affd 100644
--- a/test/src/error-rollback-test.c
+++ b/test/src/error-rollback-test.c
@@ -142,18 +142,19 @@ error_rollback_print(void)
        print_result(hstmt);
 }
 
-int
-main(int argc, char **argv)
+/*
+ * Test for rollback protocol 0 (Nop).
+ *
+ * Additional options can be specified to play with combinations of
+ * Protocol and TransactionErrorBehavior. With protocol 0, it is the
+ * responsability of application to issue rollbacks.
+ */
+void
+error_rollback_test_0(char *options)
 {
        SQLRETURN rc;
 
-       /*
-        * Test for protocol at 0.
-        * Do nothing when error occurs and let application do necessary
-        * ROLLBACK on error.
-        */
-       printf("Test for rollback protocol 0\n");
-       error_rollback_init("Protocol=7.4-0");
+       error_rollback_init(options);
 
        /* Insert a row correctly */
        error_rollback_exec_success();
@@ -181,17 +182,25 @@ main(int argc, char **argv)
 
        /* Clean up */
        error_rollback_clean();
+}
 
-       /*
-        * Test for rollback protocol 1
-        * In case of an error rollback the entire transaction.
-        */
-       printf("Test for rollback protocol 1\n");
-       error_rollback_init("Protocol=7.4-1");
+/*
+ * Test for rollback protocols 1 (Statement) or 2 (Transaction)
+ *
+ * Options can be specified to manipulate the protocol used with Protocol
+ * and TransactionErrorBehavior. When priting results, there should be one
+ * row for protocol 1 as rollback is done for an entire transaction is case
+ * of a failure. There will be two rows for protocol 2 as tollback is issued
+ * for each statement.
+ */
+void
+error_rollback_test_12(char *options)
+{
+       error_rollback_init(options);
 
        /*
-        * Insert a row, trigger an error, and re-insert a row. Only one
-        * row should be visible here.
+        * Insert a row, trigger an error, and re-insert a row. Depending
+        * on the protocol, one or two rows should be visible.
         */
        error_rollback_exec_success();
        error_rollback_exec_failure();
@@ -200,23 +209,40 @@ main(int argc, char **argv)
 
        /* Clean up */
        error_rollback_clean();
+}
 
+int
+main(int argc, char **argv)
+{
        /*
-        * Test for rollback protocol 2
-        * In the case of an error rollback only the latest statement.
+        * Test for Protocol only
         */
-       printf("Test for rollback protocol 2\n");
-       error_rollback_init("Protocol=7.4-2");
+       printf("Test for Protocol=7.4-0\n");
+       error_rollback_test_0("Protocol=7.4-0");
+       printf("Test for Protocol=7.4-1\n");
+       error_rollback_test_12("Protocol=7.4-1");
+       printf("Test for Protocol=7.4-2\n");
+       error_rollback_test_12("Protocol=7.4-2");
 
        /*
-        * Similarly to previous case, do insert, error and insert. This
-        * time two rows should be visible.
+        * Now do the same tests as previously, but this time for
+        * TransactionErrorBehavior.
         */
-       error_rollback_exec_success();
-       error_rollback_exec_failure();
-       error_rollback_exec_success();
-       error_rollback_print();
+       printf("Test for TransactionErrorBehavior=Nop\n");
+       error_rollback_test_0("TransactionErrorBehavior=0");
+       printf("Test for TransactionErrorBehavior=Transaction\n");
+       error_rollback_test_12("TransactionErrorBehavior=Transaction");
+       printf("Test for TransactionErrorBehavior=Statement\n");
+       error_rollback_test_12("TransactionErrorBehavior=Statement");
 
-       /* Clean up */
-       error_rollback_clean();
+       /*
+        * Combinations of TransactionErrorBehavior and Protocol, the former
+        * should have the priority.
+        */
+       printf("Test for TransactionErrorBehavior=Nop;Protocol=7.4-2\n");
+       error_rollback_test_0("TransactionErrorBehavior=Nop;Protocol=7.4-2");
+       printf("Test for 
TransactionErrorBehavior=Transaction;Protocol=7.4-0\n");
+       
error_rollback_test_12("TransactionErrorBehavior=Transaction;Protocol=7.4-0\n");
 /* 1 row */
+       printf("Test for TransactionErrorBehavior=Statement;Protocol=7.4-1\n");
+       
error_rollback_test_12("TransactionErrorBehavior=Statement;Protocol=7.4-1\n"); 
/* 2 rows */
 }
-- 
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