Quoting Bojan Smojver <[EMAIL PROTECTED]>:
By setting IGNORE_ERRORS, we can insert SAVEPOINTS before every
query/select inside PGSQL driver automatically (i.e. the caller
doesn't have to do it).
Something like this. Note that this is just to show the concept - I'm
sure none of it would actually work, especially the Oracle bit and
savepoints in PGSQL (especially the unimplemented ones in
pquery/pselect :-). Anyhow, it should be sufficient to see the
proposal in terms of code.
--
Bojan
Index: include/apr_dbd.h
===================================================================
--- include/apr_dbd.h (revision 398810)
+++ include/apr_dbd.h (working copy)
@@ -153,6 +153,31 @@
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 */
+#define APR_DBD_TRANSACTION_MODE_BITS 0x03 /**< significant transaction mode bits */
+
+/** 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 398810)
+++ include/private/apr_dbd_internal.h (working copy)
@@ -82,9 +82,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 +94,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 +254,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 398810)
+++ 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 (sql->trans && !(trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
+ sql->trans->errnum = ret;
+ }
}
return ret;
@@ -214,7 +217,7 @@
ret = 0;
}
- if (sql->trans) {
+ if (sql->trans && !(sql->trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
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 || (trans->mode & APR_DBD_TRANSACTION_ROLLBACK)) {
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 & APR_DBD_TRANSACTION_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 398810)
+++ dbd/apr_dbd_sqlite3.c (working copy)
@@ -32,6 +32,7 @@
#define MAX_RETRY_SLEEP 100000
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 (sql->trans && !(sql->trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
sql->trans->errnum = ret;
}
return ret;
@@ -281,7 +282,7 @@
ret = 0;
}
apr_dbd_mutex_unlock();
- if (sql->trans) {
+ if (sql->trans && !(sql->trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
sql->trans->errnum = ret;
}
return ret;
@@ -355,7 +356,8 @@
int nrows = 0;
if (trans) {
- if (trans->errnum) {
+ /* rollback on error or explicit rollback request */
+ if (trans->errnum || (trans->mode & APR_DBD_TRANSACTION_ROLLBACK)) {
trans->errnum = 0;
ret = dbd_sqlite3_query(trans->handle, &nrows, "ROLLBACK");
} else {
@@ -367,6 +369,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 & APR_DBD_TRANSACTION_MODE_BITS);
+}
+
static apr_dbd_t *dbd_sqlite3_open(apr_pool_t *pool, const char *params)
{
apr_dbd_t *sql = NULL;
@@ -444,6 +463,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 398810)
+++ 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 (trans && !(trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
trans->status = TRANS_ERROR;
}
return 1;
@@ -1277,7 +1278,7 @@
#endif
/* fallthrough */
default:
- if (trans) {
+ if (trans && !(trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
trans->status = TRANS_ERROR;
}
return 1;
@@ -1383,7 +1384,7 @@
#endif
/* fallthrough */
default:
- if (trans) {
+ if (trans && !(trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
trans->status = TRANS_ERROR;
}
return 1;
@@ -1476,7 +1477,7 @@
printf("Executing prepared statement: %s\n", sql->buf);
#endif
default:
- if (trans) {
+ if (trans && !(trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
trans->status = TRANS_ERROR;
}
return 1;
@@ -1571,7 +1572,7 @@
printf("Executing prepared statement: %s\n", sql->buf);
#endif
default:
- if (trans) {
+ if (trans && !(trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
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 (trans->mode & APR_DBD_TRANSACTION_ROLLBACK) {
+ 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 & APR_DBD_TRANSACTION_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 398810)
+++ dbd/apr_dbd_pgsql.c (working copy)
@@ -31,7 +31,9 @@
#define QUERY_MAX_ARGS 40
struct apr_dbd_transaction_t {
+ int mode;
int errnum;
+ int sp_count;
apr_dbd_t *handle;
};
@@ -73,6 +75,24 @@
return sql->trans->errnum;
}
if (seek) { /* synchronous query */
+ if (sql->trans &&
+ (sql->trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
+ char spq = apr_psprintf(pool, "SAVEPOINT apr_dbd_sp_%d",
+ ++sql->trans->sp_count);
+
+ res = PQexec(sql->conn, spq);
+ 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;
+ }
+ }
res = PQexec(sql->conn, query);
if (res) {
ret = PQresultStatus(res);
@@ -86,7 +106,26 @@
}
if (ret != 0) {
if (sql->trans) {
- sql->trans->errnum = ret;
+ if ((sql->trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
+ char spq = apr_psprintf(
+ pool, "ROLLBACK TO SAVEPOINT apr_dbd_sp_%d",
+ sql->trans->sp_count);
+
+ res = PQexec(sql->conn, spq);
+ 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 {
+ sql->trans->errnum = ret;
+ }
}
return ret;
}
@@ -203,6 +242,26 @@
if (sql->trans && sql->trans->errnum) {
return sql->trans->errnum;
}
+
+ if (sql->trans &&
+ (sql->trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
+ char spq = apr_psprintf(pool, "SAVEPOINT apr_dbd_sp_%d",
+ ++sql->trans->sp_count);
+
+ res = PQexec(sql->conn, spq);
+ 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;
+ }
+ }
+
res = PQexec(sql->conn, query);
if (res) {
ret = PQresultStatus(res);
@@ -216,9 +275,32 @@
else {
ret = PGRES_FATAL_ERROR;
}
- if (sql->trans) {
- sql->trans->errnum = ret;
+
+ if (ret != 0){
+ if (sql->trans) {
+ if ((sql->trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
+ char spq = apr_psprintf(
+ pool, "ROLLBACK TO SAVEPOINT apr_dbd_sp_%d",
+ sql->trans->sp_count);
+
+ res = PQexec(sql->conn, spq);
+ 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 {
+ sql->trans->errnum = ret;
+ }
+ }
}
+
return ret;
}
@@ -383,7 +465,7 @@
ret = PGRES_FATAL_ERROR;
}
- if (sql->trans) {
+ if (sql->trans && !(sql->trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
sql->trans->errnum = ret;
}
return ret;
@@ -446,7 +528,8 @@
ret = PGRES_FATAL_ERROR;
}
if (ret != 0) {
- if (sql->trans) {
+ if (sql->trans &&
+ !(sql->trans->mode & APR_DBD_TRANSACTION_IGNORE_ERRORS)) {
sql->trans->errnum = ret;
}
return ret;
@@ -483,9 +566,6 @@
(*results)->handle = sql->conn;
}
- if (sql->trans) {
- sql->trans->errnum = ret;
- }
return ret;
}
@@ -545,7 +625,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 || (trans->mode & APR_DBD_TRANSACTION_ROLLBACK)) {
trans->errnum = 0;
res = PQexec(trans->handle->conn, "ROLLBACK");
}
@@ -567,6 +648,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 & APR_DBD_TRANSACTION_MODE_BITS);
+}
+
static apr_dbd_t *dbd_pgsql_open(apr_pool_t *pool, const char *params)
{
apr_dbd_t *sql;
@@ -656,6 +754,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 398810)
+++ 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)
{