Patch PDO_Transaction_During_AutoCommit_and_Commit_Continue_Support for PDO Firebird Bug #63690
Patch version 2012-12-05 07:16 UTC
Return to Bug #63690 |
Download this patch
Patch Revisions:
Developer: james@kenjim.com
diff --git a/ext/pdo/pdo_dbh.c b/ext/pdo/pdo_dbh.c
index acdc0dd..28d9e45 100644
--- a/ext/pdo/pdo_dbh.c
+++ b/ext/pdo/pdo_dbh.c
@@ -366,7 +366,8 @@ static PHP_METHOD(PDO, dbh_constructor)
dbh->default_fetch_type = PDO_FETCH_BOTH;
}
- dbh->auto_commit = pdo_attr_lval(options, PDO_ATTR_AUTOCOMMIT, 1 TSRMLS_CC);
+ dbh->auto_commit_setting = dbh->auto_commit = pdo_attr_lval(options, PDO_ATTR_AUTOCOMMIT, 1 TSRMLS_CC);
+ dbh->commit_continue = 0;
if (!dbh->data_source || (username && !dbh->username) || (password && !dbh->password)) {
php_error_docref(NULL TSRMLS_CC, E_ERROR, "out of memory");
@@ -611,11 +612,6 @@ static PHP_METHOD(PDO, beginTransaction)
}
PDO_CONSTRUCT_CHECK;
- if (dbh->in_txn) {
- zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "There is already an active transaction");
- RETURN_FALSE;
- }
-
if (!dbh->methods->begin) {
/* TODO: this should be an exception; see the auto-commit mode
* comments below */
@@ -623,6 +619,27 @@ static PHP_METHOD(PDO, beginTransaction)
RETURN_FALSE;
}
+ if (dbh->auto_commit_setting && dbh->auto_commit) {
+ if (dbh->methods->set_attribute) {
+ PDO_DBH_CLEAR_ERR();
+
+ zval *auto_commit_value;
+ MAKE_STD_ZVAL(auto_commit_value);
+ ZVAL_BOOL(auto_commit_value, FALSE);
+ dbh->methods->set_attribute(dbh, PDO_ATTR_AUTOCOMMIT, auto_commit_value TSRMLS_CC);
+ zval_dtor(auto_commit_value);
+ }
+ }
+
+ if (dbh->in_txn) {
+ if (dbh->commit_continue) { //Silently ignore error if we're using commits that keep transactions.
+ RETURN_TRUE;
+ } else {
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "There is already an active transaction");
+ RETURN_FALSE;
+ }
+ }
+
if (dbh->methods->begin(dbh TSRMLS_CC)) {
dbh->in_txn = 1;
RETURN_TRUE;
@@ -650,7 +667,20 @@ static PHP_METHOD(PDO, commit)
}
if (dbh->methods->commit(dbh TSRMLS_CC)) {
- dbh->in_txn = 0;
+ if (!dbh->commit_continue) {
+ dbh->in_txn = 0;
+ }
+ if (dbh->auto_commit_setting && !dbh->auto_commit) {
+ if (dbh->methods->set_attribute) {
+ PDO_DBH_CLEAR_ERR();
+
+ zval *auto_commit_value;
+ MAKE_STD_ZVAL(auto_commit_value);
+ ZVAL_BOOL(auto_commit_value, TRUE);
+ dbh->methods->set_attribute(dbh, PDO_ATTR_AUTOCOMMIT, auto_commit_value TSRMLS_CC);
+ zval_dtor(auto_commit_value);
+ }
+ }
RETURN_TRUE;
}
@@ -676,7 +706,21 @@ static PHP_METHOD(PDO, rollBack)
}
if (dbh->methods->rollback(dbh TSRMLS_CC)) {
- dbh->in_txn = 0;
+ if (!dbh->commit_continue) {
+ dbh->in_txn = 0;
+ }
+
+ if (dbh->auto_commit_setting && !dbh->auto_commit) {
+ if (dbh->methods->set_attribute) {
+ PDO_DBH_CLEAR_ERR();
+
+ zval *auto_commit_value;
+ MAKE_STD_ZVAL(auto_commit_value);
+ ZVAL_BOOL(auto_commit_value, TRUE);
+ dbh->methods->set_attribute(dbh, PDO_ATTR_AUTOCOMMIT, auto_commit_value TSRMLS_CC);
+ zval_dtor(auto_commit_value);
+ }
+ }
RETURN_TRUE;
}
@@ -835,6 +879,33 @@ static int pdo_dbh_attribute_set(pdo_dbh_t *dbh, long attr, zval *value TSRMLS_D
return SUCCESS;
}
+ case PDO_ATTR_AUTOCOMMIT:
+ /* handle autocommit so we can update auto_commit_setting before passing it on to the driver */
+ convert_to_boolean(value);
+
+ /* ignore if the new value equals the old one */
+ if (dbh->auto_commit_setting ^ Z_BVAL_P(value)) {
+ if (dbh->auto_commit_setting != dbh->auto_commit) {
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "Can not set auto commit while in a manual transaction.");
+ return FAILURE;
+ }
+ if (!dbh->methods->begin) {
+ zend_throw_exception_ex(php_pdo_get_exception(), 0 TSRMLS_CC, "This driver doesn't support transactions");
+ return FAILURE;
+ }
+
+ dbh->auto_commit_setting = Z_BVAL_P(value);
+ if (!dbh->methods->set_attribute) {
+ goto fail;
+ }
+
+ PDO_DBH_CLEAR_ERR();
+ if (dbh->methods->set_attribute(dbh, attr, value TSRMLS_CC)) {
+ return SUCCESS;
+ }
+ }
+
+
default:
;
}
@@ -924,6 +995,9 @@ static PHP_METHOD(PDO, getAttribute)
case PDO_ATTR_DEFAULT_FETCH_MODE:
RETURN_LONG(dbh->default_fetch_type);
+ case PDO_ATTR_AUTOCOMMIT:
+ RETURN_BOOL(dbh->auto_commit_setting);
+
}
if (!dbh->methods->get_attribute) {
@@ -1568,6 +1642,7 @@ static void pdo_dbh_free_storage(pdo_dbh_t *dbh TSRMLS_DC)
if (dbh->in_txn && dbh->methods && dbh->methods->rollback) {
dbh->methods->rollback(dbh TSRMLS_CC);
dbh->in_txn = 0;
+ dbh->auto_commit = dbh->auto_commit_setting;
}
if (dbh->is_persistent && dbh->methods && dbh->methods->persistent_shutdown) {
diff --git a/ext/pdo/php_pdo_driver.h b/ext/pdo/php_pdo_driver.h
index b9c46ba..4c4a28c 100644
--- a/ext/pdo/php_pdo_driver.h
+++ b/ext/pdo/php_pdo_driver.h
@@ -469,9 +469,16 @@ struct _pdo_dbh_t {
/* when set, convert int/floats to strings */
unsigned stringify:1;
+ /* keeps track of actual auto commit setting.
+ * differes from auto_commit when in a manually started transaction */
+ unsigned auto_commit_setting:1;
+
+ /* if true, after a commit we will still be in a transaction */
+ unsigned commit_continue:1;
+
/* the sum of the number of bits here and the bit fields preceeding should
* equal 32 */
- unsigned _reserved_flags:21;
+ unsigned _reserved_flags:19;
/* data source string used to open this handle */
const char *data_source;
diff --git a/ext/pdo_firebird/firebird_driver.c b/ext/pdo_firebird/firebird_driver.c
index 9b0f596..f2ead40 100644
--- a/ext/pdo_firebird/firebird_driver.c
+++ b/ext/pdo_firebird/firebird_driver.c
@@ -371,9 +371,19 @@ static int firebird_handle_commit(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */
{
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
- if (isc_commit_transaction(H->isc_status, &H->tr)) {
- RECORD_ERROR(dbh);
- return 0;
+ if (H->commit_retaining) {
+ dbh->commit_continue = 1;
+ if (isc_commit_retaining(H->isc_status, &H->tr)) {
+ RECORD_ERROR(dbh);
+ return 0;
+ }
+ } else {
+ dbh->commit_continue = 0;
+ if (isc_commit_transaction(H->isc_status, &H->tr)) {
+ RECORD_ERROR(dbh);
+ return 0;
+ }
+ dbh->in_txn = 0;
}
return 1;
}
@@ -384,9 +394,18 @@ static int firebird_handle_rollback(pdo_dbh_t *dbh TSRMLS_DC) /* {{{ */
{
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
- if (isc_rollback_transaction(H->isc_status, &H->tr)) {
- RECORD_ERROR(dbh);
- return 0;
+ if (H->commit_retaining) {
+ dbh->commit_continue = 1;
+ if (isc_rollback_retaining(H->isc_status, &H->tr)) {
+ RECORD_ERROR(dbh);
+ return 0;
+ }
+ } else {
+ dbh->commit_continue = 0;
+ if (isc_rollback_transaction(H->isc_status, &H->tr)) {
+ RECORD_ERROR(dbh);
+ return 0;
+ }
}
return 1;
}
@@ -479,24 +498,25 @@ static int firebird_handle_set_attribute(pdo_dbh_t *dbh, long attr, zval *val TS
case PDO_ATTR_AUTOCOMMIT:
convert_to_boolean(val);
-
+
/* ignore if the new value equals the old one */
- if (dbh->auto_commit ^ Z_BVAL_P(val)) {
- if (dbh->in_txn) {
- if (Z_BVAL_P(val)) {
- /* turning on auto_commit with an open transaction is illegal, because
- we won't know what to do with it */
- H->last_app_error = "Cannot enable auto-commit while a transaction is already open";
- return 0;
- } else {
- /* close the transaction */
- if (!firebird_handle_commit(dbh TSRMLS_CC)) {
- break;
+ if (dbh->auto_commit ^ Z_BVAL_P(val)) {
+ if (dbh->in_txn) {
+ if (Z_BVAL_P(val)) { //Turn on auto commit
+ if(!H->commit_retaining) {
+ /* turning on auto_commit with an open transaction is illegal, because
+ we won't know what to do with it */
+ H->last_app_error = "Cannot enable auto-commit while a transaction is already open";
+ return 0;
}
- dbh->in_txn = 0;
- }
- }
- dbh->auto_commit = Z_BVAL_P(val);
+ } else {
+ /* close the transaction */
+ if (!firebird_handle_commit(dbh TSRMLS_CC)) {
+ break;
+ }
+ }
+ }
+ dbh->auto_commit = Z_BVAL_P(val);
}
return 1;
@@ -505,6 +525,11 @@ static int firebird_handle_set_attribute(pdo_dbh_t *dbh, long attr, zval *val TS
H->fetch_table_names = Z_BVAL_P(val);
return 1;
+ case PDO_FB_ATTR_COMMIT_RETAINING:
+ convert_to_boolean(val);
+ H->commit_retaining = Z_BVAL_P(val);
+ return 1;
+
case PDO_FB_ATTR_DATE_FORMAT:
convert_to_string(val);
if (H->date_format) {
@@ -656,6 +681,8 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options TSRM
pdo_firebird_db_handle *H = dbh->driver_data = pecalloc(1,sizeof(*H),dbh->is_persistent);
+ H->commit_retaining = 0;
+
php_pdo_parse_data_source(dbh->data_source, dbh->data_source_len, vars, 3);
do {
diff --git a/ext/pdo_firebird/pdo_firebird.c b/ext/pdo_firebird/pdo_firebird.c
index c2bbe37..7d7705d 100644
--- a/ext/pdo_firebird/pdo_firebird.c
+++ b/ext/pdo_firebird/pdo_firebird.c
@@ -70,6 +70,7 @@ ZEND_GET_MODULE(pdo_firebird)
PHP_MINIT_FUNCTION(pdo_firebird) /* {{{ */
{
+ REGISTER_PDO_CLASS_CONST_LONG("FB_ATTR_COMMIT_RETAINING", (long) PDO_FB_ATTR_COMMIT_RETAINING);
REGISTER_PDO_CLASS_CONST_LONG("FB_ATTR_DATE_FORMAT", (long) PDO_FB_ATTR_DATE_FORMAT);
REGISTER_PDO_CLASS_CONST_LONG("FB_ATTR_TIME_FORMAT", (long) PDO_FB_ATTR_TIME_FORMAT);
REGISTER_PDO_CLASS_CONST_LONG("FB_ATTR_TIMESTAMP_FORMAT", (long) PDO_FB_ATTR_TIMESTAMP_FORMAT);
diff --git a/ext/pdo_firebird/php_pdo_firebird_int.h b/ext/pdo_firebird/php_pdo_firebird_int.h
index bb15d54..0316f3f 100644
--- a/ext/pdo_firebird/php_pdo_firebird_int.h
+++ b/ext/pdo_firebird/php_pdo_firebird_int.h
@@ -84,8 +84,11 @@ typedef struct {
/* prepend table names on column names in fetch */
unsigned fetch_table_names:1;
+
+ /* commit retain used to store commit/rollback mode */
+ unsigned commit_retaining:1;
- unsigned _reserved:31;
+ unsigned _reserved:30;
} pdo_firebird_db_handle;
@@ -136,6 +139,7 @@ enum {
PDO_FB_ATTR_DATE_FORMAT = PDO_ATTR_DRIVER_SPECIFIC,
PDO_FB_ATTR_TIME_FORMAT,
PDO_FB_ATTR_TIMESTAMP_FORMAT,
+ PDO_FB_ATTR_COMMIT_RETAINING,
};
#endif /* PHP_PDO_FIREBIRD_INT_H */
|