Skip to content

Commit 08e1202

Browse files
committed
Added transaction isolation level and access mode
1 parent dfaf798 commit 08e1202

6 files changed

+583
-48
lines changed

ext/pdo_firebird/firebird_driver.c

Lines changed: 136 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -756,51 +756,56 @@ static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *un
756756
/* }}} */
757757

758758
/* php_firebird_begin_transaction */
759-
static bool php_firebird_begin_transaction(pdo_dbh_t *dbh) /* {{{ */
759+
static bool php_firebird_begin_transaction(pdo_dbh_t *dbh, bool is_auto_commit_txn) /* {{{ */
760760
{
761761
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
762-
char tpb[8] = { isc_tpb_version3 }, *ptpb = tpb+1;
763-
#ifdef abies_0
764-
if (dbh->transaction_flags & PDO_TRANS_ISOLATION_LEVEL) {
765-
if (dbh->transaction_flags & PDO_TRANS_READ_UNCOMMITTED) {
766-
/* this is a poor fit, but it's all we have */
767-
*ptpb++ = isc_tpb_read_committed;
768-
*ptpb++ = isc_tpb_rec_version;
769-
dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_READ_UNCOMMITTED);
770-
} else if (dbh->transaction_flags & PDO_TRANS_READ_COMMITTED) {
771-
*ptpb++ = isc_tpb_read_committed;
772-
*ptpb++ = isc_tpb_no_rec_version;
773-
dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_READ_COMMITTED);
774-
} else if (dbh->transaction_flags & PDO_TRANS_REPEATABLE_READ) {
775-
*ptpb++ = isc_tpb_concurrency;
776-
dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_REPEATABLE_READ);
777-
} else {
778-
*ptpb++ = isc_tpb_consistency;
779-
dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_SERIALIZABLE);
780-
}
781-
}
782762

783-
if (dbh->transaction_flags & PDO_TRANS_ACCESS_MODE) {
784-
if (dbh->transaction_flags & PDO_TRANS_READONLY) {
785-
*ptpb++ = isc_tpb_read;
786-
dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READONLY);
787-
} else {
788-
*ptpb++ = isc_tpb_write;
789-
dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READWRITE);
763+
/* isc_xxx are all 1 byte. */
764+
char tpb[5] = { isc_tpb_version3 }, *ptpb = tpb + 1;
765+
766+
if (is_auto_commit_txn) {
767+
/*
768+
* In autocommit mode, we need to always read the latest information, that is,
769+
* expect phantom reads, so we set read committed.
770+
*/
771+
*ptpb++ = isc_tpb_read_committed;
772+
*ptpb++ = isc_tpb_rec_version;
773+
} else {
774+
switch (H->txn_isolation_level) {
775+
/*
776+
* firebird's read committed has the option to wait until other transactions
777+
* commit or rollback if there is indeterminate data.
778+
* Introducing too many configuration values ​​at once can cause confusion, so
779+
* we don't support in PDO that feature yet.
780+
*
781+
* Also, there is information that depending on the settings, it is possible to
782+
* reproduce behavior like read uncommited, but at least with the current firebird
783+
* API, this is not possible.
784+
*/
785+
case PDO_FB_READ_COMMITTED:
786+
*ptpb++ = isc_tpb_read_committed;
787+
*ptpb++ = isc_tpb_rec_version;
788+
break;
789+
790+
case PDO_FB_SERIALIZABLE:
791+
*ptpb++ = isc_tpb_consistency;
792+
break;
793+
794+
case PDO_FB_REPEATABLE_READ:
795+
default:
796+
*ptpb++ = isc_tpb_concurrency;
797+
break;
790798
}
791799
}
792800

793-
if (dbh->transaction_flags & PDO_TRANS_CONFLICT_RESOLUTION) {
794-
if (dbh->transaction_flags & PDO_TRANS_RETRY) {
795-
*ptpb++ = isc_tpb_wait;
796-
dbh->transaction_flags &= ~(PDO_TRANS_CONFLICT_RESOLUTION^PDO_TRANS_RETRY);
797-
} else {
798-
*ptpb++ = isc_tpb_nowait;
799-
dbh->transaction_flags &= ~(PDO_TRANS_CONFLICT_RESOLUTION^PDO_TRANS_ABORT);
800-
}
801+
802+
if (H->is_writable_txn) {
803+
*ptpb++ = isc_tpb_write;
804+
} else {
805+
*ptpb++ = isc_tpb_read;
801806
}
802-
#endif
803-
if (isc_start_transaction(H->isc_status, &H->tr, 1, &H->db, (unsigned short)(ptpb-tpb), tpb)) {
807+
808+
if (isc_start_transaction(H->isc_status, &H->tr, 1, &H->db, (unsigned short)(ptpb - tpb), tpb)) {
804809
php_firebird_error(dbh);
805810
return false;
806811
}
@@ -822,7 +827,7 @@ static bool firebird_handle_manually_begin(pdo_dbh_t *dbh) /* {{{ */
822827
}
823828
}
824829

825-
if (!php_firebird_begin_transaction(dbh)) {
830+
if (!php_firebird_begin_transaction(dbh, /* manually */ false)) {
826831
return false;
827832
}
828833
H->in_manually_txn = 1;
@@ -871,7 +876,7 @@ static bool firebird_handle_manually_commit(pdo_dbh_t *dbh) /* {{{ */
871876
* Reopen instead of retain because isolation level may change
872877
*/
873878
if (dbh->auto_commit) {
874-
if (!php_firebird_begin_transaction(dbh)) {
879+
if (!php_firebird_begin_transaction(dbh, /* auto commit mode */ true)) {
875880
return false;
876881
}
877882
}
@@ -907,7 +912,7 @@ static bool firebird_handle_manually_rollback(pdo_dbh_t *dbh) /* {{{ */
907912
* Reopen instead of retain because isolation level may change
908913
*/
909914
if (dbh->auto_commit) {
910-
if (!php_firebird_begin_transaction(dbh)) {
915+
if (!php_firebird_begin_transaction(dbh, /* auto commit mode */ true)) {
911916
return false;
912917
}
913918
}
@@ -961,6 +966,7 @@ static bool pdo_firebird_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val
961966
{
962967
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
963968
bool bval;
969+
zend_long lval;
964970

965971
switch (attr) {
966972
case PDO_ATTR_AUTOCOMMIT:
@@ -979,19 +985,19 @@ static bool pdo_firebird_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val
979985
/* ignore if the new value equals the old one */
980986
if (dbh->auto_commit ^ bval) {
981987
if (bval) {
982-
/* change to auto commit mode.
988+
/*
989+
* change to auto commit mode.
983990
* If the transaction is not started, start it.
984-
* However, this is a fallback since such a situation usually does not occur.
985991
*/
986992
if (!H->tr) {
987-
if (!php_firebird_begin_transaction(dbh)) {
993+
if (!php_firebird_begin_transaction(dbh, /* auto commit mode */ true)) {
988994
return false;
989995
}
990996
}
991997
} else {
992-
/* change to not auto commit mode.
998+
/*
999+
* change to not auto commit mode.
9931000
* close the transaction if exists.
994-
* However, this is a fallback since such a situation usually does not occur.
9951001
*/
9961002
if (H->tr) {
9971003
if (!php_firebird_commit_transaction(dbh, /* release */ false)) {
@@ -1052,6 +1058,69 @@ static bool pdo_firebird_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val
10521058
zend_string_release_ex(str, 0);
10531059
}
10541060
return true;
1061+
1062+
case PDO_FB_TRANSACTION_ISOLATION_LEVEL:
1063+
{
1064+
if (!pdo_get_long_param(&lval, val)) {
1065+
return false;
1066+
}
1067+
1068+
if (H->in_manually_txn) {
1069+
pdo_raise_impl_error(dbh, NULL, "HY000", "Cannot change transaction isolation level while a transaction is already open");
1070+
return false;
1071+
}
1072+
1073+
/* ignore if the new value equals the old one */
1074+
if (H->txn_isolation_level != lval) {
1075+
if (lval == PDO_FB_READ_COMMITTED ||
1076+
lval == PDO_FB_REPEATABLE_READ ||
1077+
lval == PDO_FB_SERIALIZABLE
1078+
) {
1079+
/*
1080+
* Autocommit mode is always read-committed, so this setting is used the next time
1081+
* a manual transaction starts. Therefore, there is no need to immediately reopen the transaction.
1082+
*/
1083+
H->txn_isolation_level = lval;
1084+
} else {
1085+
pdo_raise_impl_error(dbh, NULL, "HY000",
1086+
"Transaction isolation level must be PDO::FB_READ_COMMITTED, PDO::FB_REPEATABLE_READ, or PDO::PDO_FB_SERIALIZABLE");
1087+
return false;
1088+
}
1089+
}
1090+
}
1091+
return true;
1092+
1093+
case PDO_FB_WRITABLE_TRANSACTION:
1094+
{
1095+
if (!pdo_get_bool_param(&bval, val)) {
1096+
return false;
1097+
}
1098+
1099+
if (H->in_manually_txn) {
1100+
pdo_raise_impl_error(dbh, NULL, "HY000", "Cannot change access mode while a transaction is already open");
1101+
return false;
1102+
}
1103+
1104+
/* ignore if the new value equals the old one */
1105+
if (H->is_writable_txn != bval) {
1106+
H->is_writable_txn = bval;
1107+
if (dbh->auto_commit) {
1108+
if (H->tr) {
1109+
if (!php_firebird_commit_transaction(dbh, /* release */ false)) {
1110+
/* In case of error, revert the setting */
1111+
H->is_writable_txn = !bval;
1112+
return false;
1113+
}
1114+
}
1115+
if (!php_firebird_begin_transaction(dbh, /* auto commit mode */ true)) {
1116+
/* In case of error, revert the setting */
1117+
H->is_writable_txn = !bval;
1118+
return false;
1119+
}
1120+
}
1121+
}
1122+
}
1123+
return true;
10551124
}
10561125
return false;
10571126
}
@@ -1124,6 +1193,14 @@ static int pdo_firebird_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val)
11241193
case PDO_ATTR_FETCH_TABLE_NAMES:
11251194
ZVAL_BOOL(val, H->fetch_table_names);
11261195
return 1;
1196+
1197+
case PDO_FB_TRANSACTION_ISOLATION_LEVEL:
1198+
ZVAL_LONG(val, H->txn_isolation_level);
1199+
return 1;
1200+
1201+
case PDO_FB_WRITABLE_TRANSACTION:
1202+
ZVAL_BOOL(val, H->is_writable_txn);
1203+
return 1;
11271204
}
11281205
return 0;
11291206
}
@@ -1201,6 +1278,18 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /*
12011278
dbh->password = pestrdup(vars[5].optval, dbh->is_persistent);
12021279
}
12031280

1281+
H->in_manually_txn = 0;
1282+
H->is_writable_txn = pdo_attr_lval(driver_options, PDO_FB_WRITABLE_TRANSACTION, 1);
1283+
zend_long txn_isolation_level = pdo_attr_lval(driver_options, PDO_FB_TRANSACTION_ISOLATION_LEVEL, PDO_FB_REPEATABLE_READ);
1284+
if (txn_isolation_level == PDO_FB_READ_COMMITTED ||
1285+
txn_isolation_level == PDO_FB_REPEATABLE_READ ||
1286+
txn_isolation_level == PDO_FB_SERIALIZABLE
1287+
) {
1288+
H->txn_isolation_level = txn_isolation_level;
1289+
} else {
1290+
H->txn_isolation_level = PDO_FB_REPEATABLE_READ;
1291+
}
1292+
12041293
do {
12051294
static char const dpb_flags[] = {
12061295
isc_dpb_user_name, isc_dpb_password, isc_dpb_lc_ctype, isc_dpb_sql_role_name };
@@ -1251,9 +1340,8 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /*
12511340
"HY000", H->isc_status[1], errmsg);
12521341
}
12531342

1254-
H->in_manually_txn = 0;
12551343
if (dbh->auto_commit && !H->tr) {
1256-
ret = php_firebird_begin_transaction(dbh);
1344+
ret = php_firebird_begin_transaction(dbh, /* auto commit mode */ true);
12571345
}
12581346

12591347
if (!ret) {

ext/pdo_firebird/pdo_firebird.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ PHP_MINIT_FUNCTION(pdo_firebird) /* {{{ */
5757
REGISTER_PDO_CLASS_CONST_LONG("FB_ATTR_DATE_FORMAT", (zend_long) PDO_FB_ATTR_DATE_FORMAT);
5858
REGISTER_PDO_CLASS_CONST_LONG("FB_ATTR_TIME_FORMAT", (zend_long) PDO_FB_ATTR_TIME_FORMAT);
5959
REGISTER_PDO_CLASS_CONST_LONG("FB_ATTR_TIMESTAMP_FORMAT", (zend_long) PDO_FB_ATTR_TIMESTAMP_FORMAT);
60+
REGISTER_PDO_CLASS_CONST_LONG("FB_TRANSACTION_ISOLATION_LEVEL", (zend_long) PDO_FB_TRANSACTION_ISOLATION_LEVEL);
61+
REGISTER_PDO_CLASS_CONST_LONG("FB_READ_COMMITTED", (zend_long) PDO_FB_READ_COMMITTED);
62+
REGISTER_PDO_CLASS_CONST_LONG("FB_REPEATABLE_READ", (zend_long) PDO_FB_REPEATABLE_READ);
63+
REGISTER_PDO_CLASS_CONST_LONG("FB_SERIALIZABLE", (zend_long) PDO_FB_SERIALIZABLE);
64+
REGISTER_PDO_CLASS_CONST_LONG("FB_WRITABLE_TRANSACTION", (zend_long) PDO_FB_WRITABLE_TRANSACTION);
6065

6166
if (FAILURE == php_pdo_register_driver(&pdo_firebird_driver)) {
6267
return FAILURE;

ext/pdo_firebird/php_pdo_firebird_int.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ typedef struct {
7575
/* the transaction handle */
7676
isc_tr_handle tr;
7777
bool in_manually_txn;
78+
bool is_writable_txn;
79+
zend_ulong txn_isolation_level;
7880

7981
/* date and time format strings, can be set by the set_attribute method */
8082
char *date_format;
@@ -140,6 +142,18 @@ enum {
140142
PDO_FB_ATTR_DATE_FORMAT = PDO_ATTR_DRIVER_SPECIFIC,
141143
PDO_FB_ATTR_TIME_FORMAT,
142144
PDO_FB_ATTR_TIMESTAMP_FORMAT,
145+
146+
/*
147+
* transaction isolation level
148+
* firebird does not have a level equivalent to read uncommited.
149+
*/
150+
PDO_FB_TRANSACTION_ISOLATION_LEVEL,
151+
PDO_FB_READ_COMMITTED,
152+
PDO_FB_REPEATABLE_READ,
153+
PDO_FB_SERIALIZABLE,
154+
155+
/* transaction access mode */
156+
PDO_FB_WRITABLE_TRANSACTION,
143157
};
144158

145159
#endif /* PHP_PDO_FIREBIRD_INT_H */

0 commit comments

Comments
 (0)