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

Reply via email to