Quoting Rick Keiner <[EMAIL PROTECTED]>:
What's the status of the API change? This would bump the rev to 1.3,
correct? Any idea what the time frame is on this?
OK, here is the summary:
Database | Explicit ROLLBACK | Ignore TXN errors
------------------------------------------------
pgsql | Yes | Yes
------------------------------------------------
mysql | Yes (InnoDB) | Yes
------------------------------------------------
sqlite3 | Yes | Yes
------------------------------------------------
sqlite2 | Not tested | Not tested
------------------------------------------------
oracle | Not tested | Not tested
------------------------------------------------
I had to make some changes to the PostgreSQL patch - there were subtle
bugs related to result sets. So, I'm attaching a new patch for
reference. A patch against the latest version of MySQL driver is also
attached.
I'm not forseeing problems with SQLite2 - the code is much the same as
for SQLite3, so it should work. I just don't have the environment
handy to test.
As for Oracle, this needs to be tested by folks that understand both
the driver and the backend properly. Skeleton code is attached, feel
free to have a go at it.
--
Bojan
Index: include/apr_dbd.h
===================================================================
--- include/apr_dbd.h (revision 411975)
+++ include/apr_dbd.h (working copy)
@@ -174,6 +174,30 @@
apr_pool_t *pool,
apr_dbd_transaction_t *trans);
+#define APR_DBD_TRANSACTION_COMMIT 0x00 /**< commit the transaction */
+#define APR_DBD_TRANSACTION_ROLLBACK 0x01 /**< rollback the transaction */
+#define APR_DBD_TRANSACTION_IGNORE_ERRORS 0x02 /**< ignore transaction errors */
+
+/** apr_dbd_transaction_mode_get: get the mode of transaction
+ *
+ * @param driver - the driver
+ * @param trans - the transaction
+ * @return mode of transaction
+ */
+APU_DECLARE(int) apr_dbd_transaction_mode_get(const apr_dbd_driver_t *driver,
+ apr_dbd_transaction_t *trans);
+
+/** apr_dbd_transaction_mode_set: set the mode of transaction
+ *
+ * @param driver - the driver
+ * @param trans - the transaction
+ * @param mode - new mode of the transaction
+ * @return new mode of transaction
+ */
+APU_DECLARE(int) apr_dbd_transaction_mode_set(const apr_dbd_driver_t *driver,
+ apr_dbd_transaction_t *trans,
+ int mode);
+
/** apr_dbd_query: execute an SQL query that doesn't return a result set
*
* @param driver - the driver
Index: include/private/apr_dbd_internal.h
===================================================================
--- include/private/apr_dbd_internal.h (revision 411975)
+++ include/private/apr_dbd_internal.h (working copy)
@@ -29,6 +29,17 @@
extern "C" {
#endif
+#define TXN_IGNORE_ERRORS(t) \
+ ((t) && ((t)->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS))
+#define TXN_NOTICE_ERRORS(t) \
+ ((t) && !((t)->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS))
+
+#define TXN_DO_COMMIT(t) (!((t)->mode & APR_DBD_TRANSACTION_ROLLBACK))
+#define TXN_DO_ROLLBACK(t) ((t)->mode & APR_DBD_TRANSACTION_ROLLBACK)
+
+#define TXN_MODE_BITS \
+ (APR_DBD_TRANSACTION_ROLLBACK|APR_DBD_TRANSACTION_IGNORE_ERRORS)
+
struct apr_dbd_driver_t {
/** name */
const char *name;
@@ -82,9 +93,9 @@
/** transaction: start a transaction. May be a no-op.
*
- * @param pool - a pool to use for error messages (if any).
+ * @param pool - a pool to use for error messages (if any).
* @param handle - the connection
- * @param transaction - ptr to a transaction. May be null on entry
+ * @param trans - ptr to a transaction. May be null on entry
* @return 0 for success or error code
*/
int (*start_transaction)(apr_pool_t *pool, apr_dbd_t *handle,
@@ -94,7 +105,7 @@
* (commit on success, rollback on error).
* May be a no-op.
*
- * @param transaction - the transaction.
+ * @param trans - the transaction.
* @return 0 for success or error code
*/
int (*end_transaction)(apr_dbd_transaction_t *trans);
@@ -254,6 +265,21 @@
* @return param name, or NULL if col is out of bounds.
*/
const char* (*get_name)(const apr_dbd_results_t *res, int col);
+
+ /** transaction_mode_get: get the mode of transaction
+ *
+ * @param trans - the transaction.
+ * @return mode of transaction
+ */
+ int (*transaction_mode_get)(apr_dbd_transaction_t *trans);
+
+ /** transaction_mode_set: get the mode of transaction
+ *
+ * @param trans - the transaction.
+ * @param mode - new mode of the transaction
+ * @return new mode of transaction
+ */
+ int (*transaction_mode_set)(apr_dbd_transaction_t *trans, int mode);
};
/* Export mutex lock/unlock for drivers that need it */
Index: dbd/apr_dbd_sqlite2.c
===================================================================
--- dbd/apr_dbd_sqlite2.c (revision 411975)
+++ dbd/apr_dbd_sqlite2.c (working copy)
@@ -29,6 +29,7 @@
#include "apr_dbd_internal.h"
struct apr_dbd_transaction_t {
+ int mode;
int errnum;
apr_dbd_t *handle;
};
@@ -108,7 +109,9 @@
ret = 0;
}
else {
- sql->trans->errnum = ret;
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+ sql->trans->errnum = ret;
+ }
}
return ret;
@@ -214,7 +217,7 @@
ret = 0;
}
- if (sql->trans) {
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
sql->trans->errnum = ret;
}
@@ -292,7 +295,8 @@
int ret = -1; /* no transaction is an error cond */
if (trans) {
- if (trans->errnum) {
+ /* rollback on error or explicit rollback request */
+ if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
trans->errnum = 0;
ret =
dbd_sqlite_query(trans->handle, &rows,
@@ -308,6 +312,23 @@
return ret;
}
+static int dbd_sqlite_transaction_mode_get(apr_dbd_transaction_t *trans)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode;
+}
+
+static int dbd_sqlite_transaction_mode_set(apr_dbd_transaction_t *trans,
+ int mode)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode = (mode & TXN_MODE_BITS);
+}
+
static apr_dbd_t *dbd_sqlite_open(apr_pool_t * pool, const char *params_)
{
apr_dbd_t *sql;
@@ -396,6 +417,8 @@
dbd_sqlite_pvselect,
dbd_sqlite_pquery,
dbd_sqlite_pselect,
- dbd_sqlite_get_name
+ dbd_sqlite_get_name,
+ dbd_sqlite_transaction_mode_get,
+ dbd_sqlite_transaction_mode_set
};
#endif
Index: dbd/apr_dbd_sqlite3.c
===================================================================
--- dbd/apr_dbd_sqlite3.c (revision 411975)
+++ dbd/apr_dbd_sqlite3.c (working copy)
@@ -34,6 +34,7 @@
#define QUERY_MAX_ARGS 40
struct apr_dbd_transaction_t {
+ int mode;
int errnum;
apr_dbd_t *handle;
};
@@ -180,7 +181,7 @@
ret = sqlite3_finalize(stmt);
apr_dbd_mutex_unlock();
- if (sql->trans) {
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
sql->trans->errnum = ret;
}
return ret;
@@ -281,7 +282,7 @@
ret = 0;
}
apr_dbd_mutex_unlock();
- if (sql->trans) {
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
sql->trans->errnum = ret;
}
return ret;
@@ -387,7 +388,7 @@
ret = 0;
}
apr_dbd_mutex_unlock();
- if (sql->trans) {
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
sql->trans->errnum = ret;
}
@@ -524,7 +525,7 @@
}
apr_dbd_mutex_unlock();
- if (sql->trans) {
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
sql->trans->errnum = ret;
}
return ret;
@@ -578,7 +579,8 @@
int nrows = 0;
if (trans) {
- if (trans->errnum) {
+ /* rollback on error or explicit rollback request */
+ if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
trans->errnum = 0;
ret = dbd_sqlite3_query(trans->handle, &nrows, "ROLLBACK");
} else {
@@ -590,6 +592,23 @@
return ret;
}
+static int dbd_sqlite3_transaction_mode_get(apr_dbd_transaction_t *trans)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode;
+}
+
+static int dbd_sqlite3_transaction_mode_set(apr_dbd_transaction_t *trans,
+ int mode)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode = (mode & TXN_MODE_BITS);
+}
+
static apr_dbd_t *dbd_sqlite3_open(apr_pool_t *pool, const char *params)
{
apr_dbd_t *sql = NULL;
@@ -675,6 +694,8 @@
dbd_sqlite3_pvselect,
dbd_sqlite3_pquery,
dbd_sqlite3_pselect,
- dbd_sqlite3_get_name
+ dbd_sqlite3_get_name,
+ dbd_sqlite3_transaction_mode_get,
+ dbd_sqlite3_transaction_mode_set
};
#endif
Index: dbd/apr_dbd_oracle.c
===================================================================
--- dbd/apr_dbd_oracle.c (revision 411975)
+++ dbd/apr_dbd_oracle.c (working copy)
@@ -123,6 +123,7 @@
static int dbd_oracle_end_transaction(apr_dbd_transaction_t *trans);
struct apr_dbd_transaction_t {
+ int mode;
enum { TRANS_NONE, TRANS_ERROR, TRANS_1, TRANS_2 } status;
apr_dbd_t *handle;
OCITrans *trans;
@@ -1234,7 +1235,7 @@
#endif
/* fallthrough */
default:
- if (trans) {
+ if (TXN_NOTICE_ERRORS(trans)) {
trans->status = TRANS_ERROR;
}
return 1;
@@ -1277,7 +1278,7 @@
#endif
/* fallthrough */
default:
- if (trans) {
+ if (TXN_NOTICE_ERRORS(trans)) {
trans->status = TRANS_ERROR;
}
return 1;
@@ -1383,7 +1384,7 @@
#endif
/* fallthrough */
default:
- if (trans) {
+ if (TXN_NOTICE_ERRORS(trans)) {
trans->status = TRANS_ERROR;
}
return 1;
@@ -1476,7 +1477,7 @@
printf("Executing prepared statement: %s\n", sql->buf);
#endif
default:
- if (trans) {
+ if (TXN_NOTICE_ERRORS(trans)) {
trans->status = TRANS_ERROR;
}
return 1;
@@ -1571,7 +1572,7 @@
printf("Executing prepared statement: %s\n", sql->buf);
#endif
default:
- if (trans) {
+ if (TXN_NOTICE_ERRORS(trans)) {
trans->status = TRANS_ERROR;
}
return 1;
@@ -1664,7 +1665,12 @@
status = OCITransRollback(handle->svc, handle->err, OCI_DEFAULT);
break;
default:
- status = OCITransCommit(handle->svc, handle->err, OCI_DEFAULT);
+ /* rollback on explicit rollback request */
+ if (TXN_DO_ROLLBACK(trans)) {
+ status = OCITransRollback(handle->svc, handle->err, OCI_DEFAULT);
+ } else {
+ status = OCITransCommit(handle->svc, handle->err, OCI_DEFAULT);
+ }
break;
}
@@ -1682,6 +1688,23 @@
return ret;
}
+static int dbd_oracle_transaction_mode_get(apr_dbd_transaction_t *trans)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode;
+}
+
+static int dbd_oracle_transaction_mode_set(apr_dbd_transaction_t *trans,
+ int mode)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode = (mode & TXN_MODE_BITS);
+}
+
/* This doesn't work for BLOB because of NULLs, but it can fake it
* if the BLOB is really a string
*/
@@ -1910,6 +1933,8 @@
dbd_oracle_pvselect,
dbd_oracle_pquery,
dbd_oracle_pselect,
- dbd_oracle_get_name
+ dbd_oracle_get_name,
+ dbd_oracle_transaction_mode_get,
+ dbd_oracle_transaction_mode_set
};
#endif
Index: dbd/apr_dbd_pgsql.c
===================================================================
--- dbd/apr_dbd_pgsql.c (revision 411975)
+++ dbd/apr_dbd_pgsql.c (working copy)
@@ -37,6 +37,7 @@
#define QUERY_MAX_ARGS 40
struct apr_dbd_transaction_t {
+ int mode;
int errnum;
apr_dbd_t *handle;
};
@@ -79,6 +80,19 @@
return sql->trans->errnum;
}
if (seek) { /* synchronous query */
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ }
res = PQexec(sql->conn, query);
if (res) {
ret = PQresultStatus(res);
@@ -91,10 +105,38 @@
ret = PGRES_FATAL_ERROR;
}
if (ret != 0) {
- if (sql->trans) {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ } else if (TXN_NOTICE_ERRORS(sql->trans)){
sql->trans->errnum = ret;
}
return ret;
+ } else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "RELEASE SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ }
}
if (!*results) {
*results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
@@ -107,11 +149,52 @@
apr_pool_cleanup_null);
}
else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ }
if (PQsendQuery(sql->conn, query) == 0) {
- if (sql->trans) {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ } else if (TXN_NOTICE_ERRORS(sql->trans)){
sql->trans->errnum = 1;
}
return 1;
+ } else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "RELEASE SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ }
}
if (*results == NULL) {
*results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
@@ -209,6 +292,21 @@
if (sql->trans && sql->trans->errnum) {
return sql->trans->errnum;
}
+
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ }
+
res = PQexec(sql->conn, query);
if (res) {
ret = PQresultStatus(res);
@@ -222,9 +320,43 @@
else {
ret = PGRES_FATAL_ERROR;
}
- if (sql->trans) {
- sql->trans->errnum = ret;
+
+ if (ret != 0){
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else if (TXN_NOTICE_ERRORS(sql->trans)){
+ sql->trans->errnum = ret;
+ }
+ } else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "RELEASE SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ }
}
+
return ret;
}
@@ -369,6 +501,20 @@
return sql->trans->errnum;
}
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ return sql->trans->errnum = PGRES_FATAL_ERROR;
+ }
+ }
+
if (statement->prepared) {
res = PQexecPrepared(sql->conn, statement->name, nargs, values, 0, 0,
0);
@@ -389,9 +535,42 @@
ret = PGRES_FATAL_ERROR;
}
- if (sql->trans) {
- sql->trans->errnum = ret;
+ if (ret != 0){
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else if (TXN_NOTICE_ERRORS(sql->trans)){
+ sql->trans->errnum = ret;
+ }
+ } else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "RELEASE SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ }
}
+
return ret;
}
@@ -431,6 +610,20 @@
}
if (seek) { /* synchronous query */
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ }
if (statement->prepared) {
res = PQexecPrepared(sql->conn, statement->name, nargs, values, 0,
0, 0);
@@ -452,10 +645,40 @@
ret = PGRES_FATAL_ERROR;
}
if (ret != 0) {
- if (sql->trans) {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else if (TXN_NOTICE_ERRORS(sql->trans)){
sql->trans->errnum = ret;
}
return ret;
+ } else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "RELEASE SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ }
}
if (!*results) {
*results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
@@ -468,6 +691,20 @@
apr_pool_cleanup_null);
}
else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn, "SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ }
if (statement->prepared) {
rv = PQsendQueryPrepared(sql->conn, statement->name, nargs, values,
0, 0, 0);
@@ -477,10 +714,40 @@
values, 0, 0, 0);
}
if (rv == 0) {
- if (sql->trans) {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "ROLLBACK TO SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else if (TXN_NOTICE_ERRORS(sql->trans)){
sql->trans->errnum = 1;
}
return 1;
+ } else {
+ if (TXN_IGNORE_ERRORS(sql->trans)) {
+ PGresult *res = PQexec(sql->conn,
+ "RELEASE SAVEPOINT APR_DBD_TXN_SP");
+ if (res) {
+ ret = PQresultStatus(res);
+ PQclear(res);
+ if (!dbd_pgsql_is_success(ret)) {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ } else {
+ sql->trans->errnum = ret;
+ return PGRES_FATAL_ERROR;
+ }
+ }
}
if (!*results) {
*results = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
@@ -489,9 +756,6 @@
(*results)->handle = sql->conn;
}
- if (sql->trans) {
- sql->trans->errnum = ret;
- }
return ret;
}
@@ -551,7 +815,8 @@
PGresult *res;
int ret = -1; /* no transaction is an error cond */
if (trans) {
- if (trans->errnum) {
+ /* rollback on error or explicit rollback request */
+ if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
trans->errnum = 0;
res = PQexec(trans->handle->conn, "ROLLBACK");
}
@@ -573,6 +838,23 @@
return ret;
}
+static int dbd_pgsql_transaction_mode_get(apr_dbd_transaction_t *trans)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode;
+}
+
+static int dbd_pgsql_transaction_mode_set(apr_dbd_transaction_t *trans,
+ int mode)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode = (mode & TXN_MODE_BITS);
+}
+
static apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params)
{
apr_dbd_t *sql;
@@ -662,6 +944,8 @@
dbd_pgsql_pvselect,
dbd_pgsql_pquery,
dbd_pgsql_pselect,
- dbd_pgsql_get_name
+ dbd_pgsql_get_name,
+ dbd_pgsql_transaction_mode_get,
+ dbd_pgsql_transaction_mode_set
};
#endif
Index: dbd/apr_dbd.c
===================================================================
--- dbd/apr_dbd.c (revision 411975)
+++ dbd/apr_dbd.c (working copy)
@@ -208,6 +208,19 @@
return driver->end_transaction(trans);
}
+APU_DECLARE(int) apr_dbd_transaction_mode_get(const apr_dbd_driver_t *driver,
+ apr_dbd_transaction_t *trans)
+{
+ return driver->transaction_mode_get(trans);
+}
+
+APU_DECLARE(int) apr_dbd_transaction_mode_set(const apr_dbd_driver_t *driver,
+ apr_dbd_transaction_t *trans,
+ int mode)
+{
+ return driver->transaction_mode_set(trans, mode);
+}
+
APU_DECLARE(apr_status_t) apr_dbd_close(const apr_dbd_driver_t *driver,
apr_dbd_t *handle)
{
Index: apr_dbd_mysql.c
===================================================================
--- apr_dbd_mysql.c (revision 46)
+++ apr_dbd_mysql.c (working copy)
@@ -65,6 +65,9 @@
};
struct apr_dbd_transaction_t {
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+ int mode;
+#endif
int errnum;
apr_dbd_t *handle;
};
@@ -117,7 +120,11 @@
ret = mysql_errno(sql->conn);
}
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+#else
if (sql->trans) {
+#endif
sql->trans->errnum = ret;
}
return ret;
@@ -246,7 +253,11 @@
ret = mysql_errno(sql->conn);
}
*nrows = mysql_affected_rows(sql->conn);
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+#else
if (sql->trans) {
+#endif
sql->trans->errnum = ret;
}
return ret;
@@ -345,7 +356,11 @@
}
*nrows = mysql_stmt_affected_rows(statement->stmt);
}
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+#else
if (sql->trans) {
+#endif
sql->trans->errnum = ret;
}
return ret;
@@ -388,7 +403,11 @@
}
*nrows = mysql_stmt_affected_rows(statement->stmt);
}
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+#else
if (sql->trans) {
+#endif
sql->trans->errnum = ret;
}
return ret;
@@ -469,7 +488,11 @@
if (ret != 0) {
ret = mysql_stmt_errno(statement->stmt);
}
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+#else
if (sql->trans) {
+#endif
sql->trans->errnum = ret;
}
return ret;
@@ -551,7 +574,11 @@
if (ret != 0) {
ret = mysql_stmt_errno(statement->stmt);
}
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+ if (TXN_NOTICE_ERRORS(sql->trans)) {
+#else
if (sql->trans) {
+#endif
sql->trans->errnum = ret;
}
return ret;
@@ -560,7 +587,12 @@
{
int ret = -1;
if (trans) {
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+ /* rollback on error or explicit rollback request */
+ if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
+#else
if (trans->errnum) {
+#endif
trans->errnum = 0;
ret = mysql_rollback(trans->handle->conn);
}
@@ -591,6 +623,24 @@
handle->trans = *trans;
return (*trans)->errnum;
}
+#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
+static int dbd_mysql_transaction_mode_get(apr_dbd_transaction_t *trans)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode;
+}
+
+static int dbd_mysql_transaction_mode_set(apr_dbd_transaction_t *trans,
+ int mode)
+{
+ if (!trans)
+ return APR_DBD_TRANSACTION_COMMIT;
+
+ return trans->mode = (mode & TXN_MODE_BITS);
+}
+#endif
static apr_dbd_t *dbd_mysql_open(apr_pool_t *pool, const char *params)
{
static const char *const delims = " \r\n\t;|,";
@@ -752,7 +802,9 @@
dbd_mysql_pselect
#if APU_MAJOR_VERSION >= 2 || (APU_MAJOR_VERSION == 1 && APU_MINOR_VERSION >= 3)
,
- dbd_mysql_get_name
+ dbd_mysql_get_name,
+ dbd_mysql_transaction_mode_get,
+ dbd_mysql_transaction_mode_set
#endif
};