1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244
|
/* KInterbasDB Python Package - Implementation of Low-Level Transaction
* Operations
*
* Version 3.3
*
* The following contributors hold Copyright (C) over their respective
* portions of code (see license.txt for details):
*
* [Original Author (maintained through version 2.0-0.3.1):]
* 1998-2001 [alex] Alexander Kuznetsov <alexan@users.sourceforge.net>
* [Maintainers (after version 2.0-0.3.1):]
* 2001-2002 [maz] Marek Isalski <kinterbasdb@maz.nu>
* 2002-2007 [dsr] David Rushby <woodsplitter@rocketmail.com>
* [Contributors:]
* 2001 [eac] Evgeny A. Cherkashin <eugeneai@icc.ru>
* 2001-2002 [janez] Janez Jere <janez.jere@void.si>
*/
/*************************** DECLARATIONS : begin ****************************/
static isc_tr_handle begin_transaction(
/* Either: */ isc_db_handle db_handle, char *tpb, Py_ssize_t tpb_len,
/* Or: */ ISC_TEB *tebs, short teb_count,
ISC_STATUS *status_vector
);
static PyObject *trans___s__trans_handle;
static PyObject *trans___s__default_tpb_str_;
static PyObject *trans___s_SAVEPOINT_SPACE;
static PyObject *trans___s_ROLLBACK_TO_SPACE;
/**************************** DECLARATIONS : end *****************************/
/*********************** RAW TRANSACTION OPS : begin *************************/
static int init_kidb_transaction_support(void) {
#define INIT_TRANS_STRING_CONST(s) \
trans___s_ ## s = PyString_FromString(#s); \
if (trans___s_ ## s == NULL) { goto fail; }
INIT_TRANS_STRING_CONST(_trans_handle);
INIT_TRANS_STRING_CONST(_default_tpb_str_);
/* Can't use INIT_TRANS_STRING_CONST for these, because they contain
* spaces: */
trans___s_SAVEPOINT_SPACE = PyString_FromString("SAVEPOINT ");
if (trans___s_SAVEPOINT_SPACE == NULL) { goto fail; }
trans___s_ROLLBACK_TO_SPACE = PyString_FromString("ROLLBACK TO ");
if (trans___s_ROLLBACK_TO_SPACE == NULL) { goto fail; }
return 0;
fail:
return -1;
} /* init_kidb_transaction_support */
static isc_tr_handle begin_transaction(
/* Either: */ isc_db_handle db_handle, char *tpb, Py_ssize_t tpb_len,
/* Or: */ ISC_TEB *tebs, short teb_count,
ISC_STATUS *status_vector
)
{
isc_tr_handle trans_handle = NULL_TRANS_HANDLE;
/* (db_handle+tpb+tpb_len) and (tebs+teb_count) are mutually exclusive
* parameters. */
assert (
db_handle != NULL_DB_HANDLE
? tebs == NULL
: tebs != NULL && tpb == NULL
);
/* 2003.02.21: A huge TPB such as 'con.begin(tpb='x'*50000)' crashes the
* FB 1.0.2 server process, but responsibly raises an error with FB 1.5b2.
* Since kinterbasdb only exposes some 20 TPB component values, many of which
* are mutually exclusive, I decided to impose a reasonable limit right
* here. */
if (tpb_len > 255) {
raise_exception(ProgrammingError, "Transaction parameter buffer (TPB) too"
" large. len(tpb) must be <= 255."
);
goto fail;
}
ENTER_GDAL
if (tebs == NULL) {
isc_start_transaction(status_vector,
&trans_handle,
/* Only one database handle is being passed. */
1, &db_handle,
(unsigned short) tpb_len, /* Cast is safe b/c already checked val. */
tpb
);
} else {
isc_start_multiple(status_vector, &trans_handle, teb_count, tebs);
}
LEAVE_GDAL
if (DB_API_ERROR(status_vector)) {
raise_sql_exception(OperationalError, "begin transaction: ", status_vector);
goto fail;
}
assert (trans_handle != NULL_TRANS_HANDLE);
return trans_handle;
fail:
assert (PyErr_Occurred());
return NULL_TRANS_HANDLE;
} /* begin_transaction */
static TransactionalOperationResult prepare_transaction(
isc_tr_handle *trans_handle_p, ISC_STATUS *status_vector
)
{
assert (trans_handle_p != NULL);
if (*trans_handle_p == NULL_TRANS_HANDLE) {
raise_exception(ProgrammingError, "Attempted to prepare closed"
" transaction"
);
return OP_RESULT_ERROR;
}
ENTER_GDAL
isc_prepare_transaction(status_vector, trans_handle_p);
LEAVE_GDAL
if (DB_API_ERROR(status_vector)) {
raise_sql_exception(OperationalError, "prepare: ", status_vector);
return OP_RESULT_ERROR;
}
return OP_RESULT_OK;
} /* prepare_transaction */
static TransactionalOperationResult commit_transaction(
isc_tr_handle *trans_handle_p, boolean retaining,
ISC_STATUS *status_vector
)
{
assert (trans_handle_p != NULL);
if (*trans_handle_p == NULL_TRANS_HANDLE) {
/* As discussed on the Python DB-SIG in message:
* http://mail.python.org/pipermail/db-sig/2003-February/003158.html
* , allow a transaction to be committed even if its existence is only
* implicit. */
return OP_RESULT_OK;
}
{
/* This code can be reached when the CTT is timing out a connection. In
* that case, we want the GIL to remain held during the entire timeout
* operation. */
OPEN_LOCAL_GIL_MANIPULATION_INFRASTRUCTURE
#ifdef ENABLE_CONNECTION_TIMEOUT
const boolean should_manip_gil = NOT_RUNNING_IN_CONNECTION_TIMEOUT_THREAD;
if (should_manip_gil) {
#endif
LEAVE_GIL_WITHOUT_AFFECTING_DB_AND_WITHOUT_STARTING_CODE_BLOCK
#ifdef ENABLE_CONNECTION_TIMEOUT
}
#endif
ENTER_GDAL_WITHOUT_LEAVING_PYTHON
if (!retaining) {
isc_commit_transaction(status_vector, trans_handle_p);
} else {
isc_commit_retaining(status_vector, trans_handle_p);
assert (*trans_handle_p != NULL_TRANS_HANDLE);
}
LEAVE_GDAL_WITHOUT_ENTERING_PYTHON
#ifdef ENABLE_CONNECTION_TIMEOUT
if (should_manip_gil) {
#endif
ENTER_GIL_WITHOUT_AFFECTING_DB_AND_WITHOUT_ENDING_CODE_BLOCK
#ifdef ENABLE_CONNECTION_TIMEOUT
}
#endif
CLOSE_LOCAL_GIL_MANIPULATION_INFRASTRUCTURE
} /* end of lock manipulation scope */
if (DB_API_ERROR(status_vector)) {
raise_sql_exception(OperationalError, "commit: ", status_vector);
return OP_RESULT_ERROR;
}
return OP_RESULT_OK;
} /* commit_transaction */
static TransactionalOperationResult rollback_transaction(
isc_tr_handle *trans_handle_p, boolean retaining,
boolean allowed_to_raise, ISC_STATUS *status_vector
)
{
assert (trans_handle_p != NULL);
/* If there is not an active transaction, rolling back is meaningless, but
* acceptable. */
if (*trans_handle_p == NULL_TRANS_HANDLE) {
return OP_RESULT_OK;
}
{
/* This code can be reached when the CTT is timing out a connection. In
* that case, we want the GIL to remain held during the entire timeout
* operation. */
OPEN_LOCAL_GIL_MANIPULATION_INFRASTRUCTURE
#ifdef ENABLE_CONNECTION_TIMEOUT
const boolean should_manip_gil = NOT_RUNNING_IN_CONNECTION_TIMEOUT_THREAD;
if (should_manip_gil) {
#endif
LEAVE_GIL_WITHOUT_AFFECTING_DB_AND_WITHOUT_STARTING_CODE_BLOCK
#ifdef ENABLE_CONNECTION_TIMEOUT
}
#endif
ENTER_GDAL_WITHOUT_LEAVING_PYTHON
if (!retaining) {
isc_rollback_transaction(status_vector, trans_handle_p);
} else {
isc_rollback_retaining(status_vector, trans_handle_p);
assert (*trans_handle_p != NULL_TRANS_HANDLE);
}
LEAVE_GDAL_WITHOUT_ENTERING_PYTHON
#ifdef ENABLE_CONNECTION_TIMEOUT
if (should_manip_gil) {
#endif
ENTER_GIL_WITHOUT_AFFECTING_DB_AND_WITHOUT_ENDING_CODE_BLOCK
#ifdef ENABLE_CONNECTION_TIMEOUT
}
#endif
CLOSE_LOCAL_GIL_MANIPULATION_INFRASTRUCTURE
} /* end of lock manipulation scope */
if (DB_API_ERROR(status_vector)) {
raise_sql_exception(OperationalError, "rollback: ", status_vector);
if (allowed_to_raise) {
return OP_RESULT_ERROR;
} else {
SUPPRESS_EXCEPTION;
}
}
return OP_RESULT_OK;
} /* rollback_transaction */
/************************ RAW TRANSACTION OPS : end **************************/
|