diff --git a/connection.c b/connection.c index 8601cb7..9407619 100644 --- a/connection.c +++ b/connection.c @@ -288,7 +288,8 @@ 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->rollback_error_value = -1; + conninfo->is_rollback_error = -1; conninfo->force_abbrev_connstr = -1; conninfo->bde_environment = -1; conninfo->fake_mss = -1; @@ -341,7 +342,8 @@ 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(rollback_error_value); + CORR_VALCPY(is_rollback_error); CORR_VALCPY(force_abbrev_connstr); CORR_VALCPY(bde_environment); CORR_VALCPY(fake_mss); @@ -2733,7 +2735,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 +2846,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 & ROLLBACK_ON_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 +3343,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]; diff --git a/connection.h b/connection.h index e92ff61..453ed5b 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 RollbackError */ + char rollback_error[SMALL_REGISTRY_LEN]; + char port[SMALL_REGISTRY_LEN]; char sslmode[16]; char onlyread[SMALL_REGISTRY_LEN]; @@ -308,7 +317,19 @@ 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 rollback on error. + * + * rollback_error_value is used to store the value given by either + * Protovol (as an extension of the protocol number) or RollbackError. + * is_rollback_error is set to true if the value set is obtained with + * RollbackError and not as an extension of Protocol. Priority is given + * to RollbackError if both Protocol and RollbackError are set. + */ + signed char rollback_error_value; + signed char is_rollback_error; + signed char force_abbrev_connstr; signed char bde_environment; signed char fake_mss; diff --git a/dlg_specific.c b/dlg_specific.c index 8e051af..ff6066b 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_ROLLBACK_ON_ERROR "=%d;" 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 + ,ci->rollback_error_value ,ci->fake_oid_index ,ci->show_oid_column ,ci->row_versioning @@ -408,22 +404,22 @@ 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->rollback_error_value >= 0)) { 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 RollbackError. */ - if (ci->rollback_on_error >= 0) + if (ci->rollback_error_value >= 0) olen = snprintf(&connect_string[hlen], nlen, ";" - ABBR_PROTOCOL "=%s-%d", - ci->protocol, ci->rollback_on_error); + ABBR_PROTOCOL "=%s;" ABBR_ROLLBACK_ON_ERROR "=%d", + ci->protocol, ci->rollback_error_value); else olen = snprintf(&connect_string[hlen], nlen, ";" - ABBR_PROTOCOL "=%s", - ci->protocol); + ABBR_PROTOCOL "=%s", ci->protocol); } } if (olen < nlen) @@ -541,22 +537,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 RollbackError. + */ + if (ptr && ci->is_rollback_error < 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->rollback_error_value = atoi(ptr + 1); + mylog("rollback_error_value=%d\n", ci->rollback_error_value); } else strcpy(ci->protocol, value); } + else if (stricmp(attribute, INI_ROLLBACK_ON_ERROR) == 0 || + stricmp(attribute, ABBR_ROLLBACK_ON_ERROR) == 0) + { + ci->rollback_error_value = atoi(value); + ci->is_rollback_error = 0; + } + else if (stricmp(attribute, INI_SHOWOIDCOLUMN) == 0 || stricmp(attribute, ABBR_SHOWOIDCOLUMN) == 0) strcpy(ci->show_oid_column, value); @@ -663,7 +670,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, ci->rollback_error, ci->conn_settings, ci->disallow_premature); return found; } @@ -893,21 +900,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 RollbackError, we simply ignore the value set here. + */ + if (ptr = strchr(ci->protocol, '-'), NULL != ptr && + ci->is_rollback_error < 0) { *ptr = '\0'; - if (overwrite || ci->rollback_on_error < 0) + if (overwrite || ci->rollback_error_value < 0) { - ci->rollback_on_error = atoi(ptr + 1); - mylog("rollback_on_error=%d\n", ci->rollback_on_error); + ci->rollback_error_value = atoi(ptr + 1); + mylog("rollback_error_value=%d\n", ci->rollback_error_value); } } } + /* + * Set up value for RollbackError. This gets priority over Protocol. + */ + if (ci->rollback_error[0] == '\0' || overwrite) + { + if ((ci->rollback_error_value < 0 && ci->is_rollback_error < 0) + || overwrite) + { + ci->rollback_error_value = atoi(ci->rollback_error); + ci->is_rollback_error = 0; + mylog("rollback_error_value=%d\n", ci->rollback_error_value); + } + } + if (NAME_IS_NULL(ci->conn_settings) || overwrite) { SQLGetPrivateProfileString(DSN, INI_CONNSETTINGS, "", encoded_item, sizeof(encoded_item), ODBC_INI); @@ -1205,12 +1236,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, "%d", ci->rollback_error_value); + SQLWritePrivateProfileString(DSN, + INI_ROLLBACK_ON_ERROR, temp, ODBC_INI); diff --git a/dlg_specific.h b/dlg_specific.h index c73bb64..d2d1575 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_ROLLBACK_ON_ERROR "RollbackError" /* Rollback on error */ +#define ABBR_ROLLBACK_ON_ERROR "AA" #define INI_OPTIMIZER "Optimizer" /* Use backend genetic * optimizer */ #define ABBR_OPTIMIZER "B4" diff --git a/docs/config-opt.html b/docs/config-opt.html index 6673bc2..bba3266 100644 --- a/docs/config-opt.html +++ b/docs/config-opt.html @@ -146,6 +146,17 @@ + Rollback on error + + + RollbackError + + + AA + + + + Backend enetic optimizer diff --git a/docs/config.html b/docs/config.html index cb1c326..b0b727a 100644 --- a/docs/config.html +++ b/docs/config.html @@ -238,23 +238,31 @@ with 6.4 and higher backends.
  with 7.4 and higher backends.
  -
  • Level of rollback on errors: Specifies what to rollback should an -error occur.
      +
  • RollbackError: Level of rollback on errors, specifies what to +rollback should an error occur.
     
  • +error.
    + +
  • Transaction(1): Rollback the entire transaction.
  • + +
  • Statement(2): Rollback the statement.
  • +Notes: + +.
     
  • OID Options:
      diff --git a/execute.c b/execute.c index 1cc4f04..d502df2 100644 --- a/execute.c +++ b/execute.c @@ -652,7 +652,7 @@ inolog("%s:%p->internal=%d\n", func, stmt, stmt->internal); if (conn) ci = &conn->connInfo; ret = 0; - if (!ci || ci->rollback_on_error < 0) /* default */ + if (!ci || ci->rollback_error_value < 0) /* default */ { if (conn && PG_VERSION_GE(conn, 8.0)) ret = 2; /* statement rollback */ @@ -661,7 +661,7 @@ inolog("%s:%p->internal=%d\n", func, stmt, stmt->internal); } else { - ret = ci->rollback_on_error; + ret = ci->rollback_error_value; if (2 == ret && PG_VERSION_LT(conn, 8.0)) ret = 1; } diff --git a/test/expected/error-rollback.out b/test/expected/error-rollback.out index 436a328..da9cdf3 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 RollbackError=0 +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 RollbackError=1 +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 RollbackError=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 RollbackError=0;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 RollbackError=1;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 RollbackError=2;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..bbff007 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. + * + * Additional options can be specified to play with combinations of + * Protocol and RollbackError. 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 or 2 + * + * Options can be specified to manipulate the protocol used with Protocol + * and RollbackError. When priting results, there should be one row for + * protocol 1 as rollback is done for an entire transaction is case of + * 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 + * RollbackError. */ - error_rollback_exec_success(); - error_rollback_exec_failure(); - error_rollback_exec_success(); - error_rollback_print(); + printf("Test for RollbackError=0\n"); + error_rollback_test_0("RollbackError=0"); + printf("Test for RollbackError=1\n"); + error_rollback_test_12("RollbackError=1"); + printf("Test for RollbackError=2\n"); + error_rollback_test_12("RollbackError=2"); - /* Clean up */ - error_rollback_clean(); + /* + * Combinations of RollbackError and Protocol, the former + * should have the priority. + */ + printf("Test for RollbackError=0;Protocol=7.4-2\n"); + error_rollback_test_0("RollbackError=0;Protocol=7.4-2"); + printf("Test for RollbackError=1;Protocol=7.4-0\n"); + error_rollback_test_12("RollbackError=1;Protocol=7.4-0\n"); /* 1 row */ + printf("Test for RollbackError=2;Protocol=7.4-1\n"); + error_rollback_test_12("RollbackError=2;Protocol=7.4-1\n"); /* 2 rows */ }