Skip to content

Commit 751c796

Browse files
committed
[pdo_firebird] Transaction management optimization
1 parent d7d3a1c commit 751c796

12 files changed

+184
-74
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: 139 additions & 45 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, bool retain);
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, /* release */ false);
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,28 +808,105 @@ static bool firebird_handle_begin(pdo_dbh_t *dbh) /* {{{ */
809808
}
810809
/* }}} */
811810

812-
/* called by PDO to commit a transaction */
813-
static bool firebird_handle_commit(pdo_dbh_t *dbh) /* {{{ */
811+
/* called by PDO to start a transaction */
812+
static bool firebird_handle_manually_begin(pdo_dbh_t *dbh) /* {{{ */
814813
{
815814
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
816815

817-
if (isc_commit_transaction(H->isc_status, &H->tr)) {
818-
php_firebird_error(dbh);
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)) {
819826
return false;
820827
}
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+
861+
/* called by PDO to commit a transaction */
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, dbh->auto_commit)) {
866+
return false;
867+
}
868+
H->in_manually_txn = 0;
869+
return true;
870+
}
871+
/* }}} */
872+
873+
/* php_firebird_rollback_transaction */
874+
static bool php_firebird_rollback_transaction(pdo_dbh_t *dbh, bool retain) /* {{{ */
875+
{
876+
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
877+
878+
/**
879+
* `retaining` keeps the transaction open without closing it.
880+
*
881+
* firebird needs to always have a transaction open to emulate autocommit mode,
882+
* and in autocommit mode it keeps the transaction open.
883+
*
884+
* Same as close and then begin again, but use retain to save overhead.
885+
*/
886+
if (retain) {
887+
if (isc_rollback_retaining(H->isc_status, &H->tr)) {
888+
php_firebird_error(dbh);
889+
return false;
890+
}
891+
} else {
892+
if (isc_rollback_transaction(H->isc_status, &H->tr)) {
893+
php_firebird_error(dbh);
894+
return false;
895+
}
896+
}
821897
return true;
822898
}
823899
/* }}} */
824900

825901
/* called by PDO to rollback a transaction */
826-
static bool firebird_handle_rollback(pdo_dbh_t *dbh) /* {{{ */
902+
static bool firebird_handle_manually_rollback(pdo_dbh_t *dbh) /* {{{ */
827903
{
828904
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
829905

830-
if (isc_rollback_transaction(H->isc_status, &H->tr)) {
831-
php_firebird_error(dbh);
906+
if (!php_firebird_rollback_transaction(dbh, dbh->auto_commit)) {
832907
return false;
833908
}
909+
H->in_manually_txn = 0;
834910
return true;
835911
}
836912
/* }}} */
@@ -848,16 +924,6 @@ static int php_firebird_alloc_prepare_stmt(pdo_dbh_t *dbh, const zend_string *sq
848924
return 0;
849925
}
850926

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-
861927
/* allocate the statement */
862928
if (isc_dsql_allocate_statement(H->isc_status, &H->db, s)) {
863929
php_firebird_error(dbh);
@@ -898,21 +964,31 @@ static bool pdo_firebird_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val
898964
return false;
899965
}
900966

967+
if (H->in_manually_txn) {
968+
/* change auto commit mode with an open transaction is illegal, because
969+
we won't know what to do with it */
970+
const char *msg = "Cannot change autocommit mode while a transaction is already open";
971+
php_firebird_error_with_info(dbh, "HY000", strlen("HY000"), msg, strlen(msg));
972+
return false;
973+
}
974+
901975
/* ignore if the new value equals the old one */
902976
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;
977+
if (bval) {
978+
/* change to auto commit mode */
979+
/* If the transaction is not started, start it. */
980+
if (!H->tr) {
981+
if (!php_firebird_begin_transaction(dbh)) {
982+
return false;
983+
}
984+
}
985+
} else {
986+
/* change to not auto commit mode */
987+
/* close the transaction if exists */
988+
if (H->tr) {
989+
if (!php_firebird_commit_transaction(dbh, /* release */ false)) {
990+
return false;
914991
}
915-
dbh->in_txn = false;
916992
}
917993
}
918994
dbh->auto_commit = bval;
@@ -1058,22 +1134,35 @@ static void pdo_firebird_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval
10581134
}
10591135
/* }}} */
10601136

1137+
/* {{{ firebird_in_manually_transaction */
1138+
static bool pdo_firebird_in_manually_transaction(pdo_dbh_t *dbh)
1139+
{
1140+
/**
1141+
* we can tell if a transaction exists now by checking H->tr,
1142+
* but which will always be true in autocommit mode.
1143+
* So this function checks if there is currently a "manually begun transaction".
1144+
*/
1145+
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
1146+
return H->in_manually_txn;
1147+
}
1148+
/* }}} */
1149+
10611150
static const struct pdo_dbh_methods firebird_methods = { /* {{{ */
10621151
firebird_handle_closer,
10631152
firebird_handle_preparer,
10641153
firebird_handle_doer,
10651154
firebird_handle_quoter,
1066-
firebird_handle_begin,
1067-
firebird_handle_commit,
1068-
firebird_handle_rollback,
1155+
firebird_handle_manually_begin,
1156+
firebird_handle_manually_commit,
1157+
firebird_handle_manually_rollback,
10691158
pdo_firebird_set_attribute,
10701159
NULL, /* last_id not supported */
10711160
pdo_firebird_fetch_error_func,
10721161
pdo_firebird_get_attribute,
10731162
NULL, /* check_liveness */
10741163
NULL, /* get driver methods */
10751164
NULL, /* request shutdown */
1076-
NULL, /* in transaction, use PDO's internal tracking mechanism */
1165+
pdo_firebird_in_manually_transaction,
10771166
NULL /* get gc */
10781167
};
10791168
/* }}} */
@@ -1154,6 +1243,11 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /*
11541243
"HY000", H->isc_status[1], errmsg);
11551244
}
11561245

1246+
H->in_manually_txn = 0;
1247+
if (dbh->auto_commit && !H->tr) {
1248+
ret = php_firebird_begin_transaction(dbh);
1249+
}
1250+
11571251
if (!ret) {
11581252
firebird_handle_closer(dbh);
11591253
}

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,

ext/pdo_firebird/tests/bug_47415.phpt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,6 @@ $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
1616
$dbh->exec('CREATE TABLE test47415 (idx int NOT NULL PRIMARY KEY, txt VARCHAR(20))');
1717
$dbh->exec('INSERT INTO test47415 VALUES(0, \'String0\')');
1818

19-
$dbh->commit();
20-
2119
$query = "SELECT idx, txt FROM test47415 ORDER by idx";
2220
$idx = $txt = 0;
2321
$stmt = $dbh->prepare($query);
@@ -32,8 +30,6 @@ var_dump($stmt->rowCount());
3230
$stmt = $dbh->prepare('DELETE FROM test47415');
3331
$stmt->execute();
3432

35-
$dbh->commit();
36-
3733
unset($stmt);
3834
unset($dbh);
3935
?>

ext/pdo_firebird/tests/bug_48877.phpt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ $dbh->exec('CREATE TABLE test48877 (A integer)');
1818
$dbh->exec("INSERT INTO test48877 VALUES ('1')");
1919
$dbh->exec("INSERT INTO test48877 VALUES ('2')");
2020
$dbh->exec("INSERT INTO test48877 VALUES ('3')");
21-
$dbh->commit();
2221

2322
$query = "SELECT * FROM test48877 WHERE A = :paramno";
2423

@@ -33,7 +32,6 @@ var_dump($stmt->rowCount());
3332
$stmt = $dbh->prepare('DELETE FROM test48877');
3433
$stmt->execute();
3534

36-
$dbh->commit();
3735
unset($stmt);
3836
unset($dbh);
3937

ext/pdo_firebird/tests/bug_53280.phpt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ require("testdb.inc");
1414

1515
$dbh->exec('CREATE TABLE test53280(A VARCHAR(30), B VARCHAR(30), C VARCHAR(30))');
1616
$dbh->exec("INSERT INTO test53280 VALUES ('A', 'B', 'C')");
17-
$dbh->commit();
1817

1918
$stmt1 = "SELECT B FROM test53280 WHERE A = ? AND B = ?";
2019
$stmt2 = "SELECT B, C FROM test53280 WHERE A = ? AND B = ?";
@@ -29,7 +28,6 @@ $stmth1->execute(array('A', 'B'));
2928
$rows = $stmth1->fetchAll(); // <------- segfault
3029
var_dump($rows);
3130

32-
$dbh->commit();
3331
unset($stmth1);
3432
unset($stmth2);
3533
unset($stmt);

0 commit comments

Comments
 (0)