20140331_error_rollback_v2.patch

text/plain

Filename: 20140331_error_rollback_v2.patch
Type: text/plain
Part: 0
Message: Re: Re: [HACKERS] New parameter RollbackError to control rollback behavior on error

Patch

Same data as JSON: GET /api/v1/attachments/:id/patch the parsed metadata as JSON — format, series position, per-file stats; never the diff bytes. API reference →
Format: unified
Series: patch v2
File+
connection.c 56 14
connection.h 45 2
dlg_specific.c 62 26
dlg_specific.h 2 0
dlg_wingui.c 5 5
docs/config.html 27 15
docs/config-opt.html 11 0
execute.c 12 9
info.c 1 1
multibyte.c 2 2
parse.c 3 3
qresult.c 3 3
src/error-rollback-test.c 55 29
statement.c 1 1
test/expected/error-rollback.out 67 3
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 */
 }