@@ -756,51 +756,56 @@ static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *un
756
756
/* }}} */
757
757
758
758
/* 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 ) /* {{{ */
760
760
{
761
761
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
- }
782
762
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 ;
790
798
}
791
799
}
792
800
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 ;
801
806
}
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 )) {
804
809
php_firebird_error (dbh );
805
810
return false;
806
811
}
@@ -822,7 +827,7 @@ static bool firebird_handle_manually_begin(pdo_dbh_t *dbh) /* {{{ */
822
827
}
823
828
}
824
829
825
- if (!php_firebird_begin_transaction (dbh )) {
830
+ if (!php_firebird_begin_transaction (dbh , /* manually */ false )) {
826
831
return false;
827
832
}
828
833
H -> in_manually_txn = 1 ;
@@ -871,7 +876,7 @@ static bool firebird_handle_manually_commit(pdo_dbh_t *dbh) /* {{{ */
871
876
* Reopen instead of retain because isolation level may change
872
877
*/
873
878
if (dbh -> auto_commit ) {
874
- if (!php_firebird_begin_transaction (dbh )) {
879
+ if (!php_firebird_begin_transaction (dbh , /* auto commit mode */ true )) {
875
880
return false;
876
881
}
877
882
}
@@ -907,7 +912,7 @@ static bool firebird_handle_manually_rollback(pdo_dbh_t *dbh) /* {{{ */
907
912
* Reopen instead of retain because isolation level may change
908
913
*/
909
914
if (dbh -> auto_commit ) {
910
- if (!php_firebird_begin_transaction (dbh )) {
915
+ if (!php_firebird_begin_transaction (dbh , /* auto commit mode */ true )) {
911
916
return false;
912
917
}
913
918
}
@@ -961,6 +966,7 @@ static bool pdo_firebird_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val
961
966
{
962
967
pdo_firebird_db_handle * H = (pdo_firebird_db_handle * )dbh -> driver_data ;
963
968
bool bval ;
969
+ zend_long lval ;
964
970
965
971
switch (attr ) {
966
972
case PDO_ATTR_AUTOCOMMIT :
@@ -979,19 +985,19 @@ static bool pdo_firebird_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val
979
985
/* ignore if the new value equals the old one */
980
986
if (dbh -> auto_commit ^ bval ) {
981
987
if (bval ) {
982
- /* change to auto commit mode.
988
+ /*
989
+ * change to auto commit mode.
983
990
* If the transaction is not started, start it.
984
- * However, this is a fallback since such a situation usually does not occur.
985
991
*/
986
992
if (!H -> tr ) {
987
- if (!php_firebird_begin_transaction (dbh )) {
993
+ if (!php_firebird_begin_transaction (dbh , /* auto commit mode */ true )) {
988
994
return false;
989
995
}
990
996
}
991
997
} else {
992
- /* change to not auto commit mode.
998
+ /*
999
+ * change to not auto commit mode.
993
1000
* close the transaction if exists.
994
- * However, this is a fallback since such a situation usually does not occur.
995
1001
*/
996
1002
if (H -> tr ) {
997
1003
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
1052
1058
zend_string_release_ex (str , 0 );
1053
1059
}
1054
1060
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;
1055
1124
}
1056
1125
return false;
1057
1126
}
@@ -1124,6 +1193,14 @@ static int pdo_firebird_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *val)
1124
1193
case PDO_ATTR_FETCH_TABLE_NAMES :
1125
1194
ZVAL_BOOL (val , H -> fetch_table_names );
1126
1195
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 ;
1127
1204
}
1128
1205
return 0 ;
1129
1206
}
@@ -1201,6 +1278,18 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /*
1201
1278
dbh -> password = pestrdup (vars [5 ].optval , dbh -> is_persistent );
1202
1279
}
1203
1280
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
+
1204
1293
do {
1205
1294
static char const dpb_flags [] = {
1206
1295
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) /*
1251
1340
"HY000" , H -> isc_status [1 ], errmsg );
1252
1341
}
1253
1342
1254
- H -> in_manually_txn = 0 ;
1255
1343
if (dbh -> auto_commit && !H -> tr ) {
1256
- ret = php_firebird_begin_transaction (dbh );
1344
+ ret = php_firebird_begin_transaction (dbh , /* auto commit mode */ true );
1257
1345
}
1258
1346
1259
1347
if (!ret ) {
0 commit comments