Skip to content

Commit 65ef778

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

File tree

5 files changed

+191
-39
lines changed

5 files changed

+191
-39
lines changed

ext/pdo_firebird/firebird_driver.c

Lines changed: 139 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
}
@@ -1062,6 +1087,70 @@ static void pdo_firebird_fetch_error_func(pdo_dbh_t *dbh, pdo_stmt_t *stmt, zval
10621087
}
10631088
/* }}} */
10641089

1090+
/* change transaction access mode. writable or readonly */
1091+
static bool firebird_change_transaction_access_mode(pdo_dbh_t *dbh, bool writable) /* {{{ */
1092+
{
1093+
pdo_firebird_db_handle *H = (pdo_firebird_db_handle *)dbh->driver_data;
1094+
1095+
if (H->is_writable_txn == writable) {
1096+
return true;
1097+
}
1098+
1099+
if (H->tr && H->in_manually_txn) {
1100+
H->last_app_error = "Cannot change access mode while a transaction is already open";
1101+
return false;
1102+
}
1103+
H->is_writable_txn = writable;
1104+
if (dbh->auto_commit) {
1105+
if (H->tr && !firebird_commit_transaction(dbh, false)) {
1106+
return false;
1107+
}
1108+
if (!_firebird_begin_transaction(dbh)) {
1109+
return false;
1110+
}
1111+
}
1112+
1113+
return true;
1114+
}
1115+
1116+
/* fbWritable or fbReadonly */
1117+
static void firebird_fb_access_mode(INTERNAL_FUNCTION_PARAMETERS, bool writable) /* {{{ */
1118+
{
1119+
pdo_dbh_t *dbh;
1120+
1121+
ZEND_PARSE_PARAMETERS_NONE();
1122+
1123+
dbh = Z_PDO_DBH_P(ZEND_THIS);
1124+
PDO_CONSTRUCT_CHECK;
1125+
1126+
RETURN_BOOL(firebird_change_transaction_access_mode(dbh, writable));
1127+
}
1128+
/* }}} */
1129+
1130+
/* {{{ change transaction mode to writable */
1131+
PHP_METHOD(PDO_Firebird_Ext, fbWritable)
1132+
{
1133+
firebird_fb_access_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU, true);
1134+
}
1135+
/* }}} */
1136+
1137+
/* {{{ change transaction mode to readonly */
1138+
PHP_METHOD(PDO_Firebird_Ext, fbReadonly)
1139+
{
1140+
firebird_fb_access_mode(INTERNAL_FUNCTION_PARAM_PASSTHRU, false);
1141+
}
1142+
/* }}} */
1143+
1144+
static const zend_function_entry *pdo_firebird_get_driver_methods(pdo_dbh_t *dbh, int kind)
1145+
{
1146+
switch (kind) {
1147+
case PDO_DBH_DRIVER_METHOD_KIND_DBH:
1148+
return class_PDO_Firebird_Ext_methods;
1149+
default:
1150+
return NULL;
1151+
}
1152+
}
1153+
10651154
/* {{{ firebird_in_manually_transaction */
10661155
static bool firebird_in_manually_transaction(pdo_dbh_t *dbh)
10671156
{
@@ -1083,7 +1172,7 @@ static const struct pdo_dbh_methods firebird_methods = { /* {{{ */
10831172
pdo_firebird_fetch_error_func,
10841173
firebird_handle_get_attribute,
10851174
NULL, /* check_liveness */
1086-
NULL, /* get driver methods */
1175+
pdo_firebird_get_driver_methods,
10871176
NULL, /* request shutdown */
10881177
firebird_in_manually_transaction,
10891178
NULL /* get gc */
@@ -1116,6 +1205,20 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /*
11161205
dbh->password = pestrdup(vars[5].optval, dbh->is_persistent);
11171206
}
11181207

1208+
H->in_manually_txn = 0;
1209+
if (driver_options) {
1210+
H->is_writable_txn = pdo_attr_lval(driver_options, PDO_FB_WRITABLE_TRANSACTION, 1);
1211+
zend_long txn_isolation_level = pdo_attr_lval(driver_options, PDO_FB_TRANSACTION_ISOLATION_LEVEL, PDO_FB_REPEATABLE_READ);
1212+
if (txn_isolation_level == PDO_FB_READ_COMMITTED ||
1213+
txn_isolation_level == PDO_FB_REPEATABLE_READ ||
1214+
txn_isolation_level == PDO_FB_SERIALIZABLE
1215+
) {
1216+
H->txn_isolation_level = txn_isolation_level;
1217+
} else {
1218+
H->txn_isolation_level = PDO_FB_REPEATABLE_READ;
1219+
}
1220+
}
1221+
11191222
do {
11201223
static char const dpb_flags[] = {
11211224
isc_dpb_user_name, isc_dpb_password, isc_dpb_lc_ctype, isc_dpb_sql_role_name };
@@ -1166,7 +1269,6 @@ static int pdo_firebird_handle_factory(pdo_dbh_t *dbh, zval *driver_options) /*
11661269
"HY000", H->isc_status[1], errmsg);
11671270
}
11681271

1169-
H->in_manually_txn = 0;
11701272
if (dbh->auto_commit && !H->tr) {
11711273
ret = _firebird_begin_transaction(dbh);
11721274
}
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)