Skip to content

Commit b88c34f

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

File tree

5 files changed

+308
-38
lines changed

5 files changed

+308
-38
lines changed

ext/pdo_firebird/firebird_driver.c

Lines changed: 97 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
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"
3334

@@ -700,48 +701,31 @@ static zend_string* firebird_handle_quoter(pdo_dbh_t *dbh, const zend_string *un
700701
static bool _firebird_begin_transaction(pdo_dbh_t *dbh) /* {{{ */
701702
{
702703
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 */
704+
char tpb[5] = { isc_tpb_version3 }, *ptpb = tpb + strlen(tpb);
705+
706+
switch (H->txn_isolation_level) {
707+
case PDO_FB_READ_COMMITTED:
708708
*ptpb++ = isc_tpb_read_committed;
709709
*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 {
710+
break;
711+
712+
case PDO_FB_SERIALIZABLE:
719713
*ptpb++ = isc_tpb_consistency;
720-
dbh->transaction_flags &= ~(PDO_TRANS_ISOLATION_LEVEL^PDO_TRANS_SERIALIZABLE);
721-
}
722-
}
714+
break;
723715

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-
}
716+
case PDO_FB_REPEATABLE_READ:
717+
default:
718+
*ptpb++ = isc_tpb_concurrency;
719+
break;
732720
}
733721

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-
}
722+
if (H->is_writable_txn) {
723+
*ptpb++ = isc_tpb_write;
724+
} else {
725+
*ptpb++ = isc_tpb_read;
742726
}
743-
#endif
744-
if (isc_start_transaction(H->isc_status, &H->tr, 1, &H->db, (unsigned short)(ptpb-tpb), tpb)) {
727+
728+
if (isc_start_transaction(H->isc_status, &H->tr, 1, &H->db, (unsigned short)(ptpb - tpb), tpb)) {
745729
RECORD_ERROR(dbh);
746730
return false;
747731
}
@@ -882,6 +866,7 @@ static bool firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *
882866
{
883867
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
884868
bool bval;
869+
zend_long lval;
885870

886871
switch (attr) {
887872
case PDO_ATTR_AUTOCOMMIT:
@@ -962,6 +947,60 @@ static bool firebird_handle_set_attribute(pdo_dbh_t *dbh, zend_long attr, zval *
962947
zend_string_release_ex(str, 0);
963948
}
964949
return true;
950+
951+
case PDO_FB_TRANSACTION_ISOLATION_LEVEL:
952+
{
953+
if (!pdo_get_long_param(&lval, val)) {
954+
return false;
955+
}
956+
if (H->txn_isolation_level != lval) {
957+
if (lval == PDO_FB_READ_COMMITTED ||
958+
lval == PDO_FB_REPEATABLE_READ ||
959+
lval == PDO_FB_SERIALIZABLE
960+
) {
961+
if (H->tr && H->in_manually_txn) {
962+
H->last_app_error = "Cannot change isolation level while a transaction is already open";
963+
return false;
964+
}
965+
H->txn_isolation_level = lval;
966+
if (dbh->auto_commit) {
967+
if (H->tr && !firebird_commit_transaction(dbh, false)) {
968+
return false;
969+
}
970+
if (!_firebird_begin_transaction(dbh)) {
971+
return false;
972+
}
973+
}
974+
} else {
975+
return false;
976+
}
977+
}
978+
}
979+
return true;
980+
981+
case PDO_FB_WRITABLE_TRANSACTION:
982+
{
983+
if (!pdo_get_bool_param(&bval, val)) {
984+
return false;
985+
}
986+
987+
if (H->is_writable_txn != bval) {
988+
if (H->tr && H->in_manually_txn) {
989+
H->last_app_error = "Cannot change access mode while a transaction is already open";
990+
return false;
991+
}
992+
H->is_writable_txn = bval;
993+
if (dbh->auto_commit) {
994+
if (H->tr && !firebird_commit_transaction(dbh, false)) {
995+
return false;
996+
}
997+
if (!_firebird_begin_transaction(dbh)) {
998+
return false;
999+
}
1000+
}
1001+
}
1002+
}
1003+
return true;
9651004
}
9661005
return false;
9671006
}
@@ -1034,6 +1073,14 @@ static int firebird_handle_get_attribute(pdo_dbh_t *dbh, zend_long attr, zval *v
10341073
case PDO_ATTR_FETCH_TABLE_NAMES:
10351074
ZVAL_BOOL(val, H->fetch_table_names);
10361075
return 1;
1076+
1077+
case PDO_FB_TRANSACTION_ISOLATION_LEVEL:
1078+
ZVAL_LONG(val, H->txn_isolation_level);
1079+
return 1;
1080+
1081+
case PDO_FB_WRITABLE_TRANSACTION:
1082+
ZVAL_BOOL(val, H->is_writable_txn);
1083+
return 1;
10371084
}
10381085
return 0;
10391086
}
@@ -1116,6 +1163,21 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /*
11161163
dbh->password = pestrdup(vars[5].optval, dbh->is_persistent);
11171164
}
11181165

1166+
H->in_manually_txn = 0;
1167+
H->is_writable_txn = 1;
1168+
if (driver_options) {
1169+
H->is_writable_txn = pdo_attr_lval(driver_options, PDO_FB_WRITABLE_TRANSACTION, 1);
1170+
zend_long txn_isolation_level = pdo_attr_lval(driver_options, PDO_FB_TRANSACTION_ISOLATION_LEVEL, PDO_FB_REPEATABLE_READ);
1171+
if (txn_isolation_level == PDO_FB_READ_COMMITTED ||
1172+
txn_isolation_level == PDO_FB_REPEATABLE_READ ||
1173+
txn_isolation_level == PDO_FB_SERIALIZABLE
1174+
) {
1175+
H->txn_isolation_level = txn_isolation_level;
1176+
} else {
1177+
H->txn_isolation_level = PDO_FB_REPEATABLE_READ;
1178+
}
1179+
}
1180+
11191181
do {
11201182
static char const dpb_flags[] = {
11211183
isc_dpb_user_name, isc_dpb_password, isc_dpb_lc_ctype, isc_dpb_sql_role_name };
@@ -1166,7 +1228,6 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /*
11661228
"HY000", H->isc_status[1], errmsg);
11671229
}
11681230

1169-
H->in_manually_txn = 0;
11701231
if (dbh->auto_commit && !H->tr) {
11711232
ret = _firebird_begin_transaction(dbh);
11721233
}

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 */
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
--TEST--
2+
PDO_Firebird: transaction access mode
3+
--EXTENSIONS--
4+
pdo_firebird
5+
--SKIPIF--
6+
<?php require('skipif.inc'); ?>
7+
--ENV--
8+
LSAN_OPTIONS=detect_leaks=0
9+
--FILE--
10+
<?php
11+
12+
require("testdb.inc");
13+
unset($dbh);
14+
15+
$table = 'transaction_access_mode';
16+
17+
$values = [
18+
['val' => true, 'label' => 'writable'],
19+
['val' => false, 'label' => 'readonly'],
20+
];
21+
22+
echo "Set attr in construct\n";
23+
24+
foreach ($values as $value) {
25+
$dbh = new PDO(
26+
PDO_FIREBIRD_TEST_DSN,
27+
PDO_FIREBIRD_TEST_USER,
28+
PDO_FIREBIRD_TEST_PASS,
29+
[
30+
PDO::FB_WRITABLE_TRANSACTION => $value['val'],
31+
],
32+
);
33+
34+
if ($dbh->getAttribute(PDO::FB_WRITABLE_TRANSACTION) === $value['val']) {
35+
echo "OK: {$value['label']}\n";
36+
} else {
37+
echo "NG: {$value['label']}\n";
38+
}
39+
40+
unset($dbh);
41+
}
42+
43+
echo "\n";
44+
echo "Set attr in setAttribute and behavior check\n";
45+
46+
$dbh = new PDO(
47+
PDO_FIREBIRD_TEST_DSN,
48+
PDO_FIREBIRD_TEST_USER,
49+
PDO_FIREBIRD_TEST_PASS,
50+
);
51+
52+
$dbh->query("CREATE TABLE {$table} (val int)");
53+
54+
var_dump($dbh->setAttribute(PDO::FB_WRITABLE_TRANSACTION, true));
55+
56+
if ($dbh->getAttribute(PDO::FB_WRITABLE_TRANSACTION) === true) {
57+
echo "OK: writable\n";
58+
} else {
59+
echo "NG: writable\n";
60+
}
61+
$dbh->query("INSERT INTO {$table} VALUES (12)");
62+
$r = $dbh->query("SELECT * FROM {$table}");
63+
foreach ($r as $row) {
64+
var_dump($row);
65+
}
66+
67+
var_dump($dbh->setAttribute(PDO::FB_WRITABLE_TRANSACTION, false));
68+
69+
if ($dbh->getAttribute(PDO::FB_WRITABLE_TRANSACTION) === false) {
70+
echo "OK: readonly\n";
71+
} else {
72+
echo "NG: readonly\n";
73+
}
74+
try {
75+
$dbh->query("INSERT INTO {$table} VALUES (19)");
76+
} catch (PDOException $e) {
77+
echo "error with readonly\n";
78+
}
79+
$r = $dbh->query("SELECT * FROM {$table}");
80+
foreach ($r as $row) {
81+
var_dump($row);
82+
}
83+
84+
unset($dbh);
85+
?>
86+
--CLEAN--
87+
<?php
88+
require 'testdb.inc';
89+
@$dbh->exec('DROP TABLE transaction_access_mode');
90+
unset($dbh);
91+
?>
92+
--EXPECT--
93+
Set attr in construct
94+
OK: writable
95+
OK: readonly
96+
97+
Set attr in setAttribute and behavior check
98+
bool(true)
99+
OK: writable
100+
array(2) {
101+
["VAL"]=>
102+
int(12)
103+
[0]=>
104+
int(12)
105+
}
106+
bool(true)
107+
OK: readonly
108+
error with readonly
109+
array(2) {
110+
["VAL"]=>
111+
int(12)
112+
[0]=>
113+
int(12)
114+
}

0 commit comments

Comments
 (0)