File: _kicore_transaction_support.c

package info (click to toggle)
python-kinterbasdb 3.3.0-2
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 2,432 kB
  • ctags: 1,830
  • sloc: ansic: 16,803; python: 3,514; makefile: 63; sh: 22
file content (244 lines) | stat: -rw-r--r-- 7,863 bytes parent folder | download | duplicates (2)
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 **************************/