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 @@ + Rollback on error + + + TransactionErrorBehavior + + + AA + + + + Backend enetic optimizer 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.
  with 7.4 and higher backends.
  -
  • Level of rollback on errors: Specifies what to rollback should an -error occur.
      +
  • TransactionErrorBehavior: Level of rollback on errors, specifies what to +rollback should an error occur.
     
  • +
  • Default: 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.
  • + +
  • Nop: Don't rollback anything and let the application handle the +error.
  • + +
  • Transaction: Rollback the entire transaction.
  • + +
  • Statement: Rollback the statement.
  • +Notes: + +.
     
  • OID Options:
      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 */ }