Skip to content

Commit 9d75ad9

Browse files
committed
Added transaction isolation level and access mode
1 parent f0ca8ff commit 9d75ad9

7 files changed

+421
-39
lines changed

ext/pdo_firebird/firebird_driver.c

Lines changed: 148 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,13 @@
2828
#include "ext/standard/info.h"
2929
#include "pdo/php_pdo.h"
3030
#include "pdo/php_pdo_driver.h"
31+
#include "pdo/php_pdo_error.h"
3132
#include "php_pdo_firebird.h"
3233
#include "php_pdo_firebird_int.h"
34+
#include "firebird_driver_arginfo.h"
3335

3436
static int firebird_alloc_prepare_stmt(pdo_dbh_t*, const zend_string*, XSQLDA*, isc_stmt_handle*, HashTable*);
37+
static bool firebird_change_transaction_access_mode(pdo_dbh_t *dbh, bool writable);
3538

3639
const char CHR_LETTER = 1;
3740
const char CHR_DIGIT = 2;
@@ -700,48 +703,31 @@ static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *un
700703
static bool _firebird_begin_transaction(pdo_dbh_t *dbh) /* {{{ */
701704
{
702705
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
703-
char tpb[8] = { isc_tpb_version3 }, *ptpb = tpb+1;
704-
#ifdef abies_0
705-
if (dbh->transaction_flags & PDO_TRANS_ISOLATION_LEVEL) {
706-
if (dbh->transaction_flags & PDO_TRANS_READ_UNCOMMITTED) {
707-
/* this is a poor fit, but it's all we have */
706+
char tpb[5] = { isc_tpb_version3 }, *ptpb = tpb + strlen(tpb);
707+
708+
switch (H->txn_isolation_level) {
709+
case PDO_FB_READ_COMMITTED:
708710
*ptpb++ = isc_tpb_read_committed;
709711
*ptpb++ = isc_tpb_rec_version;
710-
dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_READ_UNCOMMITTED);
711-
} else if (dbh->transaction_flags & PDO_TRANS_READ_COMMITTED) {
712-
*ptpb++ = isc_tpb_read_committed;
713-
*ptpb++ = isc_tpb_no_rec_version;
714-
dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_READ_COMMITTED);
715-
} else if (dbh->transaction_flags & PDO_TRANS_REPEATABLE_READ) {
716-
*ptpb++ = isc_tpb_concurrency;
717-
dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_REPEATABLE_READ);
718-
} else {
712+
break;
713+
714+
case PDO_FB_SERIALIZABLE:
719715
*ptpb++ = isc_tpb_consistency;
720-
dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_SERIALIZABLE);
721-
}
722-
}
716+
break;
723717

724-
if (dbh->transaction_flags & PDO_TRANS_ACCESS_MODE) {
725-
if (dbh->transaction_flags & PDO_TRANS_READONLY) {
726-
*ptpb++ = isc_tpb_read;
727-
dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READONLY);
728-
} else {
729-
*ptpb++ = isc_tpb_write;
730-
dbh->transaction_flags &= ~(PDO_TRANS_ACCESS_MODE^PDO_TRANS_READWRITE);
731-
}
718+
case PDO_FB_REPEATABLE_READ:
719+
default:
720+
*ptpb++ = isc_tpb_concurrency;
721+
break;
732722
}
733723

734-
if (dbh->transaction_flags & PDO_TRANS_CONFLICT_RESOLUTION) {
735-
if (dbh->transaction_flags & PDO_TRANS_RETRY) {
736-
*ptpb++ = isc_tpb_wait;
737-
dbh->transaction_flags &= ~(PDO_TRANS_CONFLICT_RESOLUTION^PDO_TRANS_RETRY);
738-
} else {
739-
*ptpb++ = isc_tpb_nowait;
740-
dbh->transaction_flags &= ~(PDO_TRANS_CONFLICT_RESOLUTION^PDO_TRANS_ABORT);
741-
}
724+
if (H->is_writable_txn) {
725+
*ptpb++ = isc_tpb_write;
726+
} else {
727+
*ptpb++ = isc_tpb_read;
742728
}
743-
#endif
744-
if (isc_start_transaction(H->isc_status, &H->tr, 1, &H->db, (unsigned short)(ptpb-tpb), tpb)) {
729+
730+
if (isc_start_transaction(H->isc_status, &H->tr, 1, &H->db, (unsigned short)(ptpb - tpb), tpb)) {
745731
RECORD_ERROR(dbh);
746732
return false;
747733
}
@@ -882,6 +868,7 @@ static bool firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *
882868
{
883869
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
884870
bool bval;
871+
zend_long lval;
885872

886873
switch (attr) {
887874
case PDO_ATTR_AUTOCOMMIT:
@@ -962,6 +949,44 @@ static bool firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *
962949
zend_string_release_ex(str, 0);
963950
}
964951
return true;
952+
953+
case PDO_FB_TRANSACTION_ISOLATION_LEVEL:
954+
{
955+
if (!pdo_get_long_param(&lval, val)) {
956+
return false;
957+
}
958+
if (H->txn_isolation_level != lval) {
959+
if (lval == PDO_FB_READ_COMMITTED ||
960+
lval == PDO_FB_REPEATABLE_READ ||
961+
lval == PDO_FB_SERIALIZABLE
962+
) {
963+
if (H->tr && H->in_manually_txn) {
964+
H->last_app_error = "Cannot change isolation level while a transaction is already open";
965+
return false;
966+
}
967+
H->txn_isolation_level = lval;
968+
if (dbh->auto_commit) {
969+
if (H->tr && !firebird_commit_transaction(dbh, false)) {
970+
return false;
971+
}
972+
if (!_firebird_begin_transaction(dbh)) {
973+
return false;
974+
}
975+
}
976+
} else {
977+
return false;
978+
}
979+
}
980+
}
981+
return true;
982+
983+
case PDO_FB_WRITABLE_TRANSACTION:
984+
{
985+
if (!pdo_get_bool_param(&bval, val)) {
986+
return false;
987+
}
988+
return firebird_change_transaction_access_mode(dbh, bval);
989+
}
965990
}
966991
return false;
967992
}
@@ -1034,6 +1059,14 @@ static int firebird_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *v
10341059
case PDO_ATTR_FETCH_TABLE_NAMES:
10351060
ZVAL_BOOL(val, H->fetch_table_names);
10361061
return 1;
1062+
1063+
case PDO_FB_TRANSACTION_ISOLATION_LEVEL:
1064+
ZVAL_LONG(val, H->txn_isolation_level);
1065+
return 1;
1066+
1067+
case PDO_FB_WRITABLE_TRANSACTION:
1068+
ZVAL_BOOL(val, H->is_writable_txn);
1069+
return 1;
10371070
}
10381071
return 0;
10391072
}
@@ -1062,6 +1095,70 @@ static void pdo_firebird_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval
10621095
}
10631096
/* }}} */
10641097

1098+
/* change transaction access mode. writable or readonly */
1099+
static bool firebird_change_transaction_access_mode(pdo_dbh_t *dbh, bool writable) /* {{{ */
1100+
{
1101+
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
1102+
1103+
if (H->is_writable_txn == writable) {
1104+
return true;
1105+
}
1106+
1107+
if (H->tr && H->in_manually_txn) {
1108+
H->last_app_error = "Cannot change access mode while a transaction is already open";
1109+
return false;
1110+
}
1111+
H->is_writable_txn = writable;
1112+
if (dbh->auto_commit) {
1113+
if (H->tr && !firebird_commit_transaction(dbh, false)) {
1114+
return false;
1115+
}
1116+
if (!_firebird_begin_transaction(dbh)) {
1117+
return false;
1118+
}
1119+
}
1120+
1121+
return true;
1122+
}
1123+
1124+
/* fbWritable or fbReadonly */
1125+
static void firebird_fb_access_mode(INTERNAL_FUNCTION_PARAMETERS, bool writable) /* {{{ */
1126+
{
1127+
pdo_dbh_t *dbh;
1128+
1129+
ZEND_PARSE_PARAMETERS_NONE();
1130+
1131+
dbh = Z_PDO_DBH_P(ZEND_THIS);
1132+
PDO_CONSTRUCT_CHECK;
1133+
1134+
RETURN_BOOL(firebird_change_transaction_access_mode(dbh, writable));
1135+
}
1136+
/* }}} */
1137+
1138+
/* {{{ change transaction mode to writable */
1139+
PHP_METHOD(PDO_Firebird_Ext, fbWritable)
1140+
{
1141+
firebird_fb_access_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
1142+
}
1143+
/* }}} */
1144+
1145+
/* {{{ change transaction mode to readonly */
1146+
PHP_METHOD(PDO_Firebird_Ext, fbReadonly)
1147+
{
1148+
firebird_fb_access_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
1149+
}
1150+
/* }}} */
1151+
1152+
static const zend_function_entry *pdo_firebird_get_driver_methods(pdo_dbh_t *dbh, int kind)
1153+
{
1154+
switch (kind) {
1155+
case PDO_DBH_DRIVER_METHOD_KIND_DBH:
1156+
return class_PDO_Firebird_Ext_methods;
1157+
default:
1158+
return NULL;
1159+
}
1160+
}
1161+
10651162
/* {{{ firebird_in_manually_transaction */
10661163
static bool firebird_in_manually_transaction(pdo_dbh_t *dbh)
10671164
{
@@ -1083,7 +1180,7 @@ static const struct pdo_dbh_methods firebird_methods = { /* {{{ */
10831180
pdo_firebird_fetch_error_func,
10841181
firebird_handle_get_attribute,
10851182
NULL, /* check_liveness */
1086-
NULL, /* get driver methods */
1183+
pdo_firebird_get_driver_methods,
10871184
NULL, /* request shutdown */
10881185
firebird_in_manually_transaction,
10891186
NULL /* get gc */
@@ -1116,6 +1213,21 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /*
11161213
dbh->password = pestrdup(vars[5].optval, dbh->is_persistent);
11171214
}
11181215

1216+
H->in_manually_txn = 0;
1217+
H->is_writable_txn = 1;
1218+
if (driver_options) {
1219+
H->is_writable_txn = pdo_attr_lval(driver_options, PDO_FB_WRITABLE_TRANSACTION, 1);
1220+
zend_long txn_isolation_level = pdo_attr_lval(driver_options, PDO_FB_TRANSACTION_ISOLATION_LEVEL, PDO_FB_REPEATABLE_READ);
1221+
if (txn_isolation_level == PDO_FB_READ_COMMITTED ||
1222+
txn_isolation_level == PDO_FB_REPEATABLE_READ ||
1223+
txn_isolation_level == PDO_FB_SERIALIZABLE
1224+
) {
1225+
H->txn_isolation_level = txn_isolation_level;
1226+
} else {
1227+
H->txn_isolation_level = PDO_FB_REPEATABLE_READ;
1228+
}
1229+
}
1230+
11191231
do {
11201232
static char const dpb_flags[] = {
11211233
isc_dpb_user_name, isc_dpb_password, isc_dpb_lc_ctype, isc_dpb_sql_role_name };
@@ -1166,7 +1278,6 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /*
11661278
"HY000", H->isc_status[1], errmsg);
11671279
}
11681280

1169-
H->in_manually_txn = 0;
11701281
if (dbh->auto_commit && !H->tr) {
11711282
ret = _firebird_begin_transaction(dbh);
11721283
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
/** @generate-function-entries */
4+
5+
/**
6+
* These are extension methods for PDO. This is not a real class.
7+
* @undocumentable
8+
*/
9+
class PDO_Firebird_Ext {
10+
/** @tentative-return-type */
11+
public function fbWritable(): bool {}
12+
13+
/** @tentative-return-type */
14+
public function fbReadonly(): bool {}
15+
}

ext/pdo_firebird/firebird_driver_arginfo.h

Lines changed: 18 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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 & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,10 @@ typedef void (*info_func_t)(char*);
6060
#endif
6161

6262
extern bool _firebird_commit_transaction(pdo_dbh_t *dbh, bool retain);
63-
#define firebird_commit_transaction(d, r) _firebird_commit_transaction(d, r)
63+
#define firebird_commit_transaction(d,r) _firebird_commit_transaction(d, r)
6464

6565
extern bool _firebird_rollback_transaction(pdo_dbh_t *dbh, bool retain);
66-
#define firebird_rollback_transaction(d, r) _firebird_rollback_transaction(d, r)
66+
#define firebird_rollback_transaction(d,r) _firebird_rollback_transaction(d, r)
6767

6868
typedef struct {
6969

@@ -76,6 +76,7 @@ typedef struct {
7676
/* the transaction handle */
7777
isc_tr_handle tr;
7878
bool in_manually_txn:1;
79+
bool is_writable_txn:1;
7980

8081
/* the last error that didn't come from the API */
8182
char const *last_app_error;
@@ -92,6 +93,8 @@ typedef struct {
9293

9394
unsigned _reserved:29;
9495

96+
/* transaction isolation level */
97+
zend_ulong txn_isolation_level;
9598
} pdo_firebird_db_handle;
9699

97100

@@ -138,6 +141,15 @@ enum {
138141
PDO_FB_ATTR_DATE_FORMAT = PDO_ATTR_DRIVER_SPECIFIC,
139142
PDO_FB_ATTR_TIME_FORMAT,
140143
PDO_FB_ATTR_TIMESTAMP_FORMAT,
144+
145+
/* transaction isolation level */
146+
PDO_FB_TRANSACTION_ISOLATION_LEVEL,
147+
PDO_FB_READ_COMMITTED,
148+
PDO_FB_REPEATABLE_READ,
149+
PDO_FB_SERIALIZABLE,
150+
151+
/** transaction access mode */
152+
PDO_FB_WRITABLE_TRANSACTION,
141153
};
142154

143155
#endif /* PHP_PDO_FIREBIRD_INT_H */

0 commit comments

Comments
 (0)