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 /> </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 */ }
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers