20140331_error_rollback_v2.patch
text/plain
Filename: 20140331_error_rollback_v2.patch
Type: text/plain
Part: 0
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 /> </li>
with 7.4 and higher backends.<br /> </li>
</ul></li>
-<li><b>Level of rollback on errors:</b> Specifies what to rollback should an
-error occur.<br />
+<li><b>TransactionErrorBehavior</b>: Level of rollback on errors, specifies what to
+rollback should an error occur.<br />
<ul>
-<li><i>Nop(0):</i> Don't rollback anything and let the application handle the
-error.<br /> </li>
-
-<li><i>Transaction(1):</i> Rollback the entire transaction.<br /> </li>
-
-<li><i>Statement(2):</i> Rollback the statement.<br /> </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 /> </li>
<li><b>OID Options:</b><br />
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 */
}