Skip to content

Commit 16962f7

Browse files
committed
[pdo_firebird] Transaction management optimization
1 parent d7d3a1c commit 16962f7

14 files changed

+388
-72
lines changed

ext/pdo/tests/pdo_017.phpt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ $dir = getenv('REDIR_TEST_DIR');
88
if (false == $dir) die('skip no driver');
99
require_once $dir . 'pdo_test.inc';
1010
PDOTest::skip();
11-
if (str_starts_with(getenv('PDOTEST_DSN'), "firebird")) die('xfail firebird driver does not behave as expected');
1211

1312
$db = PDOTest::factory();
1413
try {

ext/pdo_firebird/firebird_driver.c

Lines changed: 145 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
static int php_firebird_alloc_prepare_stmt(pdo_dbh_t*, const zend_string*, XSQLDA*, isc_stmt_handle*,
3535
HashTable*);
36+
static bool php_firebird_rollback_transaction(pdo_dbh_t *dbh);
3637

3738
const char CHR_LETTER = 1;
3839
const char CHR_DIGIT = 2;
@@ -526,17 +527,14 @@ static void firebird_handle_closer(pdo_dbh_t *dbh) /* {{{ */
526527
{
527528
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
528529

529-
if (dbh->in_txn) {
530+
if (H->tr) {
530531
if (dbh->auto_commit) {
531-
if (isc_commit_transaction(H->isc_status, &H->tr)) {
532-
php_firebird_error(dbh);
533-
}
532+
php_firebird_commit_transaction(dbh, /* release */ false);
534533
} else {
535-
if (isc_rollback_transaction(H->isc_status, &H->tr)) {
536-
php_firebird_error(dbh);
537-
}
534+
php_firebird_rollback_transaction(dbh);
538535
}
539536
}
537+
H->in_manually_txn = 0;
540538

541539
if (isc_detach_database(H->isc_status, &H->db)) {
542540
php_firebird_error(dbh);
@@ -702,9 +700,10 @@ static zend_long firebird_handle_doer(pdo_dbh_t *dbh, const zend_string *sql) /*
702700
}
703701
}
704702

705-
/* commit if we're in auto_commit mode */
706-
if (dbh->auto_commit && isc_commit_retaining(H->isc_status, &H->tr)) {
707-
php_firebird_error(dbh);
703+
if (dbh->auto_commit && !H->in_manually_txn) {
704+
if (!php_firebird_commit_transaction(dbh, /* retain */ true)) {
705+
ret = -1;
706+
}
708707
}
709708

710709
free_statement:
@@ -756,8 +755,8 @@ static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *un
756755
}
757756
/* }}} */
758757

759-
/* called by PDO to start a transaction */
760-
static bool firebird_handle_begin(pdo_dbh_t *dbh) /* {{{ */
758+
/* php_firebird_begin_transaction */
759+
static bool php_firebird_begin_transaction(pdo_dbh_t *dbh) /* {{{ */
761760
{
762761
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
763762
char tpb[8] = { isc_tpb_version3 }, *ptpb = tpb+1;
@@ -809,12 +808,84 @@ static bool firebird_handle_begin(pdo_dbh_t *dbh) /* {{{ */
809808
}
810809
/* }}} */
811810

811+
/* called by PDO to start a transaction */
812+
static bool firebird_handle_manually_begin(pdo_dbh_t *dbh) /* {{{ */
813+
{
814+
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
815+
816+
/**
817+
* If in autocommit mode and in transaction, we will need to close the transaction once.
818+
*/
819+
if (dbh->auto_commit && H->tr) {
820+
if (!php_firebird_commit_transaction(dbh, /* release */ false)) {
821+
return false;
822+
}
823+
}
824+
825+
if (!php_firebird_begin_transaction(dbh)) {
826+
return false;
827+
}
828+
H->in_manually_txn = 1;
829+
return true;
830+
}
831+
/* }}} */
832+
833+
/* php_firebird_commit_transaction */
834+
bool php_firebird_commit_transaction(pdo_dbh_t *dbh, bool retain) /* {{{ */
835+
{
836+
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
837+
838+
/**
839+
* `retaining` keeps the transaction open without closing it.
840+
*
841+
* firebird needs to always have a transaction open to emulate autocommit mode,
842+
* and in autocommit mode it keeps the transaction open.
843+
*
844+
* Same as close and then begin again, but use retain to save overhead.
845+
*/
846+
if (retain) {
847+
if (isc_commit_retaining(H->isc_status, &H->tr)) {
848+
php_firebird_error(dbh);
849+
return false;
850+
}
851+
} else {
852+
if (isc_commit_transaction(H->isc_status, &H->tr)) {
853+
php_firebird_error(dbh);
854+
return false;
855+
}
856+
}
857+
return true;
858+
}
859+
/* }}} */
860+
812861
/* called by PDO to commit a transaction */
813-
static bool firebird_handle_commit(pdo_dbh_t *dbh) /* {{{ */
862+
static bool firebird_handle_manually_commit(pdo_dbh_t *dbh) /* {{{ */
863+
{
864+
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
865+
if (!php_firebird_commit_transaction(dbh, /*release*/ false)) {
866+
return false;
867+
}
868+
869+
/**
870+
* If in autocommit mode, begin the transaction again
871+
* Reopen instead of retain because isolation level may change
872+
*/
873+
if (dbh->auto_commit) {
874+
if (!php_firebird_begin_transaction(dbh)) {
875+
return false;
876+
}
877+
}
878+
H->in_manually_txn = 0;
879+
return true;
880+
}
881+
/* }}} */
882+
883+
/* php_firebird_rollback_transaction */
884+
static bool php_firebird_rollback_transaction(pdo_dbh_t *dbh) /* {{{ */
814885
{
815886
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
816887

817-
if (isc_commit_transaction(H->isc_status, &H->tr)) {
888+
if (isc_rollback_transaction(H->isc_status, &H->tr)) {
818889
php_firebird_error(dbh);
819890
return false;
820891
}
@@ -823,14 +894,24 @@ static bool firebird_handle_commit(pdo_dbh_t *dbh) /* {{{ */
823894
/* }}} */
824895

825896
/* called by PDO to rollback a transaction */
826-
static bool firebird_handle_rollback(pdo_dbh_t *dbh) /* {{{ */
897+
static bool firebird_handle_manually_rollback(pdo_dbh_t *dbh) /* {{{ */
827898
{
828899
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
829900

830-
if (isc_rollback_transaction(H->isc_status, &H->tr)) {
831-
php_firebird_error(dbh);
901+
if (!php_firebird_rollback_transaction(dbh)) {
832902
return false;
833903
}
904+
905+
/**
906+
* If in autocommit mode, begin the transaction again
907+
* Reopen instead of retain because isolation level may change
908+
*/
909+
if (dbh->auto_commit) {
910+
if (!php_firebird_begin_transaction(dbh)) {
911+
return false;
912+
}
913+
}
914+
H->in_manually_txn = 0;
834915
return true;
835916
}
836917
/* }}} */
@@ -848,16 +929,6 @@ static int php_firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const zend_string *sq
848929
return 0;
849930
}
850931

851-
/* start a new transaction implicitly if auto_commit is enabled and no transaction is open */
852-
if (dbh->auto_commit && !dbh->in_txn) {
853-
/* dbh->transaction_flags = PDO_TRANS_READ_UNCOMMITTED; */
854-
855-
if (!firebird_handle_begin(dbh)) {
856-
return 0;
857-
}
858-
dbh->in_txn = true;
859-
}
860-
861932
/* allocate the statement */
862933
if (isc_dsql_allocate_statement(H->isc_status, &H->db, s)) {
863934
php_firebird_error(dbh);
@@ -898,21 +969,34 @@ static bool pdo_firebird_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val
898969
return false;
899970
}
900971

972+
if (H->in_manually_txn) {
973+
/* change auto commit mode with an open transaction is illegal, because
974+
we won't know what to do with it */
975+
pdo_raise_impl_error(dbh, NULL, "HY000", "Cannot change autocommit mode while a transaction is already open");
976+
return false;
977+
}
978+
901979
/* ignore if the new value equals the old one */
902980
if (dbh->auto_commit ^ bval) {
903-
if (dbh->in_txn) {
904-
if (bval) {
905-
/* turning on auto_commit with an open transaction is illegal, because
906-
we won't know what to do with it */
907-
const char *msg = "Cannot enable auto-commit while a transaction is already open";
908-
php_firebird_error_with_info(dbh, "HY000", strlen("HY000"), msg, strlen(msg));
909-
return false;
910-
} else {
911-
/* close the transaction */
912-
if (!firebird_handle_commit(dbh)) {
913-
break;
981+
if (bval) {
982+
/* change to auto commit mode.
983+
* If the transaction is not started, start it.
984+
* However, this is a fallback since such a situation usually does not occur.
985+
*/
986+
if (!H->tr) {
987+
if (!php_firebird_begin_transaction(dbh)) {
988+
return false;
989+
}
990+
}
991+
} else {
992+
/* change to not auto commit mode.
993+
* close the transaction if exists.
994+
* However, this is a fallback since such a situation usually does not occur.
995+
*/
996+
if (H->tr) {
997+
if (!php_firebird_commit_transaction(dbh, /* release */ false)) {
998+
return false;
914999
}
915-
dbh->in_txn = false;
9161000
}
9171001
}
9181002
dbh->auto_commit = bval;
@@ -1058,22 +1142,35 @@ static void pdo_firebird_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval
10581142
}
10591143
/* }}} */
10601144

1145+
/* {{{ firebird_in_manually_transaction */
1146+
static bool pdo_firebird_in_manually_transaction(pdo_dbh_t *dbh)
1147+
{
1148+
/**
1149+
* we can tell if a transaction exists now by checking H->tr,
1150+
* but which will always be true in autocommit mode.
1151+
* So this function checks if there is currently a "manually begun transaction".
1152+
*/
1153+
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
1154+
return H->in_manually_txn;
1155+
}
1156+
/* }}} */
1157+
10611158
static const struct pdo_dbh_methods firebird_methods = { /* {{{ */
10621159
firebird_handle_closer,
10631160
firebird_handle_preparer,
10641161
firebird_handle_doer,
10651162
firebird_handle_quoter,
1066-
firebird_handle_begin,
1067-
firebird_handle_commit,
1068-
firebird_handle_rollback,
1163+
firebird_handle_manually_begin,
1164+
firebird_handle_manually_commit,
1165+
firebird_handle_manually_rollback,
10691166
pdo_firebird_set_attribute,
10701167
NULL, /* last_id not supported */
10711168
pdo_firebird_fetch_error_func,
10721169
pdo_firebird_get_attribute,
10731170
NULL, /* check_liveness */
10741171
NULL, /* get driver methods */
10751172
NULL, /* request shutdown */
1076-
NULL, /* in transaction, use PDO's internal tracking mechanism */
1173+
pdo_firebird_in_manually_transaction,
10771174
NULL /* get gc */
10781175
};
10791176
/* }}} */
@@ -1154,6 +1251,11 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /*
11541251
"HY000", H->isc_status[1], errmsg);
11551252
}
11561253

1254+
H->in_manually_txn = 0;
1255+
if (dbh->auto_commit && !H->tr) {
1256+
ret = php_firebird_begin_transaction(dbh);
1257+
}
1258+
11571259
if (!ret) {
11581260
firebird_handle_closer(dbh);
11591261
}

ext/pdo_firebird/firebird_statement.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,7 @@ static int pdo_firebird_stmt_execute(pdo_stmt_t *stmt) /* {{{ */
180180
;
181181
}
182182

183-
/* commit? */
184-
if (stmt->dbh->auto_commit && isc_commit_retaining(H->isc_status, &H->tr)) {
183+
if (stmt->dbh->auto_commit && !S->H->in_manually_txn && !php_firebird_commit_transaction(stmt->dbh, /* retain */ true)) {
185184
break;
186185
}
187186

ext/pdo_firebird/php_pdo_firebird_int.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ typedef struct {
6666
} pdo_firebird_error_info;
6767

6868
typedef struct {
69-
7069
/* the result of the last API call */
7170
ISC_STATUS isc_status[20];
7271

@@ -75,6 +74,7 @@ typedef struct {
7574

7675
/* the transaction handle */
7776
isc_tr_handle tr;
77+
bool in_manually_txn;
7878

7979
/* date and time format strings, can be set by the set_attribute method */
8080
char *date_format;
@@ -93,7 +93,6 @@ typedef struct {
9393

9494

9595
typedef struct {
96-
9796
/* the link that owns this statement */
9897
pdo_firebird_db_handle *H;
9998

@@ -122,7 +121,6 @@ typedef struct {
122121

123122
/* the output SQLDA */
124123
XSQLDA out_sqlda; /* last member */
125-
126124
} pdo_firebird_stmt;
127125

128126
extern const pdo_driver_t pdo_firebird_driver;
@@ -136,6 +134,8 @@ extern void php_firebird_set_error(pdo_dbh_t *dbh, pdo_stmt_t *stmt, const char
136134
#define php_firebird_error_with_info(d,e,el,m,ml) php_firebird_set_error(d, NULL, e, el, m, ml)
137135
#define php_firebird_error_stmt_with_info(s,e,el,m,ml) php_firebird_set_error(s->dbh, s, e, el, m, ml)
138136

137+
extern bool php_firebird_commit_transaction(pdo_dbh_t *dbh, bool retain);
138+
139139
enum {
140140
PDO_FB_ATTR_DATE_FORMAT = PDO_ATTR_DRIVER_SPECIFIC,
141141
PDO_FB_ATTR_TIME_FORMAT,

0 commit comments

Comments
 (0)