So, we know by now that some databases will fail the whole transaction
if any kind of error occurs, while some others won't. We also know
that sometimes one option is the desired behaviour, while at other
times the other option is what the caller wants - regardless of the
underlying database. So, given that APR DBD is supposed to be database
neutral, here is another solution for the same problem.
#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 error conditions */
We then have our already known functions:
apr_dbd_transaction_mode_get()
apr_dbd_transaction_mode_set()
So, we can call (once only!):
apr_dbd_transaction_mode_set(driver, trans,
APR_DBD_TRANSACTION_COMMIT|
APR_DBD_TRANSACTION_IGNORE_ERRORS);
Then, inside the query/select functions we leave trans->errnum at zero
in case any error occurs, but return the status to the caller. If the
caller is intersted in the status, he/she can do something about it
(such as call mode set with ROLLBACK and then call end, to rollback).
Or, the caller can just continue with the next query/select. With
IGNORE_ERRORS set, at no point will the transaction be in a bad state
(i.e. this is how SQLite/Oracle/MySQL work). After all that, COMMIT is
executed and whatever is good gets committed (so this is effectively
COMMIT_ON_SUCCESS, but without the need to keep "recovering" the error
status).
In case the IGNORE_ERRORS flag isn't set, the logic reverts to the
current behaviour (i.e. the way PGSQL does things). Any error triggers
a transaction error and the whole transaction will be rolled back for
sure.
Here is the part we can now do with IGNORE_ERRORS that we could not do
before - we can make PGSQL behave like other databases if we so wish.
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). If an error occurs withing query/select, we
simply roll back to the previous savepoint, keep trans->errnum always
at zero and return the error code of the actual query. The caller can
then decide what should be done with the whole thing. This brings us
in line with transactional behaviour other databases.
In summary, we can pick the behaviour we want and users get
savepoints/error recovery for free with PGSQL if they so wish.
Let me know what you think...
--
Bojan